diff --git a/ircd/json.cc b/ircd/json.cc index 620968de7..6da2c1e91 100644 --- a/ircd/json.cc +++ b/ircd/json.cc @@ -41,8 +41,8 @@ namespace qi = spirit::qi; using spirit::unused_type; using qi::lit; -using qi::int_; using qi::char_; +using qi::long_; using qi::double_; using qi::raw; using qi::omit; @@ -54,7 +54,7 @@ using qi::attr; using karma::lit; using karma::char_; -using karma::int_; +using karma::long_; using karma::double_; using karma::bool_; using karma::maxwidth; @@ -68,38 +68,40 @@ struct input { template using rule = qi::rule; + rule<> NUL { lit('\0') ,"nul" }; + // insignificant whitespaces - rule<> SP { lit('\x20') ,"space" }; - rule<> HT { lit('\x09') ,"horizontal tab" }; - rule<> CR { lit('\x0D') ,"carriage return" }; - rule<> LF { lit('\x0A') ,"line feed" }; + rule<> SP { lit('\x20') ,"space" }; + rule<> HT { lit('\x09') ,"horizontal tab" }; + rule<> CR { lit('\x0D') ,"carriage return" }; + rule<> LF { lit('\x0A') ,"line feed" }; // whitespace skipping - rule<> WS { SP | HT | CR | LF ,"whitespace" }; - rule<> ws { *(WS) ,"whitespace monoid" }; - rule<> wsp { +(WS) ,"whitespace semigroup" }; + rule<> WS { SP | HT | CR | LF ,"whitespace" }; + rule<> ws { *(WS) ,"whitespace monoid" }; + rule<> wsp { +(WS) ,"whitespace semigroup" }; // structural - rule<> object_begin { lit('{') ,"object begin" }; - rule<> object_end { lit('}') ,"object end" }; - rule<> array_begin { lit('[') ,"array begin" }; - rule<> array_end { lit(']') ,"array end" }; - rule<> name_sep { lit(':') ,"name sep" }; - rule<> value_sep { lit(',') ,"value sep" }; + rule<> object_begin { lit('{') ,"object begin" }; + rule<> object_end { lit('}') ,"object end" }; + rule<> array_begin { lit('[') ,"array begin" }; + rule<> array_end { lit(']') ,"array end" }; + rule<> name_sep { lit(':') ,"name sep" }; + rule<> value_sep { lit(',') ,"value sep" }; // literal - rule lit_true { lit("true") ,"literal true" }; - rule lit_false { lit("false") ,"literal false" }; - rule lit_null { lit("null") ,"literal null" }; + rule lit_true { lit("true") ,"literal true" }; + rule lit_false { lit("false") ,"literal false" }; + rule lit_null { lit("null") ,"literal null" }; - rule<> quote { lit("\"") ,"quote" }; - rule chars { raw[*(char_ - quote)] ,"characters" }; - rule string { quote >> chars >> quote ,"string" }; - rule name { string ,"name" }; + rule<> quote { lit('"') ,"quote" }; + rule chars { raw[*(char_ - quote)] ,"characters" }; + rule string { quote >> chars >> quote ,"string" }; + rule name { string ,"name" }; - rule boolean { lit_true | lit_false ,"boolean" }; - rule literal { lit_true | lit_false | lit_null ,"literal" }; - rule number { raw[double_] ,"number" }; + rule boolean { lit_true | lit_false ,"boolean" }; + rule literal { lit_true | lit_false | lit_null ,"literal" }; + rule number { raw[double_] ,"number" }; rule array { @@ -178,19 +180,20 @@ struct output rule boolean { lit_true | lit_false ,"boolean" }; rule literal { lit_true | lit_false | lit_null ,"literal" }; - rule chars { *(~char_("\"")) ,"characters" }; + rule chars { *(~char_('"')) ,"characters" }; rule string { quote << chars << quote ,"string" }; rule number { double_ ,"number" }; rule name { string ,"name" }; - rule value { rule{} ,"value" }; + rule value { rule{} /* subclass implemented */ ,"value" }; + rule member { name << name_sep << value ,"member" }; - rule elems { (value % value_sep) ,"elements" }; + rule elem { value ,"element" }; + rule elems { -(value % value_sep) ,"elements" }; rule array { array_begin << elems << array_end ,"array" }; - rule member { name << name_sep << value ,"member" }; - rule members { (member % value_sep) ,"members" }; + rule members { -(member % value_sep) ,"members" }; rule document { object_begin << members << object_end ,"document" }; output() @@ -228,6 +231,8 @@ const ostreamer; size_t print(char *const &buf, const size_t &max, const arr &); size_t print(char *const &buf, const size_t &max, const doc &); size_t print(char *const &buf, const size_t &max, const obj &); +arr serialize(const arr &, char *&start, char *const &stop); +doc serialize(const doc &, char *&start, char *const &stop); doc serialize(const obj &, char *&start, char *const &stop); std::ostream &operator<<(std::ostream &, const arr &); @@ -245,15 +250,15 @@ ircd::json::printer::printer() const auto recurse_array([&] { char *out(const_cast(a.data())); - karma::generate(out, maxwidth(a.size())[array], json::arr(a)); - a.resize(size_t(out - a.data())); + const arr r(serialize(json::arr(a), out, out + a.size())); + a.resize(size_t(out - r.data())); }); const auto recurse_document([&] { char *out(const_cast(a.data())); - karma::generate(out, maxwidth(a.size())[document], json::doc(a)); - a.resize(size_t(out - a.data())); + const doc d(serialize(json::doc(a), out, out + a.size())); + a.resize(size_t(out - d.data())); }); const auto quote_string([&] @@ -299,15 +304,15 @@ ircd::json::ostreamer::ostreamer() const auto recurse_array([&] { char *out(const_cast(a.data())); - const auto count(print(out, a.size() + 1, json::arr(a))); - a.resize(count); + const arr r(serialize(json::arr(a), out, out + a.size())); + a.resize(size_t(out - r.data())); }); const auto recurse_document([&] { char *out(const_cast(a.data())); - const auto count(print(out, a.size() + 1, json::doc(a))); - a.resize(count); + const doc d(serialize(json::doc(a), out, out + a.size())); + a.resize(size_t(out - d.data())); }); const auto quote_string([&] @@ -372,14 +377,19 @@ ircd::json::serialize(const obj &obj, const auto print_string([&stop, &out](const val &val) { - karma::generate(out, maxwidth(stop - out)[printer.string] | eps[throws], val); + karma::generate(out, maxwidth(stop - out)[printer.string] | eps[throws], string_view{val}); + }); + + const auto print_literal([&stop, &out](const val &val) + { + karma::generate(out, maxwidth(stop - out)[karma::string] | eps[throws], string_view{val}); }); const auto print_object([&stop, &out](const val &val) { if(val.serial) { - karma::generate(out, maxwidth(stop - out)[printer.document] | eps[throws], val); + karma::generate(out, maxwidth(stop - out)[printer.document] | eps[throws], string_view{val}); return; } @@ -391,14 +401,33 @@ ircd::json::serialize(const obj &obj, { if(val.serial) { - karma::generate(out, maxwidth(stop - out)[printer.array] | eps[throws], val); + karma::generate(out, maxwidth(stop - out)[printer.array] | eps[throws], string_view{val}); return; } + assert(0); //assert(val.object); //serialize(*val.object, out, stop); }); + const auto print_number([&stop, &out](const val &val) + { + if(val.serial) + { + if(val.floats) + karma::generate(out, maxwidth(stop - out)[double_] | eps[throws], string_view{val}); + else + karma::generate(out, maxwidth(stop - out)[long_] | eps[throws], string_view{val}); + + return; + } + + if(val.floats) + karma::generate(out, maxwidth(stop - out)[double_] | eps[throws], val.floating); + else + karma::generate(out, maxwidth(stop - out)[long_] | eps[throws], val.integer); + }); + const auto print_member([&](const obj::member &member) { const auto generate_name @@ -413,8 +442,8 @@ ircd::json::serialize(const obj &obj, case STRING: print_string(member.second); break; case OBJECT: print_object(member.second); break; case ARRAY: print_array(member.second); break; - default: - throw type_error("Cannot stream unsupported member type"); + case NUMBER: print_number(member.second); break; + case LITERAL: print_literal(member.second); break; } }); @@ -448,14 +477,19 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) const auto stream_string([&osi](const val &val) { - karma::generate(osi, ostreamer.string, val); + karma::generate(osi, ostreamer.string, string_view{val}); + }); + + const auto stream_literal([&osi](const val &val) + { + karma::generate(osi, karma::string, string_view{val}); }); const auto stream_object([&osi, &s](const val &val) { if(val.serial) { - karma::generate(osi, ostreamer.document, val); + karma::generate(osi, ostreamer.document, string_view{val}); return; } @@ -463,11 +497,11 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) s << *val.object; }); - const auto stream_array([&osi, &s](const val &val) + const auto stream_array([&osi](const val &val) { if(val.serial) { - karma::generate(osi, ostreamer.array, val); + karma::generate(osi, ostreamer.array, string_view{val}); return; } @@ -476,17 +510,35 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) //s << *val.object; }); + const auto stream_number([&osi](const val &val) + { + if(val.serial) + { + if(val.floats) + karma::generate(osi, double_, string_view{val}); + else + karma::generate(osi, long_, string_view{val}); + + return; + } + + if(val.floats) + karma::generate(osi, double_, val.floating); + else + karma::generate(osi, long_, val.integer); + }); + const auto stream_member([&](const obj::member &member) { karma::generate(osi, ostreamer.name << ostreamer.name_sep, string_view(member.first)); switch(member.second.type) { - case STRING: stream_string(member.second); break; - case OBJECT: stream_object(member.second); break; - case ARRAY: stream_array(member.second); break; - default: - throw type_error("cannot stream unsupported member type"); + case STRING: stream_string(member.second); break; + case OBJECT: stream_object(member.second); break; + case ARRAY: stream_array(member.second); break; + case NUMBER: stream_number(member.second); break; + case LITERAL: stream_literal(member.second); break; } }); @@ -507,24 +559,45 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) return s; } -ircd::json::obj::obj(std::initializer_list builder) +ircd::json::obj::obj(const doc &doc, + const bool &recurse) +:idx{doc.count()} { - std::transform(std::begin(builder), std::end(builder), std::back_inserter(idx), [] + const auto non_recursive_transform([] + (const doc::member &m) -> obj::member + { + return obj::member{m}; + }); + + const auto recursive_transform([&non_recursive_transform] + (const doc::member &m) -> obj::member + { + switch(type(m.second)) + { + case OBJECT: + return obj::member{m.first, std::make_unique(m.second)}; + + default: + return non_recursive_transform(m); + }; + }); + + if(recurse) + std::transform(std::begin(doc), std::end(doc), std::begin(idx), recursive_transform); + else + std::transform(std::begin(doc), std::end(doc), std::begin(idx), non_recursive_transform); +} + +ircd::json::obj::obj(std::initializer_list builder) +:idx{builder.size()} +{ + std::transform(std::begin(builder), std::end(builder), std::begin(idx), [] (auto&& m) { return std::move(const_cast(m)); }); } -ircd::json::obj::obj(const doc &doc) -{ - std::transform(std::begin(doc), std::end(doc), std::back_inserter(idx), [] - (const doc::member &m) -> obj::member - { - return { val { m.first }, val { m.second, type(m.second), true } }; - }); -} - bool ircd::json::obj::erase(const string_view &name) { @@ -549,6 +622,48 @@ ircd::json::obj::erase(const const_iterator &start, return { idx.erase(start, stop) }; } +const ircd::json::val & +ircd::json::obj::at(const string_view &path) +const +{ + const auto elem(split(path, '.')); + const auto ident(split(elem.first, '[')); + const auto &name(ident.first); + const auto &indice(ident.second); + + const auto it(find(name)); + if(unlikely(it == end())) + throw not_found("'%s'", name); + + const auto &val(it->second); + if(!indice.empty()) + { + const auto idx(lex_cast(chomp(indice, "]"))); + + if(type(val) != ARRAY) + throw not_found("cannot recurse through non-array \"%s\" for indice [%zu]", name, idx); + + if(val.serial) + throw not_found("cannot recurse through unparsed array \"%s\" for indice [%zu]", name, idx); + + assert(0); + //return val.array->at(idx); + } + else if(!elem.second.empty()) + { + if(type(val) != OBJECT) + throw not_found("cannot recurse through non-object \"%s\" for \"%s\"", name, elem.second); + + if(val.serial) + throw not_found("cannot recurse through unparsed object \"%s\" for \"%s\"", name, elem.second); + + return val.object->at(elem.second); + } + + return it->second; +} + + size_t ircd::json::obj::size() const @@ -569,36 +684,6 @@ const return ret; } -std::ostream & -ircd::json::operator<<(std::ostream &s, const val &v) -{ - switch(v.type) - { - case STRING: - s << string_view(v); - break; - - case OBJECT: - if(v.serial) - s << string_view(v); - else - s << *v.object; - break; - - case ARRAY: - if(v.serial) - s << string_view(v); - else - assert(0); - break; - - default: - throw type_error("cannot stream value type[%d]", int(v.type)); - } - - return s; -} - inline ircd::json::val::~val() noexcept @@ -618,6 +703,7 @@ const switch(type) { case STRING: + case LITERAL: return std::string(unquote(string_view(*this))); case OBJECT: @@ -632,8 +718,13 @@ const else break; - default: - break; + case NUMBER: + if(serial) + return std::string(string_view(*this)); + else if(floats) + return std::string(lex_cast(floating)); + else + return std::string(lex_cast(integer)); } throw type_error("cannot stringify type[%d]", int(type)); @@ -649,13 +740,12 @@ const case ARRAY: case OBJECT: + case NUMBER: + case LITERAL: if(serial) return string_view{string, len}; else break; - - default: - break; } throw type_error("value type[%d] is not a string", int(type)); @@ -667,16 +757,107 @@ const { switch(type) { - case NUMBER: return lex_cast(integer).size(); - case STRING: return 1 + len + 1; - case OBJECT: return serial? len : object->size(); - case ARRAY: return serial? len : 2; - default: break; + case NUMBER: return lex_cast(integer).size(); + case STRING: return 1 + len + 1; + case OBJECT: return serial? len : object->size(); + case ARRAY: return serial? len : 2; + case LITERAL: return len; }; throw type_error("cannot size type[%u]", int(type)); } +std::ostream & +ircd::json::operator<<(std::ostream &s, const val &v) +{ + switch(v.type) + { + case STRING: + case LITERAL: + s << string_view(v); + break; + + case OBJECT: + if(v.serial) + s << string_view(v); + else + s << *v.object; + break; + + case ARRAY: + if(v.serial) + s << string_view(v); + else + assert(0); + break; + + case NUMBER: + if(v.serial) + s << string_view(v); + else if(v.floats) + s << v.floating; + else + s << v.integer; + break; + } + + return s; +} + +inline bool +ircd::json::operator>(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) > static_cast(b); +} + +inline bool +ircd::json::operator<(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) < static_cast(b); +} + +inline bool +ircd::json::operator>=(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) >= static_cast(b); +} + +inline bool +ircd::json::operator<=(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) <= static_cast(b); +} + +inline bool +ircd::json::operator!=(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) != static_cast(b); +} + +inline bool +ircd::json::operator==(const val &a, const val &b) +{ + if(unlikely(type(a) != STRING || type(b) != STRING)) + throw type_error("cannot compare values"); + + return static_cast(a) == static_cast(b); +} + size_t ircd::json::print(char *const &buf, const size_t &max, @@ -754,6 +935,15 @@ ircd::json::doc::const_iterator::operator++() return *this; } +ircd::json::doc::operator std::string() +const +{ + //TODO: tmp + std::stringstream ret; + ret << (*this); + return ret.str(); +} + ircd::json::doc::const_iterator ircd::json::doc::begin() const @@ -806,18 +996,23 @@ ircd::json::serialize(const arr &a, throw print_error("The JSON generator failed to print array"); }); - const karma::rule grammar - { - printer.array_begin << (printer.value % printer.value_sep) << printer.array_end - }; - char *const start(out); - karma::generate(out, maxwidth(stop - start)[grammar] | eps[throws], a); + karma::generate(out, maxwidth(stop - out)[printer.array_begin] | eps[throws]); + + auto it(begin(a)); + if(it != end(a)) + { + karma::generate(out, maxwidth(stop - out)[printer.elem] | eps[throws], *it); + for(++it; it != end(a); ++it) + karma::generate(out, maxwidth(stop - out)[printer.value_sep << printer.elem] | eps[throws], *it); + } + + karma::generate(out, maxwidth(stop - out)[printer.array_end] | eps[throws]); return string_view{start, out}; } std::ostream & -ircd::json::operator<<(std::ostream &s, const arr &arr) +ircd::json::operator<<(std::ostream &s, const arr &a) { const auto &os(ostreamer); static const auto throws([] @@ -826,7 +1021,17 @@ ircd::json::operator<<(std::ostream &s, const arr &arr) }); karma::ostream_iterator osi(s); - karma::generate(osi, os.array | eps[throws], arr); + karma::generate(osi, ostreamer.array_begin | eps[throws]); + + auto it(begin(a)); + if(it != end(a)) + { + karma::generate(osi, ostreamer.elem | eps[throws], *it); + for(++it; it != end(a); ++it) + karma::generate(osi, (ostreamer.value_sep << ostreamer.elem) | eps[throws], *it); + } + + karma::generate(osi, ostreamer.array_end | eps[throws]); return s; } @@ -845,6 +1050,15 @@ ircd::json::arr::const_iterator::operator++() return *this; } +ircd::json::arr::operator std::string() +const +{ + //TODO: tmp + std::stringstream ret; + ret << (*this); + return ret.str(); +} + ircd::json::arr::const_iterator ircd::json::arr::begin() const @@ -879,3 +1093,16 @@ ircd::json::type(const string_view &buf) return ret; } + +enum ircd::json::type +ircd::json::type(const string_view &buf, + std::nothrow_t) +{ + static const auto flag(qi::skip_flag::dont_postskip); + + enum type ret; + if(!qi::phrase_parse(begin(buf), end(buf), parser.type, parser.WS, flag, ret)) + return STRING; + + return ret; +}