From 710d959a63b5d3e0e9755fed530318fbf01ea1ec Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Fri, 17 Mar 2017 20:32:32 -0700 Subject: [PATCH] ircd::json: Cleanup/improve grammar; improve linear array parse (incomplete). --- include/ircd/json.h | 4 +- include/ircd/json/arr.h | 169 +++++++++++++++ include/ircd/json/array.h | 99 --------- include/ircd/json/doc.h | 2 +- include/ircd/json/obj.h | 2 +- include/ircd/json/val.h | 23 +- ircd/json.cc | 430 +++++++++++++++++++++----------------- 7 files changed, 435 insertions(+), 294 deletions(-) create mode 100644 include/ircd/json/arr.h delete mode 100644 include/ircd/json/array.h diff --git a/include/ircd/json.h b/include/ircd/json.h index 616e8406b..d289c93d2 100644 --- a/include/ircd/json.h +++ b/include/ircd/json.h @@ -32,7 +32,7 @@ IRCD_EXCEPTION(error, type_error); IRCD_EXCEPTION(error, not_found); struct doc; -struct array; +struct arr; struct val; struct obj; @@ -51,7 +51,7 @@ type type(const string_view &); } // namespace json } // namespace ircd -#include "json/array.h" +#include "json/arr.h" #include "json/doc.h" #include "json/val.h" #include "json/obj.h" diff --git a/include/ircd/json/arr.h b/include/ircd/json/arr.h new file mode 100644 index 000000000..f83fdda6f --- /dev/null +++ b/include/ircd/json/arr.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2017 Charybdis Development Team + * Copyright (C) 2017 Jason Volk + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once +#define HAVE_IRCD_JSON_ARR_H + +namespace ircd { +namespace json { + +struct arr +:string_view +{ + struct const_iterator; + + using value_type = const string_view; + using pointer = value_type *; + using reference = value_type &; + using iterator = const_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + bool contains(const string_view &) const; + + const_iterator end() const; + const_iterator begin() const; + + size_t count() const; + + const_iterator find(size_t i) const; + string_view at(size_t i) const; + string_view operator[](size_t i) const; + + using string_view::string_view; + + friend arr serialize(const arr &, char *&buf, char *const &stop); + friend size_t print(char *const &buf, const size_t &max, const arr &); + friend std::ostream &operator<<(std::ostream &, const arr &); +}; + +struct arr::const_iterator +{ + using value_type = const string_view; + using pointer = value_type *; + using reference = value_type &; + using difference_type = size_t; + using iterator_category = std::forward_iterator_tag; + + protected: + friend class arr; + + const char *start; + const char *stop; + string_view state; + + const_iterator(const char *const &start, const char *const &stop) + :start{start} + ,stop{stop} + {} + + public: + value_type *operator->() const { return &state; } + value_type &operator*() const { return *operator->(); } + + const_iterator &operator++(); + + friend bool operator==(const arr::const_iterator &, const arr::const_iterator &); + friend bool operator!=(const arr::const_iterator &, const arr::const_iterator &); + friend bool operator<=(const arr::const_iterator &, const arr::const_iterator &); + friend bool operator>=(const arr::const_iterator &, const arr::const_iterator &); + friend bool operator<(const arr::const_iterator &, const arr::const_iterator &); + friend bool operator>(const arr::const_iterator &, const arr::const_iterator &); +}; + +inline bool +operator==(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start == b.start; +} + +inline bool +operator!=(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start != b.start; +} + +inline bool +operator<=(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start <= b.start; +} + +inline bool +operator>=(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start >= b.start; +} + +inline bool +operator<(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start < b.start; +} + +inline bool +operator>(const arr::const_iterator &a, const arr::const_iterator &b) +{ + return a.start > b.start; +} + +} // namespace json +} // namespace ircd + +inline bool +ircd::json::arr::contains(const string_view &s) +const +{ + return s.begin() >= this->string_view::begin() && + s.end() <= this->string_view::end(); +} + +inline ircd::string_view +ircd::json::arr::operator[](size_t i) +const +{ + const auto it(find(i)); + return it != end()? *it : string_view{}; +} + +inline ircd::string_view +ircd::json::arr::at(size_t i) +const +{ + const auto it(find(i)); + return likely(it != end())? *it : throw not_found("[%zu]", i); +} + +inline ircd::json::arr::const_iterator +ircd::json::arr::find(size_t i) +const +{ + auto it(begin()); + for(; it != end() && i; ++it, i--); + return it; +} + +inline size_t +ircd::json::arr::count() +const +{ + return std::distance(begin(), end()); +} diff --git a/include/ircd/json/array.h b/include/ircd/json/array.h deleted file mode 100644 index ea2e181ec..000000000 --- a/include/ircd/json/array.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2017 Charybdis Development Team - * Copyright (C) 2017 Jason Volk - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice is present in all copies. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once -#define HAVE_IRCD_JSON_ARRAY_H - -namespace ircd { -namespace json { - -struct array -{ - struct const_iterator; - - using value_type = const string_view; - using pointer = value_type *; - using reference = value_type &; - using iterator = const_iterator; - using size_type = size_t; - using difference_type = std::ptrdiff_t; - using key_compare = std::less; - - string_view state; - - operator string_view() const { return state; } - - const_iterator end() const; - const_iterator begin() const; - - bool empty() const; - size_t size() const; - - array(const string_view &state = {}) - :state{state} - {} -}; - -struct array::const_iterator -{ - using value_type = const string_view; - using pointer = value_type *; - using reference = value_type &; - using difference_type = size_t; - using iterator_category = std::forward_iterator_tag; - - protected: - friend class array; - - const char *start; - const char *stop; - string_view state; - - const_iterator(const char *const &start, const char *const &stop) - :start{start} - ,stop{stop} - {} - - public: - auto operator==(const const_iterator &o) const { return start == o.start && stop == o.stop; } - auto operator!=(const const_iterator &o) const { return !(*this == o); } - value_type *operator->() const { return &state; } - value_type &operator*() const { return *operator->(); } - - const_iterator &operator++(); -}; - -} // namespace json -} // namespace ircd - -inline size_t -ircd::json::array::size() -const -{ - return std::distance(begin(), end()); -} - -inline bool -ircd::json::array::empty() -const -{ - return state.empty(); -} diff --git a/include/ircd/json/doc.h b/include/ircd/json/doc.h index 04c02ec8a..88b7ad9b7 100644 --- a/include/ircd/json/doc.h +++ b/include/ircd/json/doc.h @@ -38,7 +38,7 @@ struct doc using reference = value_type &; using iterator = const_iterator; using size_type = size_t; - using difference_type = size_t; + using difference_type = ptrdiff_t; using key_compare = std::less; bool contains(const string_view &) const; diff --git a/include/ircd/json/obj.h b/include/ircd/json/obj.h index 8d047496b..1e74efebd 100644 --- a/include/ircd/json/obj.h +++ b/include/ircd/json/obj.h @@ -62,7 +62,7 @@ struct obj bool erase(const string_view &name); obj(std::initializer_list); - explicit obj(const doc &d); + obj(const doc &d); obj() = default; obj(obj &&) = default; obj(const obj &) = delete; diff --git a/include/ircd/json/val.h b/include/ircd/json/val.h index 54091a063..fa58b1f41 100644 --- a/include/ircd/json/val.h +++ b/include/ircd/json/val.h @@ -29,9 +29,10 @@ struct val { union // xxx std::variant { + uint64_t integer; const char *string; const struct obj *object; - uint64_t integer; + const struct array *array; }; uint64_t len : 59; @@ -39,6 +40,7 @@ struct val uint64_t serial : 1; uint64_t alloc : 1; + public: size_t size() const; operator string_view() const; @@ -56,7 +58,7 @@ struct val val() = default; val(val &&) noexcept; val(const val &) = delete; - val &operator=(val &&) = default; + val &operator=(val &&) noexcept; val &operator=(const val &) = delete; ~val() noexcept; @@ -105,7 +107,7 @@ ircd::json::val::val(const struct obj &object, inline ircd::json::val::val(val &&other) noexcept -:string{other.string} +:integer{other.integer} ,len{other.len} ,type{other.type} ,serial{other.serial} @@ -114,6 +116,21 @@ noexcept other.alloc = false; } +inline +ircd::json::val & +ircd::json::val::operator=(val &&other) +noexcept +{ + this->~val(); + integer = other.integer; + len = other.len; + type = other.type; + serial = other.serial; + alloc = other.alloc; + other.alloc = false; + return *this; +} + inline bool ircd::json::operator<(const val &a, const val &b) { diff --git a/ircd/json.cc b/ircd/json.cc index 4cbbcb6a8..620968de7 100644 --- a/ircd/json.cc +++ b/ircd/json.cc @@ -64,161 +64,85 @@ using karma::attr_cast; template struct input -:qi::grammar +:qi::grammar { + template using rule = qi::rule; + // insignificant whitespaces - qi::rule SP { lit('\x20'), "space" }; - qi::rule HT { lit('\x09'), "horizontal tab" }; - qi::rule CR { lit('\x0D'), "carriage return" }; - qi::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 - qi::rule WS { SP | HT | CR | LF, "whitespace" }; - qi::rule ws { *(WS), "whitespace monoid" }; - qi::rule wsp { +(WS), "whitespace semigroup" }; + rule<> WS { SP | HT | CR | LF ,"whitespace" }; + rule<> ws { *(WS) ,"whitespace monoid" }; + rule<> wsp { +(WS) ,"whitespace semigroup" }; // structural - qi::rule object_begin { lit('{'), "object begin" }; - qi::rule object_end { lit('}'), "object end" }; - qi::rule array_begin; - qi::rule array_end; - qi::rule name_sep; - qi::rule 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 - qi::rule lit_true; - qi::rule lit_false; - qi::rule lit_null; + rule lit_true { lit("true") ,"literal true" }; + rule lit_false { lit("false") ,"literal false" }; + rule lit_null { lit("null") ,"literal null" }; - qi::rule quote; - qi::rule chars; - qi::rule string; + rule<> quote { lit("\"") ,"quote" }; + rule chars { raw[*(char_ - quote)] ,"characters" }; + rule string { quote >> chars >> quote ,"string" }; + rule name { string ,"name" }; - qi::rule boolean; - qi::rule literal; - qi::rule number; - qi::rule array; - qi::rule object; + rule boolean { lit_true | lit_false ,"boolean" }; + rule literal { lit_true | lit_false | lit_null ,"literal" }; + rule number { raw[double_] ,"number" }; - qi::rule name; - qi::rule value; - qi::rule member; + rule array + { + array_begin >> -(omit[ws >> value >> ws] % value_sep) >> ws >> array_end + ,"array" + }; - qi::rule type; + rule object + { + object_begin >> -(omit[ws >> member >> ws] % value_sep) >> ws >> object_end + ,"object" + }; - input(); + rule value + { + lit_false | lit_true | lit_null | object | array | number | string + ,"value" + }; + + rule member + { + name >> ws >> name_sep >> ws >> value + ,"member" + }; + + rule type + { + (omit[object_begin] >> attr(json::OBJECT)) | + (omit[array_begin] >> attr(json::ARRAY)) | + (omit[quote] >> attr(json::STRING)) | + (omit[number] >> attr(json::NUMBER)) | + (omit[literal] >> attr(json::LITERAL)) + ,"type" + }; + + input() + :input::base_type{rule<>{}} + { + array %= array_begin >> -(omit[ws >> value >> ws] % value_sep) >> ws >> array_end; + object %= object_begin >> -(omit[ws >> member >> ws] % value_sep) >> ws >> object_end; + } }; -template -input::input() -:input::base_type -{ - object -} -,array_begin -{ - lit('[') - ,"array begin" -} -,array_end -{ - lit(']') - ,"array end" -} -,name_sep -{ - lit(':') - ,"name separator" -} -,value_sep -{ - lit(',') - ,"value separator" -} -,lit_true -{ - lit("true") - ,"literal true" -} -,lit_false -{ - lit("false") - ,"literal false" -} -,lit_null -{ - lit("null") - ,"literal null" -} -,quote -{ - lit('\"') - ,"quote" -} -,chars -{ - raw[*(char_ - quote)] - ,"string" -} -,string -{ - quote >> chars >> quote - ,"string" -} -,boolean -{ - lit_true | lit_false - ,"boolean" -} -,literal -{ - lit_true | lit_false | lit_null - ,"literal" -} -,number -{ - raw[double_] - ,"number" -} -,array -{ - array_begin >> -(omit[ws >> value >> ws] % value_sep) >> ws >> array_end - ,"array" -} -,object -{ - object_begin >> -(omit[ws >> member >> ws] % value_sep) >> ws >> object_end - ,"object" -} -,name -{ - string - ,"name" -} -,value -{ - lit_false | lit_true | lit_null | object | array | number | string - ,"value" -} -,member -{ - name >> -ws >> name_sep >> -ws >> value - ,"member" -} -,type -{ - (omit[object_begin] >> attr(json::OBJECT)) | - (omit[quote] >> attr(json::STRING)) | - (omit[number] >> attr(json::NUMBER)) | - (omit[literal] >> attr(json::LITERAL)) | - (omit[array_begin] >> attr(json::ARRAY)) - ,"type" -} -{ - array %= array_begin >> -(omit[ws >> value >> ws] % value_sep) >> ws >> array_end; - object %= object_begin >> -(omit[ws >> member >> ws] % value_sep) >> ws >> object_end; -} - template struct output :karma::grammar @@ -239,13 +163,13 @@ struct output 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 separator" }; - rule<> value_sep { lit(',') ,"value separator" }; - rule<> quote { lit('"') ,"quote" }; + 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 separator" }; + rule<> value_sep { lit(',') ,"value separator" }; + rule<> quote { lit('"') ,"quote" }; rule lit_true { karma::string("true") ,"literal true" }; rule lit_false { karma::string("false") ,"literal false" }; @@ -254,7 +178,7 @@ struct output rule boolean { lit_true | lit_false ,"boolean" }; rule literal { lit_true | lit_false | lit_null ,"literal" }; - rule chars { *(~char_("\"")) ,"chars" }; + rule chars { *(~char_("\"")) ,"characters" }; rule string { quote << chars << quote ,"string" }; rule number { double_ ,"number" }; @@ -262,8 +186,8 @@ struct output rule name { string ,"name" }; rule value { rule{} ,"value" }; - rule elems { (value % value_sep) ,"elements" }; - rule array { array_begin << elems << array_end ,"array" }; + 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" }; @@ -301,10 +225,12 @@ struct ostreamer } const ostreamer; -doc serialize(const obj &, char *&start, char *const &stop); -size_t print(char *const &buf, const size_t &max, const obj &); +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 &); +doc serialize(const obj &, char *&start, char *const &stop); +std::ostream &operator<<(std::ostream &, const arr &); std::ostream &operator<<(std::ostream &, const doc::member &); std::ostream &operator<<(std::ostream &, const doc &); std::ostream &operator<<(std::ostream &, const obj &); @@ -316,6 +242,13 @@ ircd::json::printer::printer() { const auto recursor([this](auto &a, auto &b, auto &c) { + 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 auto recurse_document([&] { char *out(const_cast(a.data())); @@ -332,7 +265,7 @@ ircd::json::printer::printer() if(likely(!a.empty())) switch(a.front()) { case '{': recurse_document(); break; - case '[': c = false; break; + case '[': recurse_array(); break; case '"': break; case '0': break; case '1': break; @@ -363,6 +296,13 @@ ircd::json::ostreamer::ostreamer() { const auto recursor([this](auto &a, auto &b, auto &c) { + 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 auto recurse_document([&] { char *out(const_cast(a.data())); @@ -379,7 +319,7 @@ ircd::json::ostreamer::ostreamer() if(likely(!a.empty())) switch(a.front()) { case '{': recurse_document(); break; - case '[': c = false; break; + case '[': recurse_array(); break; case '"': break; case '0': break; case '1': break; @@ -447,6 +387,18 @@ ircd::json::serialize(const obj &obj, serialize(*val.object, out, stop); }); + const auto print_array([&stop, &out](const val &val) + { + if(val.serial) + { + karma::generate(out, maxwidth(stop - out)[printer.array] | eps[throws], val); + return; + } + + //assert(val.object); + //serialize(*val.object, out, stop); + }); + const auto print_member([&](const obj::member &member) { const auto generate_name @@ -458,9 +410,11 @@ ircd::json::serialize(const obj &obj, switch(member.second.type) { - case OBJECT: print_object(member.second); break; case STRING: print_string(member.second); break; - default: throw type_error("Cannot stream unsupported member type"); + case OBJECT: print_object(member.second); break; + case ARRAY: print_array(member.second); break; + default: + throw type_error("Cannot stream unsupported member type"); } }); @@ -494,14 +448,14 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) const auto stream_string([&osi](const val &val) { - karma::generate(osi, ostreamer.string, string_view{val}); + karma::generate(osi, ostreamer.string, val); }); const auto stream_object([&osi, &s](const val &val) { if(val.serial) { - karma::generate(osi, ostreamer.document, string_view{val}); + karma::generate(osi, ostreamer.document, val); return; } @@ -509,15 +463,30 @@ ircd::json::operator<<(std::ostream &s, const obj &obj) s << *val.object; }); + const auto stream_array([&osi, &s](const val &val) + { + if(val.serial) + { + karma::generate(osi, ostreamer.array, val); + return; + } + + assert(0); + //assert(val.object); + //s << *val.object; + }); + 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 OBJECT: stream_object(member.second); break; case STRING: stream_string(member.second); break; - default: throw type_error("cannot stream unsupported member type"); + case OBJECT: stream_object(member.second); break; + case ARRAY: stream_array(member.second); break; + default: + throw type_error("cannot stream unsupported member type"); } }); @@ -605,6 +574,10 @@ 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); @@ -612,12 +585,15 @@ ircd::json::operator<<(std::ostream &s, const val &v) s << *v.object; break; - case STRING: - s << string_view(v); + case ARRAY: + if(v.serial) + s << string_view(v); + else + assert(0); break; default: - throw type_error("cannot stream value"); + throw type_error("cannot stream value type[%d]", int(v.type)); } return s; @@ -627,10 +603,11 @@ inline ircd::json::val::~val() noexcept { - switch(type) + switch(type) { - case OBJECT: if(alloc) delete object; break; case STRING: if(alloc) delete[] string; break; + case OBJECT: if(alloc) delete object; break; + //case ARRAY: if(alloc) delete array; break; default: break; } } @@ -640,17 +617,48 @@ const { switch(type) { - case OBJECT: if(!serial) return std::string(*object); - case STRING: return std::string{string_view(*this)}; - default: throw type_error("cannot stringify type"); + case STRING: + return std::string(unquote(string_view(*this))); + + case OBJECT: + if(serial) + return std::string(string_view(*this)); + else + return std::string(*object); + + case ARRAY: + if(serial) + return std::string(string_view(*this)); + else + break; + + default: + break; } + + throw type_error("cannot stringify type[%d]", int(type)); } ircd::json::val::operator string_view() const { - return serial? string_view { string, len }: - throw type_error("Value not a string"); + switch(type) + { + case STRING: + return unquote(string_view{string, len}); + + case ARRAY: + case OBJECT: + if(serial) + return string_view{string, len}; + else + break; + + default: + break; + } + + throw type_error("value type[%d] is not a string", int(type)); } size_t @@ -659,11 +667,14 @@ const { switch(type) { - case OBJECT: return serial? len : object->size(); - case STRING: return 1 + len + 1; case NUMBER: return lex_cast(integer).size(); - default: throw type_error("cannot size type"); + case STRING: return 1 + len + 1; + case OBJECT: return serial? len : object->size(); + case ARRAY: return serial? len : 2; + default: break; }; + + throw type_error("cannot size type[%u]", int(type)); } size_t @@ -671,11 +682,6 @@ ircd::json::print(char *const &buf, const size_t &max, const doc &doc) { - static const auto throws([] - { - throw print_error("The JSON generator failed to print document"); - }); - if(unlikely(!max)) return 0; @@ -762,7 +768,7 @@ const parser.object_begin >> parser.ws >> (parser.object_end | member) }; - iterator ret(string_view::begin(), string_view::end()); + const_iterator ret(string_view::begin(), string_view::end()); if(!qi::phrase_parse(ret.start, ret.stop, parse_begin, parser.WS, ret.state)) ret.start = ret.stop; @@ -776,12 +782,60 @@ const return { string_view::end(), string_view::end() }; } -ircd::json::array::const_iterator & -ircd::json::array::const_iterator::operator++() +size_t +ircd::json::print(char *const &buf, + const size_t &max, + const arr &arr) +{ + if(unlikely(!max)) + return 0; + + char *out(buf); + serialize(arr, out, out + (max - 1)); + *out = '\0'; + return std::distance(buf, out); +} + +ircd::json::arr +ircd::json::serialize(const arr &a, + char *&out, + char *const &stop) +{ + static const auto throws([] + { + 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); + return string_view{start, out}; +} + +std::ostream & +ircd::json::operator<<(std::ostream &s, const arr &arr) +{ + const auto &os(ostreamer); + static const auto throws([] + { + throw print_error("The JSON generator failed to output array to stream"); + }); + + karma::ostream_iterator osi(s); + karma::generate(osi, os.array | eps[throws], arr); + return s; +} + +ircd::json::arr::const_iterator & +ircd::json::arr::const_iterator::operator++() { static const qi::rule parse_next { - (parser.array_end | (parser.value_sep >> parser.ws >> raw[parser.value])) + parser.array_end | (parser.value_sep >> parser.ws >> raw[parser.value]) }; state = string_view{}; @@ -791,8 +845,8 @@ ircd::json::array::const_iterator::operator++() return *this; } -ircd::json::array::const_iterator -ircd::json::array::begin() +ircd::json::arr::const_iterator +ircd::json::arr::begin() const { static const qi::rule parse_begin @@ -800,18 +854,18 @@ const parser.array_begin >> parser.ws >> (parser.array_end | raw[parser.value]) }; - iterator ret(state.begin(), state.end()); + const_iterator ret(string_view::begin(), string_view::end()); if(!qi::phrase_parse(ret.start, ret.stop, parse_begin, parser.WS, ret.state)) ret.start = ret.stop; return ret; } -ircd::json::array::const_iterator -ircd::json::array::end() +ircd::json::arr::const_iterator +ircd::json::arr::end() const { - return { state.end(), state.end() }; + return { string_view::end(), string_view::end() }; } enum ircd::json::type