src/cpp-common: add bt2c::parseJson() functions (value mode)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Sun, 10 Dec 2023 05:18:46 +0000 (05:18 +0000)
committerSimon Marchi <simon.marchi@efficios.com>
Wed, 4 Sep 2024 19:05:14 +0000 (15:05 -0400)
This patch adds two new versions of bt2c::parseJson() which accept the
same parameters as the current "listener mode" versions, except for a
missing listener, and return some resulting
JSON value (`bt2c::JsonVal`).

This is part of an effort to support CTF2‑SPEC‑2.0 [1]. It will be
easier to convert CTF 2 metadata stream fragments using JSON value
objects than using the listener mode version of bt2c::parseJson()
directly.

`baseOffset` is also added to the offset of a text location when
creating the text location of any JSON value object to create. This will
be needed when parsing a CTF 2 fragment: the line and column numbers are
local to the fragment, but the offset is from the beginning of the whole
metadata stream. Kind of confusing, but I wouldn't know what an RS byte
means in terms of lines/columns anyway (different text editor could
interpret it in different ways).

[1]: https://diamon.org/ctf/CTF2-SPEC-2.0.html

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I3e9662f6632d10f11c08c178342785886f664797
Reviewed-on: https://review.lttng.org/c/babeltrace/+/7480
Reviewed-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/12684

src/Makefile.am
src/cpp-common/bt2c/parse-json-as-val.cpp [new file with mode: 0644]
src/cpp-common/bt2c/parse-json-as-val.hpp [new file with mode: 0644]

index 9f6bcf293dbdfb70e8fae97370571a1b7a391efe..a842780aac5803a31c35055877ba81833c377430 100644 (file)
@@ -174,6 +174,8 @@ cpp_common_libcpp_common_la_SOURCES = \
        cpp-common/bt2c/logging.hpp \
        cpp-common/bt2c/make-span.hpp \
        cpp-common/bt2c/parse-json.hpp \
+       cpp-common/bt2c/parse-json-as-val.cpp \
+       cpp-common/bt2c/parse-json-as-val.hpp \
        cpp-common/bt2c/prio-heap.hpp \
        cpp-common/bt2c/read-fixed-len-int.hpp \
        cpp-common/bt2c/regex.hpp \
