src/cpp-common: add bt2_common::parseJson() functions (value mode)
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 3 Mar 2022 15:06:29 +0000 (10:06 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Tue, 23 Aug 2022 16:06:14 +0000 (12:06 -0400)
This patch adds three new versions of bt2_common::parseJson() which
accept the same parameters as the current "listener mode" versions,
except for a missing listener, and return some resulting JSON value
(`bt2_common::JsonVal`).

This is part of an effort to support CTF2‑SPECRC‑5.0 [1]. It will be
easier to convert CTF 2 metadata stream fragments using JSON value
objects than using the listener mode version of bt2_common::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/files/CTF2-SPECRC-5.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>
src/cpp-common/Makefile.am
src/cpp-common/parse-json-as-val.cpp [new file with mode: 0644]
src/cpp-common/parse-json-as-val.hpp [new file with mode: 0644]

index d3c3398499dea4a608a365e4efc4655c6da4a443..eb9b0fbb282ffb960a0a616db723cddc938bf31d 100644 (file)
@@ -19,6 +19,7 @@ libcppcommon_la_SOURCES = \
        str-scanner.hpp str-scanner.cpp \
        parse-json.hpp \
        make-unique.hpp \
-       json-val.hpp json-val.cpp
+       json-val.hpp json-val.cpp \
+       parse-json-as-val.hpp parse-json-as-val.cpp
 
 EXTRA_DIST = bt2
diff --git a/src/cpp-common/parse-json-as-val.cpp b/src/cpp-common/parse-json-as-val.cpp
new file mode 100644 (file)
index 0000000..272733f
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2016-2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define BT_CLOG_CFG _mLogCfg
+#define BT_LOG_TAG  "PARSE-JSON-AS-VAL"
+#include "cpp-common/cfg-logging-error-reporting-throw.hpp"
+
+#include "common/common.h"
+#include "common/assert.h"
+#include "parse-json-as-val.hpp"
+
+namespace bt2_common {
+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 onArrayBegin(const TextLoc&)
+    {
+        _mStack.push_back(_StackFrame {_State::IN_ARRAY});
+    }
+
+    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.push_back(_StackFrame {_State::IN_OBJ});
+    }
+
+    void onObjKey(const std::string& key, const TextLoc&)
+    {
+        this->_stackTop().lastObjKey = key;
+    }
+
+    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
+    {
+        IN_ARRAY,
+        IN_OBJ,
+    };
+
+    /* A entry of `_mStack` */
+    struct _StackFrame
+    {
+        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::IN_ARRAY:
+            /* Append to current JSON array value container */
+            this->_stackTop().arrayValCont.push_back(std::move(jsonVal));
+            break;
+
+        case _State::IN_OBJ:
+            /*
+             * 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 char * const begin, const char * const end,
+                      const std::size_t baseOffset, const LogCfg& logCfg,
+                      const TextLocStrFmt textLocStrFmt)
+{
+    JsonValBuilder builder {baseOffset};
+
+    parseJson(begin, end, builder, baseOffset, logCfg, textLocStrFmt);
+    return builder.releaseVal();
+}
+
+} /* namespace bt2_common */
diff --git a/src/cpp-common/parse-json-as-val.hpp b/src/cpp-common/parse-json-as-val.hpp
new file mode 100644 (file)
index 0000000..0e3f847
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2022 Philippe Proulx <pproulx@efficios.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef BABELTRACE_CPP_COMMON_PARSE_JSON_AS_VAL_HPP
+#define BABELTRACE_CPP_COMMON_PARSE_JSON_AS_VAL_HPP
+
+#include <cstdlib>
+#include <string>
+
+#include "parse-json.hpp"
+#include "json-val.hpp"
+#include "text-loc.hpp"
+#include "text-loc-str.hpp"
+#include "log-cfg.hpp"
+
+namespace bt2_common {
+
+/*
+ * Parses the JSON text between `begin` and `end` (excluded) 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` and `textLocStrFmt` to format
+ * the text location part of the message.
+ */
+JsonVal::UP parseJson(const char *begin, const char *end, std::size_t baseOffset,
+                      const LogCfg& logCfg,
+                      TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET);
+
+static inline JsonVal::UP
+parseJson(const char *begin, const char *end, const LogCfg& logCfg,
+          const TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET)
+{
+    return parseJson(begin, end, 0, logCfg, textLocStrFmt);
+}
+
+/*
+ * Parses the null-terminated 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` and `textLocStrFmt` to format
+ * the text location part of the message.
+ */
+static inline JsonVal::UP
+parseJson(const char * const str, const std::size_t baseOffset, const LogCfg& logCfg,
+          const TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET)
+{
+    return parseJson(str, str + std::strlen(str), baseOffset, logCfg, textLocStrFmt);
+}
+
+static inline JsonVal::UP
+parseJson(const char * const str, const LogCfg& logCfg,
+          const TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET)
+{
+    return parseJson(str, static_cast<std::size_t>(0), logCfg, textLocStrFmt);
+}
+
+/*
+ * Parses the JSON string `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` and `textLocStrFmt` to format
+ * the text location part of the message.
+ */
+static inline JsonVal::UP
+parseJson(const std::string& str, const std::size_t baseOffset, const LogCfg& logCfg,
+          const TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET)
+{
+    return parseJson(str.data(), str.data() + str.size(), baseOffset, logCfg, textLocStrFmt);
+}
+
+static inline JsonVal::UP
+parseJson(const std::string& str, const LogCfg& logCfg,
+          const TextLocStrFmt textLocStrFmt = TextLocStrFmt::LINE_COL_NOS_AND_OFFSET)
+{
+    return parseJson(str, 0, logCfg, textLocStrFmt);
+}
+
+} /* namespace bt2_common */
+
+#endif /* BABELTRACE_CPP_COMMON_PARSE_JSON_AS_VAL_HPP */
This page took 0.027416 seconds and 5 git commands to generate.