From 572c38b700928d0b27c8bc9cfef105d736ba8c34 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Sun, 10 Dec 2023 05:18:46 +0000 Subject: [PATCH] src/cpp-common: add bt2c::parseJson() functions (value mode) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 Change-Id: I3e9662f6632d10f11c08c178342785886f664797 Reviewed-on: https://review.lttng.org/c/babeltrace/+/7480 Reviewed-by: Simon Marchi Reviewed-on: https://review.lttng.org/c/babeltrace/+/12684 --- src/Makefile.am | 2 + src/cpp-common/bt2c/parse-json-as-val.cpp | 165 ++++++++++++++++++++++ src/cpp-common/bt2c/parse-json-as-val.hpp | 38 +++++ 3 files changed, 205 insertions(+) create mode 100644 src/cpp-common/bt2c/parse-json-as-val.cpp create mode 100644 src/cpp-common/bt2c/parse-json-as-val.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 9f6bcf29..a842780a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..0b6fa2f0 --- /dev/null +++ b/src/cpp-common/bt2c/parse-json-as-val.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2024 Philippe Proulx + * + * 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 + 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 + void _handleVal(const TextLoc& loc, ArgTs&&...args) + { + /* Create a JSON value from custom arguments and `loc` */ + auto jsonVal = + createJsonVal(std::forward(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 index 00000000..7f20f3e9 --- /dev/null +++ b/src/cpp-common/bt2c/parse-json-as-val.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * 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 + +#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 */ -- 2.34.1