//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // // Category : // Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h // Created by : Steinberg, 12/2021 // Description : // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #include "json.h" #include #include #include #include #include #include #if defined(_MSC_VER) || __has_include() #include #define SMTG_HAS_CHARCONV #endif //------------------------------------------------------------------------ namespace JSON { namespace Detail { //------------------------------------------------------------------------ template struct Base { explicit Base (JsonT* o) : object_ (o) {} explicit Base (const Base& o) : object_ (o.object_) {} Base& operator= (const Base& o) = default; operator JsonT* () const { return object_; } JsonT* jsonValue () const { return object_; } protected: Base () : object_ (nullptr) {} JsonT* object_; }; //------------------------------------------------------------------------ template struct Iterator { explicit Iterator (JsonElement el) : el (el) {} bool operator== (const Iterator& other) const { return other.el == el; } bool operator!= (const Iterator& other) const { return other.el != el; } const JsonElement& operator* () const { return el; } const JsonElement& operator-> () const { return el; } Iterator& operator++ () { if (el) el = el.next (); return *this; } Iterator operator++ (int) { auto it = Iterator (el); operator++ (); return it; } private: JsonElement el; }; //------------------------------------------------------------------------ } // Detail struct Object; struct Array; struct String; struct Number; struct Boolean; //------------------------------------------------------------------------ enum class Type { Object, Array, String, Number, True, False, Null, }; //------------------------------------------------------------------------ struct SourceLocation { size_t offset; size_t line; size_t row; }; //------------------------------------------------------------------------ struct Value : Detail::Base { using Detail::Base::Base; using VariantT = std::variant; std::optional asObject () const; std::optional asArray () const; std::optional asString () const; std::optional asNumber () const; std::optional asBoolean () const; std::optional asNull () const; VariantT asVariant () const; Type type () const; SourceLocation getSourceLocation () const; }; //------------------------------------------------------------------------ struct Boolean { Boolean (size_t type) : value (type == json_type_true) {} operator bool () const { return value; } private: bool value; }; //------------------------------------------------------------------------ struct String : Detail::Base { using Detail::Base::Base; std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; } SourceLocation getSourceLocation () const; }; //------------------------------------------------------------------------ struct Number : Detail::Base { using Detail::Base::Base; std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; } std::optional getInteger () const; std::optional getDouble () const; }; //------------------------------------------------------------------------ struct ObjectElement : Detail::Base { using Detail::Base::Base; String name () const { return String (jsonValue ()->name); } Value value () const { return Value (jsonValue ()->value); } ObjectElement next () const { return ObjectElement (jsonValue ()->next); } }; //------------------------------------------------------------------------ struct Object : Detail::Base { using Detail::Base::Base; using Iterator = Detail::Iterator; size_t size () const { return jsonValue ()->length; } Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); } Iterator end () const { return Iterator (ObjectElement (nullptr)); } }; //------------------------------------------------------------------------ struct ArrayElement : Detail::Base { using Detail::Base::Base; Value value () const { return Value (jsonValue ()->value); } ArrayElement next () const { return ArrayElement (jsonValue ()->next); } }; //------------------------------------------------------------------------ struct Array : Detail::Base { using Detail::Base::Base; using Iterator = Detail::Iterator; size_t size () const { return jsonValue ()->length; } Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); } Iterator end () const { return Iterator (ArrayElement (nullptr)); } }; //------------------------------------------------------------------------ struct Document : Value { static std::variant parse (std::string_view data) { auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); }; json_parse_result_s parse_result {}; auto value = json_parse_ex (data.data (), data.size (), json_parse_flags_allow_json5 | json_parse_flags_allow_location_information, allocate, nullptr, &parse_result); if (value) return Document (value); return parse_result; } ~Document () noexcept { if (object_) std::free (object_); } Document (Document&& doc) noexcept { *this = std::move (doc); } Document& operator= (Document&& doc) noexcept { std::swap (object_, doc.object_); return *this; } private: using Value::Value; }; //------------------------------------------------------------------------ inline std::optional Value::asObject () const { if (type () != Type::Object) return {}; return Object (json_value_as_object (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asArray () const { if (type () != Type::Array) return {}; return Array (json_value_as_array (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asString () const { if (type () != Type::String) return {}; return String (json_value_as_string (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asNumber () const { if (type () != Type::Number) return {}; return Number (json_value_as_number (jsonValue ())); } //------------------------------------------------------------------------ inline std::optional Value::asBoolean () const { if (type () == Type::True || type () == Type::False) return Boolean (jsonValue ()->type); return {}; } //------------------------------------------------------------------------ inline std::optional Value::asNull () const { if (type () != Type::Null) return {}; return nullptr; } //------------------------------------------------------------------------ inline Type Value::type () const { switch (jsonValue ()->type) { case json_type_string: return Type::String; case json_type_number: return Type::Number; case json_type_object: return Type::Object; case json_type_array: return Type::Array; case json_type_true: return Type::True; case json_type_false: return Type::False; case json_type_null: return Type::Null; } assert (false); return Type::Null; } //------------------------------------------------------------------------ inline Value::VariantT Value::asVariant () const { switch (type ()) { case Type::String: return *asString (); case Type::Number: return *asNumber (); case Type::Object: return *asObject (); case Type::Array: return *asArray (); case Type::True: return *asBoolean (); case Type::False: return *asBoolean (); case Type::Null: return *asNull (); } assert (false); return nullptr; } //------------------------------------------------------------------------ inline SourceLocation Value::getSourceLocation () const { auto exValue = reinterpret_cast (jsonValue ()); return {exValue->offset, exValue->line_no, exValue->row_no}; } //------------------------------------------------------------------------ inline SourceLocation String::getSourceLocation () const { auto exValue = reinterpret_cast (jsonValue ()); return {exValue->offset, exValue->line_no, exValue->row_no}; } //------------------------------------------------------------------------ inline std::optional Number::getInteger () const { #if defined(SMTG_HAS_CHARCONV) int64_t result {0}; auto res = std::from_chars (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size, result); if (res.ec == std::errc ()) return result; return {}; #else int64_t result {0}; std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size); if (std::sscanf (str.data (), "%lld", &result) != 1) return {}; return result; #endif } //------------------------------------------------------------------------ inline std::optional Number::getDouble () const { #if 1 // clang still has no floting point from_chars version size_t ctrl {0}; auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl); if (ctrl > 0) return result; #else double result {0.}; auto res = std::from_chars (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size, result); if (res.ec == std::errc ()) return result; #endif return {}; } //------------------------------------------------------------------------ inline std::string_view errorToString (json_parse_error_e error) { switch (error) { case json_parse_error_e::json_parse_error_none: return {}; case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket: return "json_parse_error_expected_comma_or_closing_bracket"; case json_parse_error_e::json_parse_error_expected_colon: return "json_parse_error_expected_colon"; case json_parse_error_e::json_parse_error_expected_opening_quote: return "json_parse_error_expected_opening_quote"; case json_parse_error_e::json_parse_error_invalid_string_escape_sequence: return "json_parse_error_invalid_string_escape_sequence"; case json_parse_error_e::json_parse_error_invalid_number_format: return "json_parse_error_invalid_number_format"; case json_parse_error_e::json_parse_error_invalid_value: return "json_parse_error_invalid_value"; case json_parse_error_e::json_parse_error_premature_end_of_buffer: return "json_parse_error_premature_end_of_buffer"; case json_parse_error_e::json_parse_error_invalid_string: return "json_parse_error_invalid_string"; case json_parse_error_e::json_parse_error_allocator_failed: return "json_parse_error_allocator_failed"; case json_parse_error_e::json_parse_error_unexpected_trailing_characters: return "json_parse_error_unexpected_trailing_characters"; case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown"; } return {}; } //------------------------------------------------------------------------ } // JSON