diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 0b96dcae..bf88f0ff 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -3,7 +3,6 @@ // -#pragma once #ifndef SIMPLEJSON_HPP #define SIMPLEJSON_HPP @@ -19,7 +18,9 @@ #include #include #include +#include #include "../chaiscript_defines.hpp" +#include "quick_flat_map.hpp" namespace json { @@ -37,7 +38,7 @@ class JSON { public: enum class Class { - Null, + Null = 0, Object, Array, String, @@ -48,155 +49,98 @@ class JSON private: - struct QuickFlatMap + + using Data = std::variant, std::vector, std::string, double, int, bool>; + + struct Internal { - auto find(const std::string &s) noexcept { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + Internal(nullptr_t) : d(nullptr) { } + Internal() : d(nullptr) { } + Internal(Class c) : d(make_type(c)) { } + template Internal(T t) : d(std::move(t)) { } + + static Data make_type(Class c) { + switch (c) { + case Class::Null: return nullptr; + case Class::Object: return chaiscript::utility::QuickFlatMap{}; + case Class::Array: return std::vector{}; + case Class::String: return std::string{}; + case Class::Floating: return double{}; + case Class::Integral: return int{}; + case Class::Boolean: return bool{}; + } + throw std::runtime_error("unknown type"); } - auto find(const std::string &s) const noexcept { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); - } - - auto size() const noexcept { - return data.size(); - } - - auto begin() const noexcept { - return data.begin(); - } - - auto end() const noexcept { - return data.end(); - } - - - auto begin() noexcept { - return data.begin(); - } - - auto end() noexcept { - return data.end(); - } - - - JSON &operator[](const std::string &s) { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; - } else { - data.emplace_back(s, JSON()); - return data.back().second; + void set_type(Class c) { + if (type() != c) { + d = make_type(c); } } - JSON &at(const std::string &s) { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; + Class type() const noexcept { + return Class(d.index()); + } + + + template + decltype(auto) visit_or(Visitor &&visitor, Or &&other) const + { + if (type() == Class(ClassValue)) { + return visitor(std::get(ClassValue)>(d)); } else { - throw std::out_of_range("Unknown key: " + s); + return other(); } } - const JSON &at(const std::string &s) const { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; - } else { - throw std::out_of_range("Unknown key: " + s); - } + template + auto &get_set_type() { + set_type(ClassValue); + return std::get(ClassValue)>(d); } - size_t count(const std::string &s) const noexcept { - return (find(s) != data.end())?1:0; + auto &Map() { + return get_set_type(); + } + auto &Vector() { + return get_set_type(); + } + auto &String() { + return get_set_type(); + } + auto &Int() { + return get_set_type(); + } + auto &Float() { + return get_set_type(); + } + auto &Bool() { + return get_set_type(); } - std::vector> data; - - using iterator = decltype(data)::iterator; - using const_iterator = decltype(data)::const_iterator; + auto Map() const noexcept { + return std::get_if(Class::Object)>(&d); + } + auto Vector() const noexcept { + return std::get_if(Class::Array)>(&d); + } + auto String() const noexcept { + return std::get_if(Class::String)>(&d); + } + auto Int() const noexcept { + return std::get_if(Class::Integral)>(&d); + } + auto Float() const noexcept { + return std::get_if(Class::Floating)>(&d); + } + auto Bool() const noexcept { + return std::get_if(Class::Boolean)>(&d); + } + Data d; }; - struct Internal { - template - auto clone(const std::unique_ptr &ptr) { - if (ptr != nullptr) { - return std::make_unique(*ptr); - } else { - return std::unique_ptr(nullptr); - } - } - - Internal( double d ) : Float( d ), Type(Class::Floating) {} - Internal( long l ) : Int( l ), Type(Class::Integral) {} - Internal( bool b ) : Bool( b ), Type(Class::Boolean) {} - Internal( std::string s ) : String(std::make_unique(std::move(s))), Type(Class::String) {} - Internal() : Type(Class::Null) {} - - Internal(Class t_type) { - set_type(t_type); - } - - Internal(const Internal &other) - : List(clone(other.List)), - Map(clone(other.Map)), - String(clone(other.String)), - Float(other.Float), - Int(other.Int), - Bool(other.Bool), - Type(other.Type) - { - } - - Internal &operator=(const Internal &other) - { - List = clone(other.List); - Map = clone(other.Map); - String = clone(other.String); - Float = other.Float; - Int = other.Int; - Bool = other.Bool; - Type = other.Type; - return *this; - } - - void set_type( Class type ) { - if( type == Type ) { - return; - } - - Map.reset(); - List.reset(); - String.reset(); - - switch( type ) { - case Class::Object: Map = std::make_unique(); break; - case Class::Array: List = std::make_unique>(); break; - case Class::String: String = std::make_unique(); break; - case Class::Floating: Float = 0.0; break; - case Class::Integral: Int = 0; break; - case Class::Boolean: Bool = false; break; - case Class::Null: break; - } - - Type = type; - } - - Internal(Internal &&) = default; - Internal &operator=(Internal &&) = default; - - std::unique_ptr> List; - std::unique_ptr Map; - std::unique_ptr String; - double Float = 0; - long Int = 0; - bool Bool = false; - - Class Type = Class::Null; - }; Internal internal; @@ -248,7 +192,7 @@ class JSON explicit JSON( T b, typename enable_if::value>::type* = nullptr ) noexcept : internal( static_cast(b) ) {} template - explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) noexcept : internal( static_cast(i) ) {} + explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) noexcept : internal( static_cast(i) ) {} template explicit JSON( T f, typename enable_if::value>::type* = nullptr ) noexcept : internal( static_cast(f) ) {} @@ -261,17 +205,16 @@ class JSON static JSON Load( const std::string & ); JSON& operator[]( const std::string &key ) { - internal.set_type( Class::Object ); - return internal.Map->operator[]( key ); + return internal.Map().operator[]( key ); } JSON& operator[]( const size_t index ) { - internal.set_type( Class::Array ); - if( index >= internal.List->size() ) { - internal.List->resize( index + 1 ); + auto &vec = internal.Vector(); + if( index >= vec.size() ) { + vec.resize( index + 1 ); } - return internal.List->operator[]( index ); + return vec.operator[]( index ); } @@ -280,7 +223,10 @@ class JSON } const JSON &at( const std::string &key ) const { - return internal.Map->at( key ); + return internal.visit_or( + [&](const auto &m)->const JSON &{ return m.at(key); }, + []()->const JSON &{ throw std::range_error("Not an object, no keys"); } + ); } JSON &at( size_t index ) { @@ -288,100 +234,84 @@ class JSON } const JSON &at( size_t index ) const { - return internal.List->at( index ); + return internal.visit_or( + [&](const auto &m)->const JSON&{ return m.at(index); }, + []()->const JSON &{ throw std::range_error("Not an array, no indexes"); } + ); } - - long length() const noexcept { - if( internal.Type == Class::Array ) { - return static_cast(internal.List->size()); - } else { - return -1; - } + auto length() const noexcept { + return internal.visit_or( + [&](const auto &m){ return static_cast(m.size()); }, + [](){ return -1; } + ); } bool has_key( const std::string &key ) const noexcept { - if( internal.Type == Class::Object ) { - return internal.Map->count(key) != 0; - } - - return false; + return internal.visit_or( + [&](const auto &m){ return m.count(key) != 0; }, + [](){ return false; } + ); } int size() const noexcept { - if( internal.Type == Class::Object ) { - return static_cast(internal.Map->size()); - } else if( internal.Type == Class::Array ) { - return static_cast(internal.List->size()); + if (auto m = internal.Map(); m != nullptr) { + return static_cast(m->size()); + } if (auto v = internal.Vector(); v != nullptr) { + return static_cast(v->size()); } else { return -1; } } - Class JSONType() const noexcept { return internal.Type; } + Class JSONType() const noexcept { return internal.type(); } /// Functions for getting primitives from the JSON object. - bool is_null() const noexcept { return internal.Type == Class::Null; } + bool is_null() const noexcept { return internal.type() == Class::Null; } - std::string to_string() const { bool b; return to_string( b ); } - std::string to_string( bool &ok ) const { - ok = (internal.Type == Class::String); - return ok ? *internal.String : std::string(""); + std::string to_string() const noexcept { + return internal.visit_or( + [](const auto &o){ return o; }, + [](){ return std::string{}; } + ); + } + double to_float() const noexcept { + return internal.visit_or( + [](const auto &o){ return o; }, + [](){ return double{}; } + ); + } + int to_int() const noexcept { + return internal.visit_or( + [](const auto &o){ return o; }, + [](){ return int{}; } + ); + } + bool to_bool() const noexcept { + return internal.visit_or( + [](const auto &o){ return o; }, + [](){ return false; } + ); } - double to_float() const noexcept { bool b; return to_float( b ); } - double to_float( bool &ok ) const noexcept { - ok = (internal.Type == Class::Floating); - return ok ? internal.Float : 0.0; - } - - long to_int() const noexcept { bool b; return to_int( b ); } - long to_int( bool &ok ) const noexcept { - ok = (internal.Type == Class::Integral); - return ok ? internal.Int : 0; - } - - bool to_bool() const noexcept { bool b; return to_bool( b ); } - bool to_bool( bool &ok ) const noexcept { - ok = (internal.Type == Class::Boolean); - return ok ? internal.Bool : false; - } - - JSONWrapper object_range() { - if( internal.Type == Class::Object ) { - return JSONWrapper( internal.Map.get() ); - } else { - return JSONWrapper( nullptr ); - } + JSONWrapper> object_range() { + return std::get_if(Class::Object)>(&internal.d); } JSONWrapper> array_range() { - if( internal.Type == Class::Array ) { - return JSONWrapper>( internal.List.get() ); - } else { - return JSONWrapper>( nullptr ); - } + return std::get_if(Class::Array)>(&internal.d); } - JSONConstWrapper object_range() const { - if( internal.Type == Class::Object ) { - return JSONConstWrapper( internal.Map.get() ); - } else { - return JSONConstWrapper( nullptr ); - } + JSONConstWrapper> object_range() const { + return std::get_if(Class::Object)>(&internal.d); } - JSONConstWrapper> array_range() const { - if( internal.Type == Class::Array ) { - return JSONConstWrapper>( internal.List.get() ); - } else { - return JSONConstWrapper>( nullptr ); - } + return std::get_if(Class::Array)>(&internal.d); } std::string dump( long depth = 1, std::string tab = " ") const { - switch( internal.Type ) { + switch( internal.type() ) { case Class::Null: return "null"; case Class::Object: { @@ -390,7 +320,7 @@ class JSON std::string s = "{\n"; bool skip = true; - for( auto &p : *internal.Map ) { + for( auto &p : *internal.Map() ) { if( !skip ) { s += ",\n"; } s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); skip = false; @@ -401,7 +331,7 @@ class JSON case Class::Array: { std::string s = "["; bool skip = true; - for( auto &p : *internal.List ) { + for( auto &p : *internal.Vector() ) { if( !skip ) { s += ", "; } s += p.dump( depth + 1, tab ); skip = false; @@ -410,13 +340,13 @@ class JSON return s; } case Class::String: - return "\"" + json_escape( *internal.String ) + "\""; + return "\"" + json_escape( *internal.String() ) + "\""; case Class::Floating: - return std::to_string( internal.Float ); + return std::to_string( *internal.Float() ); case Class::Integral: - return std::to_string( internal.Int ); + return std::to_string( *internal.Int() ); case Class::Boolean: - return internal.Bool ? "true" : "false"; + return *internal.Bool() ? "true" : "false"; } throw std::runtime_error("Unhandled JSON type"); diff --git a/include/chaiscript/utility/quick_flat_map.hpp b/include/chaiscript/utility/quick_flat_map.hpp new file mode 100644 index 00000000..1d3be2bb --- /dev/null +++ b/include/chaiscript/utility/quick_flat_map.hpp @@ -0,0 +1,81 @@ +#ifndef CHAISCRIPT_UTILITY_QUICK_FLAT_MAP_HPP +#define CHAISCRIPT_UTILITY_QUICK_FLAT_MAP_HPP + +namespace chaiscript::utility { + + template + struct QuickFlatMap + { + auto find(const Key &s) noexcept { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto find(const Key &s) const noexcept { + return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); + } + + auto size() const noexcept { + return data.size(); + } + + auto begin() const noexcept { + return data.begin(); + } + + auto end() const noexcept { + return data.end(); + } + + + auto begin() noexcept { + return data.begin(); + } + + auto end() noexcept { + return data.end(); + } + + + Value &operator[](const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + return data.emplace_back(s, Value()).second; + } + } + + Value &at(const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + const Value &at(const Key &s) const { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + size_t count(const Key &s) const noexcept { + return (find(s) != data.end())?1:0; + } + + std::vector> data; + + using iterator = typename decltype(data)::iterator; + using const_iterator = typename decltype(data)::const_iterator; + + + }; + +} + +#endif +