diff --git a/src/cpp-common/bt2c/parse-json-as-val.cpp b/src/cpp-common/bt2c/parse-json-as-val.cpp
new file mode 100644 (file)
index 0000000..0b6fa2f
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016-2024 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "common/assert.h"
+#include "common/common.h"
+
+#include "parse-json-as-val.hpp"
+#include "parse-json.hpp"
+
+namespace bt2c {
+namespace {
+
+/*
+ * Listener for the listener version of parseJson() which iteratively
+ * builds a "root" JSON value.
+ */
+class JsonValBuilder final
+{
+public:
+    explicit JsonValBuilder(const std::size_t baseOffset) : _mBaseOffset {baseOffset}
+    {
+    }
+
+    void onNull(const TextLoc& loc)
+    {
+        this->_handleVal(loc);
+    }
+
+    template <typename ValT>
+    void onScalarVal(const ValT& val, const TextLoc& loc)
+    {
+        this->_handleVal(loc, val);
+    }
+
+    void onScalarVal(const bt2s::string_view val, const TextLoc& loc)
+    {
+        this->_handleVal(loc, val.to_string());
+    }
+
+    void onArrayBegin(const TextLoc&)
+    {
+        _mStack.emplace_back(_State::InArray);
+    }
+
+    void onArrayEnd(const TextLoc& loc)
+    {
+        auto arrayValCont = std::move(this->_stackTop().arrayValCont);
+
+        _mStack.pop_back();
+        this->_handleVal(loc, std::move(arrayValCont));
+    }
+
+    void onObjBegin(const TextLoc&)
+    {
+        _mStack.emplace_back(_State::InObj);
+    }
+
+    void onObjKey(const bt2s::string_view key, const TextLoc&)
+    {
+        this->_stackTop().lastObjKey = key.to_string();
+    }
+
+    void onObjEnd(const TextLoc& loc)
+    {
+        auto objValCont = std::move(this->_stackTop().objValCont);
+
+        _mStack.pop_back();
+        this->_handleVal(loc, std::move(objValCont));
+    }
+
+    JsonVal::UP releaseVal() noexcept
+    {
+        return std::move(_mJsonVal);
+    }
+
+private:
+    /* The state of a stack frame */
+    enum class _State
+    {
+        InArray,
+        InObj,
+    };
+
+    /*
+     * An entry of `_mStack`.
+     */
+    struct _StackFrame final
+    {
+        explicit _StackFrame(const _State stateParam) : state {stateParam}
+        {
+        }
+
+        _State state;
+        JsonArrayVal::Container arrayValCont;
+        JsonObjVal::Container objValCont;
+        std::string lastObjKey;
+    };
+
+private:
+    /*
+     * Top frame of the stack.
+     */
+    _StackFrame& _stackTop() noexcept
+    {
+        BT_ASSERT_DBG(!_mStack.empty());
+        return _mStack.back();
+    }
+
+    template <typename... ArgTs>
+    void _handleVal(const TextLoc& loc, ArgTs&&...args)
+    {
+        /* Create a JSON value from custom arguments and `loc` */
+        auto jsonVal =
+            createJsonVal(std::forward<ArgTs>(args)...,
+                          TextLoc {loc.offset() + _mBaseOffset, loc.lineNo(), loc.colNo()});
+
+        if (_mStack.empty()) {
+            /* Assign as root */
+            _mJsonVal = std::move(jsonVal);
+            return;
+        }
+
+        switch (_mStack.back().state) {
+        case _State::InArray:
+            /* Append to current JSON array value container */
+            this->_stackTop().arrayValCont.push_back(std::move(jsonVal));
+            break;
+
+        case _State::InObj:
+            /*
+             * Insert into current JSON object value container
+             *
+             * It's safe to move `this->_stackTop().lastObjKey` as it's
+             * only used once.
+             */
+            this->_stackTop().objValCont.insert(
+                std::make_pair(std::move(this->_stackTop().lastObjKey), std::move(jsonVal)));
+            break;
+
+        default:
+            bt_common_abort();
+        }
+    }
+
+private:
+    std::size_t _mBaseOffset;
+    std::vector<_StackFrame> _mStack;
+    JsonVal::UP _mJsonVal;
+};
+
+} /* namespace */
+
+JsonVal::UP parseJson(const bt2s::string_view str, const std::size_t baseOffset,
+                      const Logger& logger)
+{
+    JsonValBuilder builder {baseOffset};
+
+    parseJson(str, builder, baseOffset, logger);
+    return builder.releaseVal();
+}
+
+} /* namespace bt2c */
diff --git a/src/cpp-common/bt2c/parse-json-as-val.hpp b/src/cpp-common/bt2c/parse-json-as-val.hpp
new file mode 100644 (file)
index 0000000..7f20f3e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2022-2024 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_BT2C_PARSE_JSON_AS_VAL_HPP
+#define BABELTRACE_CPP_COMMON_BT2C_PARSE_JSON_AS_VAL_HPP
+
+#include <cstdlib>
+
+#include "logging.hpp"
+
+#include "cpp-common/bt2s/string-view.hpp"
+
+#include "json-val.hpp"
+
+namespace bt2c {
+
+/*
+ * Parses the JSON text `str` and returns the resulting JSON value,
+ * adding `baseOffset` to the text location offset of all the created
+ * JSON values.
+ *
+ * When this function logs or appends a cause to the error of the
+ * current thread, it uses `baseOffset` to format the text location part
+ * of the message.
+ */
+JsonVal::UP parseJson(bt2s::string_view str, std::size_t baseOffset, const Logger& logger);
+
+inline JsonVal::UP parseJson(const bt2s::string_view str, const Logger& logger)
+{
+    return parseJson(str, 0, logger);
+}
+
+} /* namespace bt2c */
+
+#endif /* BABELTRACE_CPP_COMMON_BT2C_PARSE_JSON_AS_VAL_HPP */
This page took 0.027179 seconds and 4 git commands to generate.