From 898ef70ce733ca50be65ec5bdda2f3eed853b535 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Mon, 11 Dec 2023 16:51:28 -0500 Subject: [PATCH] Implement CTF 2 JSON requirements This patch implements all the CTF 2 JSON requirements. The internal "public" API is the `Ctf2JsonAnyFragmentValReq` PImpl class which validates a single CTF 2 fragment. That's all the CTF 2 metadata stream parser will need. Here's an example: Code: Ctf2JsonAnyFragValReq {}.validate(*bt2c::parseJson(jsonText)); JSON text: { "type": "data-stream-class", "namespace": "lol", "name": "salut", "packet-context-field-class": { "type": "structure", "member-classes": [ { "name": "meow", "field-class": { "type": "null-terminated-string" } }, { "name": "lel", "field-class": "uint16" }, { "name": "meow", "field-class": { "type": "dynamic-length-array", "length-field-location": {"path": ["lel"]}, "element-field-class": { "type": "fixed-length-signed-integer", "length": 23, "byte-order": "little-endian", "alignment": 24, "attributes": {} } } } ] } } Exception message: [1:1] Invalid data stream class fragment: [5:33] In object property `packet-context-field-class`: [5:33] Invalid scope field class: [5:33] Invalid structure field class: [7:23] In object property `member-classes`: [18:7] In array element #3: [18:7] Invalid structure field member class: [20:24] In object property `field-class`: [20:24] Invalid dynamic-length array field class: [23:34] In object property `element-field-class`: [23:34] Invalid fixed-length signed integer field class: [27:26] In object property `alignment`: [27:26] Invalid alignment: 24 is not a power of two. I'd say that Ctf2JsonAnyFragValReq::validate() can validate 95 % of a CTF 2 metadata stream. See its class comment to learn what it can't do. There are a few clang-format off/on comments because I hate what it does with nested brace-enclosed lists. Signed-off-by: Philippe Proulx Change-Id: Ic622a099a1e0aa5daa896cdfa910d24b817a9a5f Reviewed-on: https://review.lttng.org/c/babeltrace/+/12717 --- src/Makefile.am | 5 + src/cpp-common/bt2c/json-val-req.cpp | 15 + .../ctf/common/src/metadata/json/strings.cpp | 109 + .../ctf/common/src/metadata/json/strings.hpp | 113 + .../ctf/common/src/metadata/json/val-req.cpp | 2556 +++++++++++++++++ .../ctf/common/src/metadata/json/val-req.hpp | 211 ++ 6 files changed, 3009 insertions(+) create mode 100644 src/cpp-common/bt2c/json-val-req.cpp create mode 100644 src/plugins/ctf/common/src/metadata/json/strings.cpp create mode 100644 src/plugins/ctf/common/src/metadata/json/strings.hpp create mode 100644 src/plugins/ctf/common/src/metadata/json/val-req.cpp create mode 100644 src/plugins/ctf/common/src/metadata/json/val-req.hpp diff --git a/src/Makefile.am b/src/Makefile.am index dcbc8700..408a3762 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -174,6 +174,7 @@ cpp_common_libcpp_common_la_SOURCES = \ cpp-common/bt2c/glib-up.hpp \ cpp-common/bt2c/json-val.cpp \ cpp-common/bt2c/json-val.hpp \ + cpp-common/bt2c/json-val-req.cpp \ cpp-common/bt2c/json-val-req.hpp \ cpp-common/bt2c/libc-up.hpp \ cpp-common/bt2c/logging.hpp \ @@ -701,6 +702,10 @@ plugins_ctf_babeltrace_plugin_ctf_la_SOURCES = \ plugins/ctf/common/src/item-seq/medium.hpp \ plugins/ctf/common/src/metadata/ctf-ir.cpp \ plugins/ctf/common/src/metadata/ctf-ir.hpp \ + plugins/ctf/common/src/metadata/json/strings.cpp \ + plugins/ctf/common/src/metadata/json/strings.hpp \ + plugins/ctf/common/src/metadata/json/val-req.cpp \ + plugins/ctf/common/src/metadata/json/val-req.hpp \ plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.cpp \ plugins/ctf/common/src/metadata/tsdl/metadata-stream-decoder.hpp \ plugins/ctf/common/src/msg-iter.cpp \ diff --git a/src/cpp-common/bt2c/json-val-req.cpp b/src/cpp-common/bt2c/json-val-req.cpp new file mode 100644 index 00000000..a208cb64 --- /dev/null +++ b/src/cpp-common/bt2c/json-val-req.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#include "json-val-req.hpp" + +namespace bt2c { +namespace internal { + +constexpr const char *JsonValOps::objValPropName; + +} /* namespace internal */ +} /* namespace bt2c */ diff --git a/src/plugins/ctf/common/src/metadata/json/strings.cpp b/src/plugins/ctf/common/src/metadata/json/strings.cpp new file mode 100644 index 00000000..01fa045e --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/json/strings.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#include "strings.hpp" + +namespace ctf { +namespace jsonstr { + +const char * const accuracy = "accuracy"; +const char * const align = "alignment"; +const char * const attrs = "attributes"; +const char * const bigEndian = "big-endian"; +const char * const bitOrder = "bit-order"; +const char * const byteOrder = "byte-order"; +const char * const clkCls = "clock-class"; +const char * const cycles = "cycles"; +const char * const dataStreamCls = "data-stream-class"; +const char * const dataStreamClsId = "data-stream-class-id"; +const char * const dataStreamId = "data-stream-id"; +const char * const defClkClsId = "default-clock-class-id"; +const char * const defClkTs = "default-clock-timestamp"; +const char * const descr = "description"; +const char * const discEventRecordCounterSnap = "discarded-event-record-counter-snapshot"; +const char * const dynLenArray = "dynamic-length-array"; +const char * const dynLenBlob = "dynamic-length-blob"; +const char * const dynLenStr = "dynamic-length-string"; +const char * const elemFc = "element-field-class"; +const char * const encoding = "encoding"; +const char * const env = "environment"; +const char * const eventRecordCls = "event-record-class"; +const char * const eventRecordClsId = "event-record-class-id"; +const char * const eventRecordCommonCtx = "event-record-common-context"; +const char * const eventRecordCommonCtxFc = "event-record-common-context-field-class"; +const char * const eventRecordHeader = "event-record-header"; +const char * const eventRecordHeaderFc = "event-record-header-field-class"; +const char * const eventRecordPayload = "event-record-payload"; +const char * const eventRecordSpecCtx = "event-record-specific-context"; +const char * const extensions = "extensions"; +const char * const fc = "field-class"; +const char * const fcAlias = "field-class-alias"; +const char * const fixedLenBitArray = "fixed-length-bit-array"; +const char * const fixedLenBitMap = "fixed-length-bit-map"; +const char * const fixedLenBool = "fixed-length-boolean"; +const char * const fixedLenFloat = "fixed-length-floating-point-number"; +const char * const fixedLenSInt = "fixed-length-signed-integer"; +const char * const fixedLenUInt = "fixed-length-unsigned-integer"; +const char * const flags = "flags"; +const char * const freq = "frequency"; +const char * const ftl = "first-to-last"; +const char * const id = "id"; +const char * const len = "length"; +const char * const lenFieldLoc = "length-field-location"; +const char * const littleEndian = "little-endian"; +const char * const ltf = "last-to-first"; +const char * const mappings = "mappings"; +const char * const mediaType = "media-type"; +const char * const memberClasses = "member-classes"; +const char * const metadataStreamUuid = "metadata-stream-uuid"; +const char * const minAlign = "minimum-alignment"; +const char * const name = "name"; +const char * const ns = "namespace"; +const char * const nullTerminatedStr = "null-terminated-string"; +const char * const offsetFromOrigin = "offset-from-origin"; +const char * const optional = "optional"; +const char * const opts = "options"; +const char * const origin = "origin"; +const char * const path = "path"; +const char * const payloadFc = "payload-field-class"; +const char * const pktContentLen = "packet-content-length"; +const char * const pktCtx = "packet-context"; +const char * const pktCtxFc = "packet-context-field-class"; +const char * const pktEndDefClkTs = "packet-end-default-clock-timestamp"; +const char * const pktHeader = "packet-header"; +const char * const pktHeaderFc = "packet-header-field-class"; +const char * const pktMagicNumber = "packet-magic-number"; +const char * const pktSeqNum = "packet-sequence-number"; +const char * const pktTotalLen = "packet-total-length"; +const char * const preamble = "preamble"; +const char * const precision = "precision"; +const char * const prefDispBase = "preferred-display-base"; +const char * const roles = "roles"; +const char * const seconds = "seconds"; +const char * const selFieldLoc = "selector-field-location"; +const char * const selFieldRanges = "selector-field-ranges"; +const char * const specCtxFc = "specific-context-field-class"; +const char * const staticLenArray = "static-length-array"; +const char * const staticLenBlob = "static-length-blob"; +const char * const staticLenStr = "static-length-string"; +const char * const structure = "structure"; +const char * const traceCls = "trace-class"; +const char * const type = "type"; +const char * const uid = "uid"; +const char * const unixEpoch = "unix-epoch"; +const char * const utf16Be = "utf-16be"; +const char * const utf16Le = "utf-16le"; +const char * const utf32Be = "utf-32be"; +const char * const utf32Le = "utf-32le"; +const char * const utf8 = "utf-8"; +const char * const uuid = "uuid"; +const char * const variant = "variant"; +const char * const varLenSInt = "variable-length-signed-integer"; +const char * const varLenUInt = "variable-length-unsigned-integer"; +const char * const version = "version"; + +} /* namespace jsonstr */ +} /* namespace ctf */ diff --git a/src/plugins/ctf/common/src/metadata/json/strings.hpp b/src/plugins/ctf/common/src/metadata/json/strings.hpp new file mode 100644 index 00000000..5ac1f361 --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/json/strings.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_STRINGS_HPP +#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_STRINGS_HPP + +namespace ctf { +namespace jsonstr { + +extern const char * const accuracy; +extern const char * const align; +extern const char * const attrs; +extern const char * const bigEndian; +extern const char * const bitOrder; +extern const char * const byteOrder; +extern const char * const clkCls; +extern const char * const cycles; +extern const char * const dataStreamCls; +extern const char * const dataStreamClsId; +extern const char * const dataStreamId; +extern const char * const defClkClsId; +extern const char * const defClkTs; +extern const char * const descr; +extern const char * const discEventRecordCounterSnap; +extern const char * const dynLenArray; +extern const char * const dynLenBlob; +extern const char * const dynLenStr; +extern const char * const elemFc; +extern const char * const encoding; +extern const char * const env; +extern const char * const eventRecordCls; +extern const char * const eventRecordClsId; +extern const char * const eventRecordCommonCtx; +extern const char * const eventRecordCommonCtxFc; +extern const char * const eventRecordHeader; +extern const char * const eventRecordHeaderFc; +extern const char * const eventRecordPayload; +extern const char * const eventRecordSpecCtx; +extern const char * const extensions; +extern const char * const fc; +extern const char * const fcAlias; +extern const char * const fixedLenBitArray; +extern const char * const fixedLenBitMap; +extern const char * const fixedLenBool; +extern const char * const fixedLenFloat; +extern const char * const fixedLenSInt; +extern const char * const fixedLenUInt; +extern const char * const flags; +extern const char * const freq; +extern const char * const ftl; +extern const char * const id; +extern const char * const len; +extern const char * const lenFieldLoc; +extern const char * const littleEndian; +extern const char * const ltf; +extern const char * const mappings; +extern const char * const mediaType; +extern const char * const memberClasses; +extern const char * const metadataStreamUuid; +extern const char * const minAlign; +extern const char * const name; +extern const char * const ns; +extern const char * const nullTerminatedStr; +extern const char * const offsetFromOrigin; +extern const char * const optional; +extern const char * const opts; +extern const char * const origin; +extern const char * const path; +extern const char * const payloadFc; +extern const char * const pktContentLen; +extern const char * const pktCtx; +extern const char * const pktCtxFc; +extern const char * const pktEndDefClkTs; +extern const char * const pktHeader; +extern const char * const pktHeaderFc; +extern const char * const pktMagicNumber; +extern const char * const pktSeqNum; +extern const char * const pktTotalLen; +extern const char * const preamble; +extern const char * const precision; +extern const char * const prefDispBase; +extern const char * const roles; +extern const char * const seconds; +extern const char * const selFieldLoc; +extern const char * const selFieldRanges; +extern const char * const specCtxFc; +extern const char * const staticLenArray; +extern const char * const staticLenBlob; +extern const char * const staticLenStr; +extern const char * const structure; +extern const char * const traceCls; +extern const char * const type; +extern const char * const uid; +extern const char * const unixEpoch; +extern const char * const utf16Be; +extern const char * const utf16Le; +extern const char * const utf32Be; +extern const char * const utf32Le; +extern const char * const utf8; +extern const char * const uuid; +extern const char * const variant; +extern const char * const varLenSInt; +extern const char * const varLenUInt; +extern const char * const version; +extern const char * const version; + +} /* namespace jsonstr */ +} /* namespace ctf */ + +#endif /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_STRINGS_HPP */ diff --git a/src/plugins/ctf/common/src/metadata/json/val-req.cpp b/src/plugins/ctf/common/src/metadata/json/val-req.cpp new file mode 100644 index 00000000..4bd24512 --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/json/val-req.cpp @@ -0,0 +1,2556 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include "common/assert.h" +#include "cpp-common/bt2c/contains.hpp" +#include "cpp-common/bt2c/exc.hpp" +#include "cpp-common/bt2c/json-val-req.hpp" +#include "cpp-common/bt2c/logging.hpp" + +#include "strings.hpp" +#include "val-req.hpp" + +namespace ctf { +namespace src { +namespace { + +/* + * CTF 2 JSON alignment value requirement. + */ +class AlignValReq final : public bt2c::JsonValHasTypeReq +{ +public: + explicit AlignValReq(const bt2c::Logger& parentLogger) noexcept : + bt2c::JsonValHasTypeReq {bt2c::ValType::UInt, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +protected: + static bool _isPowOfTwo(const unsigned long long val) noexcept + { + return ((val & (val - 1)) == 0) && val > 0; + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + const auto val = *jsonVal.asUInt(); + + if (!this->_isPowOfTwo(val)) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonVal.loc(), "{} is not a power of two.", val); + } + } +}; + +/* + * CTF 2 JSON byte order value requirement. + */ +class ByteOrderValReq final : public bt2c::JsonStrValInSetReq +{ +public: + explicit ByteOrderValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonStrValInSetReq { + bt2c::JsonStrValInSetReq::Set {jsonstr::bigEndian, jsonstr::littleEndian}, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonStrValInSetReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid byte order."); + } + } +}; + +/* + * CTF 2 JSON bit order value requirement. + */ +class BitOrderValReq final : public bt2c::JsonStrValInSetReq +{ +public: + explicit BitOrderValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonStrValInSetReq {bt2c::JsonStrValInSetReq::Set {jsonstr::ftl, jsonstr::ltf}, + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonStrValInSetReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid bit order."); + } + } +}; + +/* + * CTF 2 JSON UUID value requirement. + */ +class UuidValReq final : public bt2c::JsonArrayValReq +{ +public: + explicit UuidValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonArrayValReq {16, bt2c::JsonUIntValInRangeReq::shared(0, 255, parentLogger), + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonArrayValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid UUID."); + } + } +}; + +/* + * CTF 2 JSON field location path element value requirement. + */ +class FieldLocPathElemValReq final : public bt2c::JsonValReq +{ +public: + explicit FieldLocPathElemValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonValReq {parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + if (jsonVal.isNull() || jsonVal.isStr()) { + /* Valid */ + return; + } + + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC(this->_logger(), bt2c::Error, jsonVal.loc(), + "Expecting a string or `null`."); + } +}; + +/* + * CTF 2 JSON field location value requirement. + */ +class FieldLocValReq final : public bt2c::JsonObjValReq +{ +public: + explicit FieldLocValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + {jsonstr::origin, { + bt2c::JsonStrValInSetReq::shared({ + jsonstr::pktHeader, + jsonstr::pktCtx, + jsonstr::eventRecordHeader, + jsonstr::eventRecordCommonCtx, + jsonstr::eventRecordSpecCtx, + jsonstr::eventRecordPayload, + }, parentLogger) + }}, + {jsonstr::path, { + bt2c::JsonArrayValReq::shared(1, bt2s::nullopt, + FieldLocPathElemValReq::shared(parentLogger), parentLogger), + true + }}, + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + + /* Validate that the last path element is not `null` */ + { + const auto& jsonLastPathElem = + **(jsonVal.asObj()[jsonstr::path]->asArray().end() - 1); + + if (jsonLastPathElem.isNull()) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC(this->_logger(), bt2c::Error, + jsonLastPathElem.loc(), + "Path ends with `null`."); + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid field location."); + } + } +}; + +/* + * CTF 2 JSON user attributes value requirement. + */ +class UserAttrsValReq final : public bt2c::JsonObjValReq +{ +public: + explicit UserAttrsValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq {{}, true, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid user attributes."); + } + } +}; + +/* + * CTF 2 JSON trace environment value requirement. + */ +class TraceEnvValReq final : public bt2c::JsonObjValReq +{ +public: + explicit TraceEnvValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq {{}, true, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + + /* Validate types of entries */ + for (auto& keyJsonValPair : jsonVal.asObj()) { + auto& jsonEntry = keyJsonValPair.second; + + if (!jsonEntry->isUInt() && !jsonEntry->isSInt() && !jsonEntry->isStr()) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonEntry->loc(), + "Entry `{}`: expecting an integer or a string.", keyJsonValPair.first); + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid trace environment."); + } + } +}; + +/* + * CTF 2 JSON extensions value requirement. + */ +class ExtValReq final : public bt2c::JsonObjValReq +{ +public: + explicit ExtValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq {{}, true, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid extensions."); + } + + if (jsonVal.asObj().size() > 0) { + /* Never valid */ + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonVal.loc(), + "This version of the `ctf` plugin doesn't support any CTF 2 extension."); + } + } +}; + +/* + * CTF 2 JSON roles value requirement. + */ +class RolesValReq final : public bt2c::JsonArrayValReq +{ +public: + /* + * Builds a CTF 2 JSON roles value requirement: _validate() + * validates that a given JSON array value only contains the roles + * `validRoles`. + */ + explicit RolesValReq(bt2c::JsonStrValInSetReq::Set validRoles, + const bt2c::Logger& parentLogger) : + bt2c::JsonArrayValReq { + bt2c::JsonStrValInSetReq::shared(std::move(validRoles), parentLogger), parentLogger} + { + } + + static SP shared(bt2c::JsonStrValInSetReq::Set validRoles, const bt2c::Logger& parentLogger) + { + return std::make_shared(std::move(validRoles), parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonArrayValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid roles."); + } + } +}; + +/* + * Adds a JSON object value property requirement having the key `key` to + * `propReqs`, passing `valReq` and `isRequired` to its constructor. + */ +void addToPropReqs(bt2c::JsonObjValReq::PropReqs& propReqs, std::string&& key, + bt2c::JsonValReq::SP valReq, const bool isRequired = false) +{ + propReqs.emplace( + std::make_pair(std::move(key), bt2c::JsonObjValPropReq {std::move(valReq), isRequired})); +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 JSON type + * object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry objTypePropReqEntry(std::string&& type, + const bt2c::Logger& parentLogger) +{ + return {jsonstr::type, {bt2c::JsonStrValInSetReq::shared(std::move(type), parentLogger), true}}; +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 attributes + * object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry attrsPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::attrs, {UserAttrsValReq::shared(parentLogger)}}; +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 extensions object + * property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry extPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::extensions, {ExtValReq::shared(parentLogger)}}; +} + +/* + * CTF 2 JSON field class value abstract requirement. + * + * All derived classes are required to implement a static typeStr() + * method which returns the type string of the field class. + */ +class FcValReq : public bt2c::JsonObjValReq +{ +protected: + /* + * Builds a CTF 2 JSON field class value requirement of type `type`, + * adding `propReqs` to the base JSON object value property + * requirements. + */ + explicit FcValReq(std::string&& type, PropReqs&& propReqs, const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq { + this->_buildPropReqs(std::move(type), std::move(propReqs), parentLogger), parentLogger} + { + } + + /* + * Builds a CTF 2 JSON field class value requirement of type `type`. + */ + explicit FcValReq(std::string&& type, const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), {}, parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) + { + propReqs.insert(objTypePropReqEntry(std::move(type), parentLogger)); + propReqs.insert(attrsPropReqEntry(parentLogger)); + propReqs.insert(extPropReqEntry(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON fixed-length bit array field class value requirement. + */ +class FixedLenBitArrayFcValReq : public FcValReq +{ +protected: + /* + * Builds a CTF 2 JSON fixed-length bit array field class value + * requirement of type `type`, adding `propReqs` to the base JSON + * object value property requirements. + */ + explicit FixedLenBitArrayFcValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), this->_buildPropReqs(std::move(propReqs), parentLogger), + parentLogger} + { + } + + /* + * Builds a CTF 2 JSON fixed-length bit array field class value + * requirement of type `type`. + */ + explicit FixedLenBitArrayFcValReq(std::string&& type, const bt2c::Logger& parentLogger) : + FixedLenBitArrayFcValReq {std::move(type), {}, parentLogger} + { + } + +public: + explicit FixedLenBitArrayFcValReq(const bt2c::Logger& parentLogger) : + FixedLenBitArrayFcValReq {this->typeStr(), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenBitArray; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid fixed-length bit array field class."); + } + } + + static PropReqs _buildPropReqs(PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + addToPropReqs(propReqs, jsonstr::len, + bt2c::JsonUIntValInRangeReq::shared(1, 64, parentLogger), true); + addToPropReqs(propReqs, jsonstr::byteOrder, ByteOrderValReq::shared(parentLogger), true); + addToPropReqs(propReqs, jsonstr::bitOrder, BitOrderValReq::shared(parentLogger)); + addToPropReqs(propReqs, jsonstr::align, AlignValReq::shared(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON fixed-length bit map field class flags value requirement. + * + * An instance of this class validates that a given JSON value is a CTF + * 2 fixed-length bit map field class flags object. + */ +class FixedLenBitMapFcFlagsValReq final : public bt2c::JsonObjValReq +{ +public: + explicit FixedLenBitMapFcFlagsValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq {{}, true, parentLogger}, _mRangeSetReq {parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + + /* Require at least one flag */ + if (jsonVal.asObj().size() < 1) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonVal.loc(), "Expecting at least one flag."); + } + + /* Validate range sets */ + for (auto& keyJsonValPair : jsonVal.asObj()) { + try { + _mRangeSetReq.validate(*keyJsonValPair.second); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid flag `{}`.", keyJsonValPair.first); + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid enumeration field class mappings."); + } + } + + Ctf2JsonIntRangeSetValReqBase _mRangeSetReq; +}; + +/* + * CTF 2 JSON fixed-length bit map field class value requirement. + */ +class FixedLenBitMapFcValReq final : public FixedLenBitArrayFcValReq +{ +public: + explicit FixedLenBitMapFcValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + FixedLenBitArrayFcValReq {this->typeStr(), { + {jsonstr::flags, {FixedLenBitMapFcFlagsValReq::shared(parentLogger), true}}, + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenBitMap; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + + /* + * Validate that the upper value of each flag bit range is + * less than the length of instances. + */ + const auto len = jsonVal.asObj().rawUIntVal(jsonstr::len); + + for (auto& keyJsonValPair : jsonVal.asObj()[jsonstr::flags]->asObj()) { + for (auto& jsonRange : keyJsonValPair.second->asArray()) { + auto& jsonRangeUpper = jsonRange->asArray()[1].asUInt(); + + if (*jsonRangeUpper >= len) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonRangeUpper.loc(), + "Flag `{}`: bit index {} is greater than or equal to " + "the value of the `{}` property ({} bits).", + keyJsonValPair.first, *jsonRangeUpper, jsonstr::len, len); + } + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid fixed-length bit map field class."); + } + } +}; + +/* + * CTF 2 JSON fixed-length boolean field class value requirement. + */ +class FixedLenBoolFcValReq final : public FixedLenBitArrayFcValReq +{ +public: + explicit FixedLenBoolFcValReq(const bt2c::Logger& parentLogger) : + FixedLenBitArrayFcValReq {this->typeStr(), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenBool; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid fixed-length boolean field class."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 integer field + * class preferred display base object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry intFcPrefDispBasePropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::prefDispBase, + {bt2c::JsonUIntValInSetReq::shared({2, 8, 10, 16}, parentLogger)}}; +} + +/* + * CTF 2 JSON fixed-length integer field class value abstract + * requirement. + */ +class FixedLenIntFcValReq : public FixedLenBitArrayFcValReq +{ +protected: + /* + * Builds a CTF 2 JSON fixed-length integer field class value + * requirement of type `type`, adding `propReqs` to the base JSON + * object value property requirements. + */ + explicit FixedLenIntFcValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + FixedLenBitArrayFcValReq { + std::move(type), this->_buildPropReqs(std::move(propReqs), parentLogger), parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + propReqs.insert(intFcPrefDispBasePropReqEntry(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 unsigned + * integer field class roles object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry uIntFcRolesPropReqEntry(const bt2c::Logger& parentLogger) +{ + /* clang-format off */ + return {jsonstr::roles, { + RolesValReq::shared({ + jsonstr::dataStreamClsId, + jsonstr::dataStreamId, + jsonstr::defClkTs, + jsonstr::discEventRecordCounterSnap, + jsonstr::eventRecordClsId, + jsonstr::pktContentLen, + jsonstr::pktEndDefClkTs, + jsonstr::pktMagicNumber, + jsonstr::pktSeqNum, + jsonstr::pktTotalLen, + }, parentLogger) + }}; + /* clang-format on */ +} + +/* + * CTF 2 JSON integer field class mappings value requirement. + * + * An instance of this class validates that a given JSON value is + * a CTF 2 integer field class mappings object, each integer value + * within the integer ranges satisfying an instance of `JsonIntValReqT`. + */ +template +class IntFcMappingsValReq final : public bt2c::JsonObjValReq +{ +public: + explicit IntFcMappingsValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq {{}, true, parentLogger}, _mRangeSetReq {parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + + /* Validate range sets */ + for (auto& keyJsonValPair : jsonVal.asObj()) { + try { + _mRangeSetReq.validate(*keyJsonValPair.second); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid mapping `{}`.", + keyJsonValPair.first); + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid enumeration field class mappings."); + } + } + + Ctf2JsonIntRangeSetValReqBase _mRangeSetReq; +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 integer field + * class mappings object property requirement. + */ +template +bt2c::JsonObjValReq::PropReqsEntry intFcMappingsPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::mappings, {IntFcMappingsValReq::shared(parentLogger)}}; +} + +/* + * CTF 2 JSON fixed-length unsigned integer field class value + * requirement. + */ +class FixedLenUIntFcValReq final : public FixedLenIntFcValReq +{ +public: + /* + * Builds a CTF 2 JSON fixed-length unsigned integer field class + * value requirement. + */ + explicit FixedLenUIntFcValReq(const bt2c::Logger& parentLogger) : + FixedLenIntFcValReq {this->typeStr(), this->_buildPropReqs(parentLogger), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenUInt; + } + +private: + static PropReqs _buildPropReqs(const bt2c::Logger& parentLogger) + { + PropReqs propReqs; + + propReqs.insert(intFcMappingsPropReqEntry(parentLogger)); + propReqs.insert(uIntFcRolesPropReqEntry(parentLogger)); + return propReqs; + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), + "Invalid fixed-length unsigned integer field class."); + } + } +}; + +/* + * CTF 2 JSON fixed-length signed integer field class value requirement. + */ +class FixedLenSIntFcValReq final : public FixedLenIntFcValReq +{ +public: + /* + * Builds a CTF 2 JSON fixed-length signed integer field class + * value requirement. + */ + explicit FixedLenSIntFcValReq(const bt2c::Logger& parentLogger) : + FixedLenIntFcValReq {this->typeStr(), + { + intFcMappingsPropReqEntry(parentLogger), + }, + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenSInt; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid fixed-length signed integer field class."); + } + } +}; + +/* + * CTF 2 JSON fixed-length floating-point number field class value + * requirement. + */ +class FixedLenFloatFcValReq final : public FixedLenBitArrayFcValReq +{ +public: + explicit FixedLenFloatFcValReq(const bt2c::Logger& parentLogger) : + FixedLenBitArrayFcValReq {this->typeStr(), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fixedLenFloat; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), + "Invalid fixed-length floating-point number field class."); + } + } +}; + +/* + * CTF 2 JSON variable-length integer field class value abstract + * requirement. + */ +class VarLenIntFcValReq : public FcValReq +{ +protected: + /* + * Builds a CTF 2 JSON variable-length integer field class value + * requirement of type `type`, adding `propReqs` to the base JSON + * object value property requirements. + */ + explicit VarLenIntFcValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), this->_buildPropReqs(std::move(propReqs), parentLogger), + parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + propReqs.insert(intFcPrefDispBasePropReqEntry(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON variable-length unsigned integer field class value + * requirement. + */ +class VarLenUIntFcValReq : public VarLenIntFcValReq +{ +public: + /* + * Builds a CTF 2 JSON variable-length unsigned integer field class + * value requirement. + */ + explicit VarLenUIntFcValReq(const bt2c::Logger& parentLogger) : + VarLenIntFcValReq {this->typeStr(), this->_buildPropReqs(parentLogger), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::varLenUInt; + } + +private: + static PropReqs _buildPropReqs(const bt2c::Logger& parentLogger) + { + PropReqs propReqs; + + propReqs.insert(intFcMappingsPropReqEntry(parentLogger)); + propReqs.insert(uIntFcRolesPropReqEntry(parentLogger)); + return propReqs; + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), + "Invalid variable-length unsigned integer field class."); + } + } +}; + +/* + * CTF 2 JSON variable-length signed integer field class + * value requirement. + */ +class VarLenSIntFcValReq : public VarLenIntFcValReq +{ +public: + /* + * Builds a CTF 2 JSON variable-length unsigned integer field class + * value requirement. + */ + explicit VarLenSIntFcValReq(const bt2c::Logger& parentLogger) : + VarLenIntFcValReq {this->typeStr(), + { + intFcMappingsPropReqEntry(parentLogger), + }, + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::varLenSInt; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), + "Invalid variable-length signed integer field class."); + } + } +}; + +/* + * CTF 2 JSON string encoding value requirement. + */ +class StrEncodingValReq final : public bt2c::JsonStrValInSetReq +{ +public: + explicit StrEncodingValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonStrValInSetReq {bt2c::JsonStrValInSetReq::Set { + jsonstr::utf8, + jsonstr::utf16Be, + jsonstr::utf16Le, + jsonstr::utf32Be, + jsonstr::utf32Le, + }, + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonStrValInSetReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid string encoding."); + } + } +}; + +/* + * CTF 2 JSON string field class value requirement. + */ +class StrFcValReq : public FcValReq +{ +protected: + explicit StrFcValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), this->_buildPropReqs(std::move(propReqs), parentLogger), + parentLogger} + { + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid string field class."); + } + } + +private: + static PropReqs _buildPropReqs(PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + addToPropReqs(propReqs, jsonstr::encoding, StrEncodingValReq::shared(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON null-terminated string field class value requirement. + */ +class NullTerminatedStrFcValReq final : public StrFcValReq +{ +public: + explicit NullTerminatedStrFcValReq(const bt2c::Logger& parentLogger) : + StrFcValReq {this->typeStr(), {}, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::nullTerminatedStr; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid null-terminated string field class."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 static-length + * field class length object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry staticLenFcLenPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::len, + {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger), true}}; +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 + * dynamic-length field class length field location object + * property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry dynLenFcLenFieldLocPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::lenFieldLoc, {FieldLocValReq::shared(parentLogger), true}}; +} + +/* + * CTF 2 JSON static-length string field class value requirement. + */ +class StaticLenStrFcValReq final : public StrFcValReq +{ +public: + explicit StaticLenStrFcValReq(const bt2c::Logger& parentLogger) : + StrFcValReq {this->typeStr(), {staticLenFcLenPropReqEntry(parentLogger)}, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::staticLenStr; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + StrFcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid static-length string field class."); + } + } +}; + +/* + * CTF 2 JSON dynamic-length string field class value requirement. + */ +class DynLenStrFcValReq final : public StrFcValReq +{ +public: + explicit DynLenStrFcValReq(const bt2c::Logger& parentLogger) : + StrFcValReq {this->typeStr(), {dynLenFcLenFieldLocPropReqEntry(parentLogger)}, parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::dynLenStr; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + StrFcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid dynamic-length string field class."); + } + } +}; + +/* + * CTF 2 JSON BLOB field class value abstract requirement. + */ +class BlobFcValReq : public FcValReq +{ +protected: + /* + * Builds a CTF 2 JSON BLOB field class value requirement of type + * `type`, adding `propReqs` to the base JSON object value property + * requirements. + */ + explicit BlobFcValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), this->_buildPropReqs(std::move(propReqs), parentLogger), + parentLogger} + { + } + + /* + * Builds a CTF 2 JSON BLOB field class value requirement of type + * `type`. + */ + explicit BlobFcValReq(std::string&& type, const bt2c::Logger& parentLogger) : + BlobFcValReq {std::move(type), {}, parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + addToPropReqs(propReqs, jsonstr::mediaType, + bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON static-length BLOB field class value requirement. + */ +class StaticLenBlobFcValReq final : public BlobFcValReq +{ +public: + explicit StaticLenBlobFcValReq(const bt2c::Logger& parentLogger) : + BlobFcValReq {this->typeStr(), this->_buildPropReqs(parentLogger), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::staticLenBlob; + } + +private: + static PropReqs _buildPropReqs(const bt2c::Logger& parentLogger) + { + PropReqs propReqs; + + propReqs.insert(staticLenFcLenPropReqEntry(parentLogger)); + propReqs.insert( + {jsonstr::roles, RolesValReq::shared({jsonstr::metadataStreamUuid}, parentLogger)}); + return propReqs; + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + + const auto jsonRoles = jsonVal.asObj()[jsonstr::roles]; + + if (jsonRoles && !jsonRoles->asArray().isEmpty()) { + /* The only valid role is the metadata stream UUID */ + auto& jsonLen = jsonVal.asObj()[jsonstr::len]->asUInt(); + + if (*jsonLen != 16) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonLen.loc(), + "`{}` property: expecting 16, not {}, because the field class has the `{}` role.", + jsonstr::len, *jsonLen, jsonstr::metadataStreamUuid); + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid static-length BLOB field class."); + } + } +}; + +/* + * CTF 2 JSON dynamic-length BLOB field class value requirement. + */ +class DynLenBlobFcValReq final : public BlobFcValReq +{ +public: + explicit DynLenBlobFcValReq(const bt2c::Logger& parentLogger) : + BlobFcValReq {this->typeStr(), + {dynLenFcLenFieldLocPropReqEntry(parentLogger)}, + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::dynLenBlob; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid dynamic-length BLOB field class."); + } + } +}; + +class AnyFullBlownFcValReq; + +/* + * CTF 2 field classes are recursive, in that some field classes may + * contain other field classes. + * + * To make it possible to build a `AnyFullBlownFcValReq` instance + * without a shared pointer, the constructor of compound field class + * requirements accepts a `const AnyFullBlownFcValReq&` (raw reference) + * parameter. The raw reference must therefore remain valid as long as + * the compound field class using it exists. + * + * Because JSON value requirements work with shared pointers to + * `const Ctf2JsonValReq` (`bt2c::JsonValReq::SP`), this + * `AnyFcValReqWrapper` class simply wraps such a + * `const AnyFullBlownFcValReq *` value: its _validate() method forwards + * the call. An `AnyFcValReqWrapper` instance doesn't own the + * raw pointer. + */ +class AnyFcValReqWrapper final : public bt2c::JsonValReq +{ +public: + explicit AnyFcValReqWrapper(const AnyFullBlownFcValReq& anyFcValReq, + const bt2c::Logger& parentLogger) : + bt2c::JsonValReq {parentLogger}, + _mAnyFullBlownFcValReq {&anyFcValReq} + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override; + + const AnyFullBlownFcValReq *_mAnyFullBlownFcValReq; +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 field class + * object property requirement having the key `key`. + */ +bt2c::JsonObjValReq::PropReqsEntry +anyFcPropReqEntry(std::string key, const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bool isRequired, const bt2c::Logger& parentLogger) +{ + return {std::move(key), + {AnyFcValReqWrapper::shared(anyFullBlownFcValReq, parentLogger), isRequired}}; +} + +/* + * Calls the other anyFcPropReqEntry() with `isRequired` set to `false`. + */ +bt2c::JsonObjValReq::PropReqsEntry +anyFcPropReqEntry(std::string key, const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) +{ + return anyFcPropReqEntry(std::move(key), anyFullBlownFcValReq, false, parentLogger); +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 object name + * object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry namePropReqEntry(const bool isRequired, + const bt2c::Logger& parentLogger) +{ + return {jsonstr::name, + {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger), isRequired}}; +} + +/* + * CTF 2 JSON structure field member class value requirement. + */ +class StructFieldMemberClsValReq final : public bt2c::JsonObjValReq +{ +public: + explicit StructFieldMemberClsValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + namePropReqEntry(true, parentLogger), + anyFcPropReqEntry(jsonstr::fc, anyFullBlownFcValReq, parentLogger), + attrsPropReqEntry(parentLogger), + extPropReqEntry(parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid structure field member class."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 minimum + * alignment object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry minAlignPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::minAlign, AlignValReq::shared(parentLogger)}; +} + +class UniqueEntryNamesValidator final +{ +public: + explicit UniqueEntryNamesValidator(const bt2c::Logger& parentLogger) : _mLogger {parentLogger} + { + } + + /* + * Validates that, within the JSON array value having the key + * `propName` within the JSON object value `jsonVal`, the `name` + * property of each (JSON object value) element, if it exists, + * is unique. + * + * Throws `TextParseError` on failure, using `elemName` to name the + * element having a duplicate name. + */ + void validate(const bt2c::JsonVal& jsonVal, const char * const propName, + const char * const elemName) const + { + const auto jsonEntries = jsonVal.asObj()[propName]; + + if (!jsonEntries) { + /* Empty */ + return; + } + + /* Use a set to accumulate unique names */ + std::unordered_set names; + + for (auto& jsonEntry : jsonEntries->asArray()) { + const auto jsonName = jsonEntry->asObj()[jsonstr::name]; + + if (!jsonName) { + /* No `name` property */ + continue; + } + + auto& jsonNameStr = jsonName->asStr(); + + if (bt2c::contains(names, *jsonNameStr)) { + /* Already in set */ + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW(bt2c::Error, jsonName->loc(), + "Duplicate {} name `{}`.", elemName, + (*jsonNameStr)); + } + + /* Add to set */ + names.insert(*jsonNameStr); + } + } + +private: + bt2c::Logger _mLogger; +}; + +/* + * CTF 2 JSON structure field class value requirement. + */ +class StructFcValReq final : public FcValReq +{ +public: + explicit StructFcValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FcValReq {this->typeStr(), { + {jsonstr::memberClasses, { + bt2c::JsonArrayValReq::shared( + StructFieldMemberClsValReq::shared(anyFullBlownFcValReq, parentLogger), + parentLogger + ) + }}, + minAlignPropReqEntry(parentLogger), + }, parentLogger}, + _mUniqueEntryNamesValidator {parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::structure; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + + /* Validate that member class names are unique */ + _mUniqueEntryNamesValidator.validate(jsonVal, jsonstr::memberClasses, + "structure field member class"); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid structure field class."); + } + } + + UniqueEntryNamesValidator _mUniqueEntryNamesValidator; +}; + +/* + * CTF 2 JSON array field class value abstract requirement. + */ +class ArrayFcValReq : public FcValReq +{ +protected: + /* + * Builds a CTF 2 JSON array field class value requirement of type + * `type`, adding `propReqs` to the base JSON object value property + * requirements. + */ + explicit ArrayFcValReq(std::string&& type, const AnyFullBlownFcValReq& anyFullBlownFcValReq, + PropReqs&& propReqs, const bt2c::Logger& parentLogger) : + FcValReq {std::move(type), + this->_buildPropReqs(anyFullBlownFcValReq, std::move(propReqs), parentLogger), + parentLogger} + { + } + + /* + * Builds a CTF 2 JSON array field class value requirement of type + * `type`. + */ + explicit ArrayFcValReq(std::string&& type, const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + ArrayFcValReq {std::move(type), anyFullBlownFcValReq, {}, parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + PropReqs&& propReqs, const bt2c::Logger& parentLogger) + { + propReqs.insert(anyFcPropReqEntry(jsonstr::elemFc, anyFullBlownFcValReq, parentLogger)); + propReqs.insert(minAlignPropReqEntry(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 JSON static-length array field class value requirement. + */ +class StaticLenArrayFcValReq final : public ArrayFcValReq +{ +public: + explicit StaticLenArrayFcValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + ArrayFcValReq {this->typeStr(), anyFullBlownFcValReq, { + staticLenFcLenPropReqEntry(parentLogger) + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::staticLenArray; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid static-length array field class."); + } + } +}; + +/* + * CTF 2 JSON dynamic-length array field class value requirement. + */ +class DynLenArrayFcValReq final : public ArrayFcValReq +{ +public: + explicit DynLenArrayFcValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + ArrayFcValReq {this->typeStr(), anyFullBlownFcValReq, { + dynLenFcLenFieldLocPropReqEntry(parentLogger) + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::dynLenArray; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid dynamic-length array field class."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 selector + * field location object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry selFieldLocPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::selFieldLoc, {FieldLocValReq::shared(parentLogger), true}}; +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 selector + * field ranges object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry selFieldRangesPropReqEntry(const bool isRequired, + const bt2c::Logger& parentLogger) +{ + return {jsonstr::selFieldRanges, + {ctf::src::Ctf2JsonIntRangeSetValReq::shared(parentLogger), isRequired}}; +} + +/* + * CTF 2 JSON optional field class value requirement. + */ +class OptionalFcValReq final : public FcValReq +{ +public: + explicit OptionalFcValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FcValReq {this->typeStr(), { + anyFcPropReqEntry(jsonstr::fc, anyFullBlownFcValReq, parentLogger), + selFieldLocPropReqEntry(parentLogger), + selFieldRangesPropReqEntry(false, parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::optional; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid optional field class."); + } + } +}; + +/* + * CTF 2 JSON variant field class option class value requirement. + */ +class VariantFcOptValReq final : public bt2c::JsonObjValReq +{ +public: + explicit VariantFcOptValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + namePropReqEntry(false, parentLogger), + anyFcPropReqEntry(jsonstr::fc, anyFullBlownFcValReq, parentLogger), + selFieldRangesPropReqEntry(true, parentLogger), + attrsPropReqEntry(parentLogger), + extPropReqEntry(parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + /* + * Not checking for integer range overlaps here because we don't + * know the signedness of those ranges yet (depends on the + * effective selector field class(es)). + * + * This will be easier to do once we know the signedness, + * comparing only integers having the same type. + */ + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid variant field class option."); + } + } +}; + +/* + * CTF 2 JSON variant field class value requirement. + */ +class VariantFcValReq final : public FcValReq +{ +public: + explicit VariantFcValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FcValReq {this->typeStr(), { + {jsonstr::opts, { + bt2c::JsonArrayValReq::shared(1, bt2s::nullopt, + VariantFcOptValReq::shared(anyFullBlownFcValReq, parentLogger), + parentLogger), + true + }}, + selFieldLocPropReqEntry(parentLogger), + }, parentLogger}, + _mUniqueEntryNamesValidator {parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::variant; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FcValReq::_validate(jsonVal); + + /* Validate that option names are unique */ + _mUniqueEntryNamesValidator.validate(jsonVal, jsonstr::opts, + "variant field class option"); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid variant field class."); + } + } + + UniqueEntryNamesValidator _mUniqueEntryNamesValidator; +}; + +/* + * CTF 2 JSON (any) full-blown field class value requirement. + */ +class AnyFullBlownFcValReq final : public bt2c::JsonObjValReq +{ +public: + explicit AnyFullBlownFcValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + { + jsonstr::type, + { + bt2c::JsonStrValInSetReq::shared({ + FixedLenBitArrayFcValReq::typeStr(), + FixedLenBoolFcValReq::typeStr(), + FixedLenBitMapFcValReq::typeStr(), + FixedLenUIntFcValReq::typeStr(), + FixedLenSIntFcValReq::typeStr(), + FixedLenFloatFcValReq::typeStr(), + VarLenUIntFcValReq::typeStr(), + VarLenSIntFcValReq::typeStr(), + NullTerminatedStrFcValReq::typeStr(), + StaticLenStrFcValReq::typeStr(), + DynLenStrFcValReq::typeStr(), + StaticLenBlobFcValReq::typeStr(), + DynLenBlobFcValReq::typeStr(), + StructFcValReq::typeStr(), + StaticLenArrayFcValReq::typeStr(), + DynLenArrayFcValReq::typeStr(), + OptionalFcValReq::typeStr(), + VariantFcValReq::typeStr(), + }, parentLogger), true + } + } + }, true, parentLogger}, + _mFlBitArrayFcValReq {parentLogger}, + _mFlBoolFcValReq {parentLogger}, + _mFlBitMapFcValReq {parentLogger}, + _mFlUIntFcValReq {parentLogger}, + _mFlSIntFcValReq {parentLogger}, + _mFlFloatFcValReq {parentLogger}, + _mVlUIntFcValReq {parentLogger}, + _mVlSIntFcValReq {parentLogger}, + _mNtStrFcValReq {parentLogger}, + _mStaticLenStrFcValReq {parentLogger}, + _mDynLenStrFcValReq {parentLogger}, + _mStaticLenBlobFcValReq {parentLogger}, + _mDynLenBlobFcValReq {parentLogger}, + _mStructFcValReq {*this, parentLogger}, + _mStaticLenArrayFcValReq {*this, parentLogger}, + _mDynLenArrayFcValReq {*this, parentLogger}, + _mOptionalFcValReq {*this, parentLogger}, + _mVariantFcValReq {*this, parentLogger} + /* clang-format on */ + { + this->_addToFcValReqs(_mFlBitArrayFcValReq); + this->_addToFcValReqs(_mFlBoolFcValReq); + this->_addToFcValReqs(_mFlBitMapFcValReq); + this->_addToFcValReqs(_mFlUIntFcValReq); + this->_addToFcValReqs(_mFlSIntFcValReq); + this->_addToFcValReqs(_mFlFloatFcValReq); + this->_addToFcValReqs(_mVlUIntFcValReq); + this->_addToFcValReqs(_mVlSIntFcValReq); + this->_addToFcValReqs(_mNtStrFcValReq); + this->_addToFcValReqs(_mStaticLenStrFcValReq); + this->_addToFcValReqs(_mDynLenStrFcValReq); + this->_addToFcValReqs(_mStaticLenBlobFcValReq); + this->_addToFcValReqs(_mDynLenBlobFcValReq); + this->_addToFcValReqs(_mStructFcValReq); + this->_addToFcValReqs(_mStaticLenArrayFcValReq); + this->_addToFcValReqs(_mDynLenArrayFcValReq); + this->_addToFcValReqs(_mOptionalFcValReq); + this->_addToFcValReqs(_mVariantFcValReq); + } + +private: + template + void _addToFcValReqs(const JsonValReqT& valReq) + { + const auto typeStr = JsonValReqT::typeStr(); + + BT_ASSERT(!bt2c::contains(_mFcValReqs, typeStr)); + _mFcValReqs.insert(std::make_pair(typeStr, &valReq)); + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid field class."); + } + + /* + * This part doesn't need to be catched because the specific + * _validate() method already appends a message like "Invalid + * xyz field class:" to the exception. + */ + const auto it = _mFcValReqs.find(*jsonVal.asObj()[jsonstr::type]->asStr()); + + BT_ASSERT(it != _mFcValReqs.end()); + it->second->validate(jsonVal); + } + + /* Subrequirements */ + FixedLenBitArrayFcValReq _mFlBitArrayFcValReq; + FixedLenBoolFcValReq _mFlBoolFcValReq; + FixedLenBitMapFcValReq _mFlBitMapFcValReq; + FixedLenUIntFcValReq _mFlUIntFcValReq; + FixedLenSIntFcValReq _mFlSIntFcValReq; + FixedLenFloatFcValReq _mFlFloatFcValReq; + VarLenUIntFcValReq _mVlUIntFcValReq; + VarLenSIntFcValReq _mVlSIntFcValReq; + NullTerminatedStrFcValReq _mNtStrFcValReq; + StaticLenStrFcValReq _mStaticLenStrFcValReq; + DynLenStrFcValReq _mDynLenStrFcValReq; + StaticLenBlobFcValReq _mStaticLenBlobFcValReq; + DynLenBlobFcValReq _mDynLenBlobFcValReq; + StructFcValReq _mStructFcValReq; + StaticLenArrayFcValReq _mStaticLenArrayFcValReq; + DynLenArrayFcValReq _mDynLenArrayFcValReq; + OptionalFcValReq _mOptionalFcValReq; + VariantFcValReq _mVariantFcValReq; + + /* + * Field class type string to JSON field class requirement. + * + * Values are owned by the members above. + */ + std::unordered_map _mFcValReqs; +}; + +void AnyFcValReqWrapper::_validate(const bt2c::JsonVal& jsonVal) const +{ + /* Check for field class alias name first (JSON string) */ + if (jsonVal.isStr()) { + /* + * Always valid: Ctf2FcBuilder::buildFcFromJsonVal() will + * validate that the field class alias exists. + */ + return; + } + + /* Delegate to AnyFullBlownFcValReq::validate() */ + _mAnyFullBlownFcValReq->validate(jsonVal); +} + +/* + * CTF 2 JSON fragment value abstract requirement. + */ +class FragmentValReq : public bt2c::JsonObjValReq +{ +protected: + /* + * Builds a CTF 2 JSON fragment value requirement of type `type`, + * adding `propReqs` to the base JSON object value property + * requirements. + */ + explicit FragmentValReq(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) : + bt2c::JsonObjValReq { + this->_buildPropReqs(std::move(type), std::move(propReqs), parentLogger), parentLogger} + { + } + + /* + * Builds a CTF 2 JSON fragment value requirement of type `type`. + */ + explicit FragmentValReq(std::string&& type, const bt2c::Logger& parentLogger) : + FragmentValReq {std::move(type), {}, parentLogger} + { + } + +private: + static PropReqs _buildPropReqs(std::string&& type, PropReqs&& propReqs, + const bt2c::Logger& parentLogger) + { + propReqs.insert(objTypePropReqEntry(std::move(type), parentLogger)); + propReqs.insert(attrsPropReqEntry(parentLogger)); + propReqs.insert(extPropReqEntry(parentLogger)); + return std::move(propReqs); + } +}; + +/* + * CTF 2 preamble fragment value requirement. + */ +class PreambleFragmentValReq final : public FragmentValReq +{ +public: + explicit PreambleFragmentValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + {jsonstr::version, {bt2c::JsonUIntValInSetReq::shared(2, parentLogger), true}}, + {jsonstr::uuid, {UuidValReq::shared(parentLogger)}}, + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::preamble; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid preamble fragment."); + } + } +}; + +/* + * CTF 2 field class alias fragment value requirement. + */ +class FcAliasFragmentValReq final : public FragmentValReq +{ +public: + explicit FcAliasFragmentValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + namePropReqEntry(true, parentLogger), + anyFcPropReqEntry(jsonstr::fc, _mAnyFullBlownFcValReq, true, parentLogger), + }, parentLogger}, + _mAnyFullBlownFcValReq {parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::fcAlias; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid field class alias fragment."); + } + } + +private: + AnyFullBlownFcValReq _mAnyFullBlownFcValReq; +}; + +/* + * CTF 2 JSON clock offset value requirement. + */ +class ClkOffsetValReq final : public bt2c::JsonObjValReq +{ +public: + explicit ClkOffsetValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + {jsonstr::seconds, {bt2c::JsonAnyIntValReq::shared(parentLogger)}}, + {jsonstr::cycles, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger)}}, + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid clock offset."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 object + * namespace object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry nsPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::ns, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger)}}; +} + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 object UID + * object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry uidPropReqEntry(const bool isRequired, + const bt2c::Logger& parentLogger) +{ + return {jsonstr::uid, + {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger), isRequired}}; +} + +/* + * CTF 2 JSON clock origin object value requirement. + */ +class ClkOriginObjValReq final : public bt2c::JsonObjValReq +{ +public: + explicit ClkOriginObjValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + nsPropReqEntry(parentLogger), + namePropReqEntry(true, parentLogger), + uidPropReqEntry(true, parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } +}; + +/* + * CTF 2 JSON clock class origin property requirement. + */ +class ClkClsOriginValReq final : public bt2c::JsonValReq +{ +public: + explicit ClkClsOriginValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonValReq {parentLogger}, _mObjReq {parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + /* Check for `unix-epoch` string */ + if (jsonVal.isStr()) { + if (*jsonVal.asStr() != jsonstr::unixEpoch) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonVal.loc(), "Expecting `{}`.", + jsonstr::unixEpoch); + } + } else { + /* Must be a valid clock origin object */ + if (!jsonVal.isObj()) { + /* Make a clear message about the expected type */ + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonVal.loc(), + "Expecting a string or an object."); + } + + /* Delegate to ClkOriginObjValReq::validate() */ + _mObjReq.validate(jsonVal); + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid clock origin."); + } + } + +private: + ClkOriginObjValReq _mObjReq; +}; + +/* + * CTF 2 JSON clock class fragment value requirement. + */ +class ClkClsFragmentValReq final : public FragmentValReq +{ +public: + explicit ClkClsFragmentValReq(const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + {jsonstr::id, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger), true}}, + nsPropReqEntry(parentLogger), + namePropReqEntry(false, parentLogger), + uidPropReqEntry(false, parentLogger), + {jsonstr::freq, {bt2c::JsonUIntValInRangeReq::shared(1, bt2s::nullopt, parentLogger), true}}, + {jsonstr::descr, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger)}}, + {jsonstr::origin, {ClkClsOriginValReq::shared(parentLogger)}}, + {jsonstr::offsetFromOrigin, {ClkOffsetValReq::shared(parentLogger)}}, + {jsonstr::precision, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger)}}, + {jsonstr::accuracy, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger)}}, + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::clkCls; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + + /* + * Validate that `seconds` within `offset-from-origin`, if + * it exists, is less than `frequency`. + */ + auto& jsonObj = jsonVal.asObj(); + + if (const auto jsonOffset = jsonObj[jsonstr::offsetFromOrigin]) { + if (const auto jsonCycles = jsonOffset->asObj()[jsonstr::cycles]) { + const auto cycles = *jsonCycles->asUInt(); + const auto freq = *jsonObj[jsonstr::freq]->asUInt(); + + if (cycles >= freq) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC( + this->_logger(), bt2c::Error, jsonCycles->loc(), + "Invalid `{}` property of `{}` property: " + "value {} is greater than the value of the `{}` property ({}).", + jsonstr::cycles, jsonstr::offsetFromOrigin, cycles, jsonstr::freq, + freq); + } + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid clock class fragment."); + } + } +}; + +/* + * CTF 2 JSON trace class fragment value requirement. + */ +class TraceClsFragmentValReq final : public FragmentValReq +{ +public: + explicit TraceClsFragmentValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + nsPropReqEntry(parentLogger), + namePropReqEntry(false, parentLogger), + uidPropReqEntry(false, parentLogger), + {jsonstr::env, {TraceEnvValReq::shared(parentLogger)}}, + anyFcPropReqEntry(jsonstr::pktHeaderFc, anyFullBlownFcValReq, parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::traceCls; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid trace class fragment."); + } + } +}; + +/* + * Returns the pair (suitable for insertion into a + * `bt2c::JsonObjValReq::PropReqs` instance) for the CTF 2 object + * numeric ID object property requirement. + */ +bt2c::JsonObjValReq::PropReqsEntry idPropReqEntry(const bt2c::Logger& parentLogger) +{ + return {jsonstr::id, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger)}}; +} + +/* + * CTF 2 JSON data stream class fragment value requirement. + */ +class DataStreamClsFragmentValReq final : public FragmentValReq +{ +public: + explicit DataStreamClsFragmentValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + idPropReqEntry(parentLogger), + nsPropReqEntry(parentLogger), + namePropReqEntry(false, parentLogger), + uidPropReqEntry(false, parentLogger), + {jsonstr::defClkClsId, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::Str, parentLogger)}}, + anyFcPropReqEntry(jsonstr::pktCtxFc, anyFullBlownFcValReq, parentLogger), + anyFcPropReqEntry(jsonstr::eventRecordHeaderFc, anyFullBlownFcValReq, parentLogger), + anyFcPropReqEntry(jsonstr::eventRecordCommonCtxFc, anyFullBlownFcValReq, parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::dataStreamCls; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid data stream class fragment."); + } + } +}; + +/* + * CTF 2 JSON event record class fragment value requirement. + */ +class EventRecordClsFragmentValReq final : public FragmentValReq +{ +public: + explicit EventRecordClsFragmentValReq(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) : + /* clang-format off */ + FragmentValReq {this->typeStr(), { + idPropReqEntry(parentLogger), + nsPropReqEntry(parentLogger), + namePropReqEntry(false, parentLogger), + uidPropReqEntry(false, parentLogger), + {jsonstr::dataStreamClsId, {bt2c::JsonValHasTypeReq::shared(bt2c::ValType::UInt, parentLogger)}}, + anyFcPropReqEntry(jsonstr::specCtxFc, anyFullBlownFcValReq, parentLogger), + anyFcPropReqEntry(jsonstr::payloadFc, anyFullBlownFcValReq, parentLogger), + }, parentLogger} + /* clang-format on */ + { + } + + static SP shared(const AnyFullBlownFcValReq& anyFullBlownFcValReq, + const bt2c::Logger& parentLogger) + { + return std::make_shared(anyFullBlownFcValReq, parentLogger); + } + + static const char *typeStr() noexcept + { + return jsonstr::eventRecordCls; + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + FragmentValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC( + this->_logger(), jsonVal.loc(), "Invalid event record class fragment."); + } + } +}; + +} /* namespace */ + +namespace internal { + +/* + * CTF 2 JSON (any) fragment value requirement (implementation). + */ +class Ctf2JsonAnyFragmentValReqImpl final : public bt2c::JsonObjValReq +{ +public: + explicit Ctf2JsonAnyFragmentValReqImpl(const bt2c::Logger& parentLogger) : + /* clang-format off */ + bt2c::JsonObjValReq {{ + { + jsonstr::type, { + bt2c::JsonStrValInSetReq::shared({ + PreambleFragmentValReq::typeStr(), + FcAliasFragmentValReq::typeStr(), + TraceClsFragmentValReq::typeStr(), + ClkClsFragmentValReq::typeStr(), + DataStreamClsFragmentValReq::typeStr(), + EventRecordClsFragmentValReq::typeStr(), + }, parentLogger), true + } + } + }, true, parentLogger}, + _mAnyFullBlownFcValReq {parentLogger}, + _preambleFragmentValReq {parentLogger}, + _fcAliasFragmentValReq {parentLogger}, + _traceClsFragmentValReq {_mAnyFullBlownFcValReq, parentLogger}, + _clkClsFragmentValReq {parentLogger}, + _dataStreamClsFragmentValReq {_mAnyFullBlownFcValReq, parentLogger}, + _eventRecordClsFragmentValReq {_mAnyFullBlownFcValReq, parentLogger} + /* clang-format on */ + { + this->_addToFcValReqs(_preambleFragmentValReq); + this->_addToFcValReqs(_fcAliasFragmentValReq); + this->_addToFcValReqs(_traceClsFragmentValReq); + this->_addToFcValReqs(_clkClsFragmentValReq); + this->_addToFcValReqs(_dataStreamClsFragmentValReq); + this->_addToFcValReqs(_eventRecordClsFragmentValReq); + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + template + void _addToFcValReqs(const JsonValReqT& valReq) + { + const auto typeStr = JsonValReqT::typeStr(); + + BT_ASSERT(!bt2c::contains(_fragValReqs, typeStr)); + _fragValReqs.insert(std::make_pair(typeStr, &valReq)); + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonObjValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid fragment."); + } + + /* + * This part doesn't need to be catched because the specific + * _validate() method already appends a message like + * "Invalid xyz fragment:" to the exception. + */ + const auto it = _fragValReqs.find(*jsonVal.asObj()[jsonstr::type]->asStr()); + + BT_ASSERT(it != _fragValReqs.end()); + it->second->validate(jsonVal); + } + + /* Single any full-blown field class value requirement instance */ + AnyFullBlownFcValReq _mAnyFullBlownFcValReq; + + /* Subrequirements */ + PreambleFragmentValReq _preambleFragmentValReq; + FcAliasFragmentValReq _fcAliasFragmentValReq; + TraceClsFragmentValReq _traceClsFragmentValReq; + ClkClsFragmentValReq _clkClsFragmentValReq; + DataStreamClsFragmentValReq _dataStreamClsFragmentValReq; + EventRecordClsFragmentValReq _eventRecordClsFragmentValReq; + + /* + * Fragment type string to JSON fragment requirement. + * + * Values are owned by the members above. + */ + std::unordered_map _fragValReqs; +}; + +} /* namespace internal */ + +Ctf2JsonAnyFragmentValReq::Ctf2JsonAnyFragmentValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonValReq {parentLogger}, + _mImpl {new internal::Ctf2JsonAnyFragmentValReqImpl {parentLogger}} +{ +} + +Ctf2JsonAnyFragmentValReq::~Ctf2JsonAnyFragmentValReq() +{ +} + +void Ctf2JsonAnyFragmentValReq::_validate(const bt2c::JsonVal& jsonVal) const +{ + _mImpl->validate(jsonVal); +} + +} /* namespace src */ +} /* namespace ctf */ diff --git a/src/plugins/ctf/common/src/metadata/json/val-req.hpp b/src/plugins/ctf/common/src/metadata/json/val-req.hpp new file mode 100644 index 00000000..5cc6147b --- /dev/null +++ b/src/plugins/ctf/common/src/metadata/json/val-req.hpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2022-2024 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_VAL_REQ_HPP +#define BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_VAL_REQ_HPP + +#include + +#include "cpp-common/bt2c/exc.hpp" +#include "cpp-common/bt2c/json-val-req.hpp" +#include "cpp-common/bt2c/json-val.hpp" +#include "cpp-common/bt2c/logging.hpp" + +namespace ctf { +namespace src { + +/* + * CTF 2 JSON integer range value requirement. + * + * An instance of this class validates that a given JSON value is + * a CTF 2 integer range, both contained values satisfying + * an instance of `JsonIntValReqT`. + */ +template +class Ctf2JsonIntRangeValReq final : public bt2c::JsonArrayValReq +{ +public: + explicit Ctf2JsonIntRangeValReq(const bt2c::Logger& parentLogger) : + bt2c::JsonArrayValReq {2, JsonIntValReqT::shared(parentLogger), parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + template + void _throwLowerGtUpper(const LowerT lower, const UpperT upper, + const bt2c::JsonVal& jsonVal) const + { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_THROW_SPEC(this->_logger(), bt2c::Error, jsonVal.loc(), + "{} is greater than {}.", lower, upper); + } + + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonArrayValReq::_validate(jsonVal); + + /* + * Here's the truth table: + * + * ╔════╦════════════╦════════════╦═══════════════════════════╗ + * ║ ID ║ Lower ║ Upper ║ Valid? ║ + * ╠════╬════════════╬════════════╬═══════════════════════════╣ + * ║ 1 ║ Unsigned ║ Unsigned ║ Lower < upper ║ + * ║ 2 ║ Signed ║ Signed ║ Lower < upper ║ + * ║ 3 ║ Unsigned ║ Signed ≥ 0 ║ Lower < upper as unsigned ║ + * ║ 4 ║ Unsigned ║ Signed < 0 ║ No ║ + * ║ 5 ║ Signed ≥ 0 ║ Unsigned ║ Lower as unsigned < upper ║ + * ║ 6 ║ Signed < 0 ║ Unsigned ║ Yes ║ + * ╚════╩════════════╩════════════╩═══════════════════════════╝ + */ + auto& lowerJsonVal = jsonVal.asArray()[0]; + auto& upperJsonVal = jsonVal.asArray()[1]; + + if (lowerJsonVal.isUInt()) { + const auto uLower = *lowerJsonVal.asUInt(); + + if (upperJsonVal.isUInt()) { + const auto uUpper = *upperJsonVal.asUInt(); + + if (uUpper < uLower) { + /* ID 1 */ + this->_throwLowerGtUpper(uLower, uUpper, jsonVal); + } + } else { + const auto sUpper = *upperJsonVal.asSInt(); + + if (sUpper < 0) { + /* ID 4 */ + this->_throwLowerGtUpper(uLower, sUpper, jsonVal); + } + + if (static_cast(sUpper) < uLower) { + /* ID 3 */ + this->_throwLowerGtUpper(uLower, sUpper, jsonVal); + } + } + } else { + const auto sLower = *lowerJsonVal.asSInt(); + + if (upperJsonVal.isSInt()) { + const auto sUpper = *upperJsonVal.asSInt(); + + if (sUpper < sLower) { + /* ID 2 */ + this->_throwLowerGtUpper(sLower, sUpper, jsonVal); + } + } else if (sLower >= 0) { + const auto uUpper = *upperJsonVal.asUInt(); + + if (uUpper < static_cast(sLower)) { + /* ID 5 */ + this->_throwLowerGtUpper(sLower, uUpper, jsonVal); + } + } + } + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid integer range."); + } + } +}; + +/* + * CTF 2 JSON integer range set value requirement. + * + * An instance of this class validates that a given JSON value is a + * CTF 2 integer range set, each element satisfying an instance of + * `Ctf2JsonIntRangeValReq`. + */ +template +class Ctf2JsonIntRangeSetValReqBase final : public bt2c::JsonArrayValReq +{ +public: + explicit Ctf2JsonIntRangeSetValReqBase(const bt2c::Logger& parentLogger) : + bt2c::JsonArrayValReq {1, bt2s::nullopt, + Ctf2JsonIntRangeValReq::shared(parentLogger), + parentLogger} + { + } + + static SP shared(const bt2c::Logger& parentLogger) + { + return std::make_shared(parentLogger); + } + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override + { + try { + bt2c::JsonArrayValReq::_validate(jsonVal); + } catch (const bt2c::Error&) { + BT_CPPLOGE_TEXT_LOC_APPEND_CAUSE_AND_RETHROW_SPEC(this->_logger(), jsonVal.loc(), + "Invalid integer range set."); + } + } +}; + +/* + * CTF 2 JSON unsigned integer range set value requirement. + */ +using Ctf2JsonUIntRangeSetValReq = Ctf2JsonIntRangeSetValReqBase; + +/* + * CTF 2 JSON signed integer range set value requirement. + */ +using Ctf2JsonSIntRangeSetValReq = Ctf2JsonIntRangeSetValReqBase; + +/* + * CTF 2 JSON integer range set value requirement. + */ +using Ctf2JsonIntRangeSetValReq = Ctf2JsonIntRangeSetValReqBase; + +namespace internal { + +class Ctf2JsonAnyFragmentValReqImpl; + +} + +/* + * CTF 2 JSON (any) fragment value requirement. + * + * This value requirement doesn't validate: + * + * • The keys of the dependent (dynamic-length, optional, and variant) + * field classes. + * + * In other words, it validates the form of field locations, but + * doesn't use them to find key field classes because there's not + * enough context. + * + * • Relative field locations. + * + * • Field roles. + * + * • Overlaps of integer ranges between variant field class options. + */ +class Ctf2JsonAnyFragmentValReq : public bt2c::JsonValReq +{ +public: + explicit Ctf2JsonAnyFragmentValReq(const bt2c::Logger& parentLogger); + ~Ctf2JsonAnyFragmentValReq(); + +private: + void _validate(const bt2c::JsonVal& jsonVal) const override; + + /* Pointer to implementation */ + std::unique_ptr _mImpl; +}; + +} /* namespace src */ +} /* namespace ctf */ + +#endif /* BABELTRACE_PLUGINS_CTF_COMMON_SRC_METADATA_JSON_VAL_REQ_HPP */ -- 2.34.1