diff --git a/include/ircd/json.h b/include/ircd/json.h index bc3276b0c..c40dcd7b7 100644 --- a/include/ircd/json.h +++ b/include/ircd/json.h @@ -68,7 +68,7 @@ namespace ircd::json struct object; struct value; struct index; - struct builder; + struct iov; enum type { @@ -101,7 +101,7 @@ namespace ircd::json #include "json/member.h" #include "json/index.h" #include "json/property.h" -#include "json/builder.h" +#include "json/iov.h" #include "json/tuple.h" namespace ircd diff --git a/include/ircd/json/builder.h b/include/ircd/json/builder.h deleted file mode 100644 index a4ad8dfe5..000000000 --- a/include/ircd/json/builder.h +++ /dev/null @@ -1,218 +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_BUILDER_H - -// ircd::json::builder is forward list to compose JSON dynamically and -// efficiently on the stack. The product of the builder is an iteration of the -// added members for use by stringifying and iovectoring. This gathers the -// members on a trip up the stack without rewriting a JSON string at each frame. -// -namespace ircd::json -{ - struct builder; - - using member_closure = std::function; - using member_closure_bool = std::function; - using builder_closure_bool = std::function; - - builder *head(builder *const &); - builder *next(builder *const &); - builder *prev(builder *const &); - builder *tail(builder *); - builder *find(builder *, const builder_closure_bool &); - - const builder *head(const builder *const &); - const builder *next(const builder *const &); - const builder *prev(const builder *const &); - const builder *tail(const builder *); - const builder *find(const builder *, const builder_closure_bool &); - - void for_each(const builder &, const member_closure &); - bool until(const builder &, const member_closure_bool &); - size_t count(const builder &, const member_closure_bool &); - size_t count(const builder &); -} - -struct ircd::json::builder -{ - struct add; - struct set; - struct push; - - IRCD_EXCEPTION(json::error, error); - IRCD_EXCEPTION(error, exists); - - member m; - const members *ms; - builder *head; - builder *child; - - bool test(const member_closure_bool &) const; - - // recursive! - const member *find(const string_view &key) const; - const json::value &at(const string_view &key) const; - bool has(const string_view &key) const; - - builder(member m = {}, - const members *const &ms = nullptr, - builder *const &head = nullptr, - builder *const &child = nullptr) - :m{std::move(m)} - ,ms{ms} - ,head{head} - ,child{child} - {} - - builder(const members &ms) - :builder{{}, &ms} - {} - - builder(member m) - :builder{std::move(m)} - {} - - friend string_view stringify(mutable_buffer &, const builder &); -}; - -struct ircd::json::builder::push -:private ircd::json::builder -{ - push(builder &head, const members &ms) - :builder{{}, &ms, &head} - { - tail(&head)->child = this; - } - - push(builder &head, member m) - :builder{std::move(m), nullptr, &head} - { - tail(&head)->child = this; - } -}; - -struct ircd::json::builder::add -:private ircd::json::builder -{ - add(builder &head, const members &ms); - add(builder &head, member member); -}; - -struct ircd::json::builder::set -:private ircd::json::builder -{ - set(builder &head, const members &ms); - set(builder &head, member member); -}; - -inline ircd::json::builder * -ircd::json::find(builder *builder, - const builder_closure_bool &test) -{ - for(; builder; builder = next(builder)) - if(test(*builder)) - return builder; - - return nullptr; -} - -inline const ircd::json::builder * -ircd::json::find(const builder *builder, - const builder_closure_bool &test) -{ - for(; builder; builder = next(builder)) - if(test(*builder)) - return builder; - - return nullptr; -} - -inline ircd::json::builder * -ircd::json::tail(builder *ret) -{ - while(ret && next(ret)) - ret = next(ret); - - return ret; -} - -inline const ircd::json::builder * -ircd::json::tail(const builder *ret) -{ - while(ret && next(ret)) - ret = next(ret); - - return ret; -} - -inline ircd::json::builder * -ircd::json::prev(builder *const &builder) -{ - assert(builder); - auto *ret(builder->head); - for(; ret; ret = next(ret)) - if(next(ret) == builder) - return ret; - - return nullptr; -} - -inline const ircd::json::builder * -ircd::json::prev(const builder *const &builder) -{ - assert(builder); - const auto *ret(builder->head); - for(; ret; ret = next(ret)) - if(next(ret) == builder) - return ret; - - return nullptr; -} - -inline ircd::json::builder * -ircd::json::next(builder *const &builder) -{ - assert(builder); - return builder->child; -} - -inline const ircd::json::builder * -ircd::json::next(const builder *const &builder) -{ - assert(builder); - return builder->child; -} - -inline ircd::json::builder * -ircd::json::head(builder *const &builder) -{ - assert(builder); - return builder->head; -} - -inline const ircd::json::builder * -ircd::json::head(const builder *const &builder) -{ - assert(builder); - return builder->head; -} diff --git a/include/ircd/json/iov.h b/include/ircd/json/iov.h new file mode 100644 index 000000000..9834f23eb --- /dev/null +++ b/include/ircd/json/iov.h @@ -0,0 +1,231 @@ +/* + * 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_IOV_H + +// ircd::json::iov is forward list to compose JSON dynamically and +// efficiently on the stack. The product of the iov is an iteration of the +// added members for use by stringifying and iovectoring. This gathers the +// members on a trip up the stack without rewriting a JSON string at each frame. +// +namespace ircd::json +{ + struct iov; + + using member_closure = std::function; + using member_closure_bool = std::function; + using iov_closure_bool = std::function; + + iov *head(iov *const &); + iov *next(iov *const &); + iov *prev(iov *const &); + iov *tail(iov *); + iov *find(iov *, const iov_closure_bool &); + + const iov *head(const iov *const &); + const iov *next(const iov *const &); + const iov *prev(const iov *const &); + const iov *tail(const iov *); + const iov *find(const iov *, const iov_closure_bool &); + + void for_each(const iov &, const member_closure &); + bool until(const iov &, const member_closure_bool &); + size_t count(const iov &, const member_closure_bool &); + size_t count(const iov &); +} + +struct ircd::json::iov +{ + struct add; + struct set; + struct push; + + IRCD_EXCEPTION(json::error, error); + IRCD_EXCEPTION(error, exists); + + iov *head {nullptr}; + iov *child {nullptr}; + const member *b {nullptr}; + const member *e {nullptr}; + member m; + + bool test(const member_closure_bool &) const; + + // recursive! + const member *find(const string_view &key) const; + const json::value &at(const string_view &key) const; + bool has(const string_view &key) const; + + iov(iov *const &head, + iov *const &child, + member m) + :head{head} + ,child{child} + ,m{std::move(m)} + {} + + iov(iov *const &head, + iov *const &child, + const member *const &begin, + const member *const &end) + :head{head} + ,child{child} + ,b{begin} + ,e{end} + {} + + iov(iov *const &head, + iov *const &child, + const members &ms) + :head{head} + ,child{child} + ,b{std::begin(ms)} + ,e{std::end(ms)} + {} + + iov(const members &ms) + :iov{nullptr, nullptr, std::begin(ms), std::end(ms)} + {} + + iov(member m = {}) + :iov{nullptr, nullptr, std::move(m)} + {} + + friend string_view stringify(mutable_buffer &, const iov &); +}; + +struct ircd::json::iov::push +:private ircd::json::iov +{ + template + push(iov &head, args&&... a) + :iov{&head, nullptr, std::forward(a)...} + { + tail(&head)->child = this; + } +}; + +struct ircd::json::iov::add +:private ircd::json::iov +{ + add(iov &head, const members &ms); + add(iov &head, member member); +}; + +struct ircd::json::iov::set +:private ircd::json::iov +{ + set(iov &head, const members &ms); + set(iov &head, member member); +}; + +inline ircd::json::iov * +ircd::json::find(iov *iov, + const iov_closure_bool &test) +{ + for(; iov; iov = next(iov)) + if(test(*iov)) + return iov; + + return nullptr; +} + +inline const ircd::json::iov * +ircd::json::find(const iov *iov, + const iov_closure_bool &test) +{ + for(; iov; iov = next(iov)) + if(test(*iov)) + return iov; + + return nullptr; +} + +inline ircd::json::iov * +ircd::json::tail(iov *ret) +{ + while(ret && next(ret)) + ret = next(ret); + + return ret; +} + +inline const ircd::json::iov * +ircd::json::tail(const iov *ret) +{ + while(ret && next(ret)) + ret = next(ret); + + return ret; +} + +inline ircd::json::iov * +ircd::json::prev(iov *const &iov) +{ + assert(iov); + auto *ret(iov->head); + for(; ret; ret = next(ret)) + if(next(ret) == iov) + return ret; + + return nullptr; +} + +inline const ircd::json::iov * +ircd::json::prev(const iov *const &iov) +{ + assert(iov); + const auto *ret(iov->head); + for(; ret; ret = next(ret)) + if(next(ret) == iov) + return ret; + + return nullptr; +} + +inline ircd::json::iov * +ircd::json::next(iov *const &iov) +{ + assert(iov); + return iov->child; +} + +inline const ircd::json::iov * +ircd::json::next(const iov *const &iov) +{ + assert(iov); + return iov->child; +} + +inline ircd::json::iov * +ircd::json::head(iov *const &iov) +{ + assert(iov); + return iov->head; +} + +inline const ircd::json::iov * +ircd::json::head(const iov *const &iov) +{ + assert(iov); + return iov->head; +} diff --git a/include/ircd/json/tuple.h b/include/ircd/json/tuple.h index bfbbe00be..9230fcdfd 100644 --- a/include/ircd/json/tuple.h +++ b/include/ircd/json/tuple.h @@ -67,7 +67,7 @@ struct tuple static constexpr size_t size(); tuple(const json::object &); - tuple(const json::builder &); + tuple(const json::iov &); tuple(const std::initializer_list &); tuple() = default; }; @@ -580,12 +580,24 @@ tuple::tuple(const json::object &object) } template -tuple::tuple(const json::builder &builder) +tuple::tuple(const json::iov &iov) { //TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that? - for_each(builder, [this] + for_each(iov, [this] (const auto &member) { + switch(type(member.second)) + { + case type::STRING: + case type::OBJECT: + case type::ARRAY: + if(unlikely(!member.second.serial)) + throw print_error("iov member '%s' must be JSON to be used by the tuple", + string_view{member.first}); + default: + break; + } + at(*this, member.first, [&member] (auto &target) { diff --git a/include/ircd/m/events.h b/include/ircd/m/events.h index f26859274..cc88c77e1 100644 --- a/include/ircd/m/events.h +++ b/include/ircd/m/events.h @@ -44,8 +44,8 @@ namespace ircd::m::events const_iterator begin(); const_iterator end(); - void insert(event &); void insert(const event &); + void insert(json::iov &); } struct ircd::m::events::transition diff --git a/include/ircd/m/room.h b/include/ircd/m/room.h index 6c419779f..f07604c01 100644 --- a/include/ircd/m/room.h +++ b/include/ircd/m/room.h @@ -56,8 +56,9 @@ struct ircd::m::room bool is_member(const m::id::user &, const string_view &membership = "join"); - void membership(const m::id::user &, const json::builder &content); - void join(const m::id::user &, const json::builder &content); + bool is_member(const m::id::user &, const string_view &membership = "join"); + void membership(const m::id::user &, json::iov &content); + void join(const m::id::user &, json::iov &content); room(const id::room &room_id); room(const id::alias &alias); diff --git a/include/ircd/resource.h b/include/ircd/resource.h index 7c9d8b33e..6af24dca5 100644 --- a/include/ircd/resource.h +++ b/include/ircd/resource.h @@ -93,8 +93,9 @@ struct ircd::resource::response { response(client &, const string_view &str, const string_view &ct = "text/plain; charset=utf8", const http::code & = http::OK); response(client &, const json::object & = "{}", const http::code & = http::OK); - response(client &, const json::members &, const http::code & = http::OK); - response(client &, const http::code &, const json::members &); + response(client &, const json::iov &, const http::code & = http::OK); + response(client &, const http::code &, const json::iov &); + response(client &, const http::code &); response() = default; }; diff --git a/ircd/json.cc b/ircd/json.cc index ef7e4aa6a..9f3d4f5c0 100644 --- a/ircd/json.cc +++ b/ircd/json.cc @@ -1414,18 +1414,18 @@ ircd::json::type(const string_view &buf, /////////////////////////////////////////////////////////////////////////////// // -// builder.h +// iov.h // ircd::string_view ircd::json::stringify(mutable_buffer &head, - const builder &builder) + const iov &iov) { - const auto num{count(builder)}; + const auto num{count(iov)}; const member *m[num]; size_t i(0); - for_each(builder, [&i, &m] + for_each(iov, [&i, &m] (const auto &member) { m[i++] = &member; @@ -1435,9 +1435,9 @@ ircd::json::stringify(mutable_buffer &head, } size_t -ircd::json::count(const builder &builder) +ircd::json::count(const iov &iov) { - return count(builder, [] + return count(iov, [] (const auto &) { return true; @@ -1445,11 +1445,11 @@ ircd::json::count(const builder &builder) } size_t -ircd::json::count(const builder &builder, +ircd::json::count(const iov &iov, const member_closure_bool &closure) { size_t ret(0); - for_each(builder, [&closure, &ret] + for_each(iov, [&closure, &ret] (const auto &member) { ret += closure(member); @@ -1459,11 +1459,11 @@ ircd::json::count(const builder &builder, } void -ircd::json::for_each(const builder &b, +ircd::json::for_each(const iov &b, const member_closure &closure) { - if(b.ms) - std::for_each(begin(*b.ms), end(*b.ms), closure); + if(b.b) + std::for_each(b.b, b.e, closure); else if(!b.m.first.empty()) closure(b.m); @@ -1472,12 +1472,12 @@ ircd::json::for_each(const builder &b, } bool -ircd::json::until(const builder &b, +ircd::json::until(const iov &b, const member_closure_bool &closure) { - if(b.ms) + if(b.b && b.e) { - if(!ircd::until(begin(*b.ms), end(*b.ms), closure)) + if(!ircd::until(b.b, b.e, closure)) return false; } else if(!b.m.first.empty() && !closure(b.m)) @@ -1490,7 +1490,7 @@ ircd::json::until(const builder &b, } bool -ircd::json::builder::has(const string_view &key) +ircd::json::iov::has(const string_view &key) const { const auto *const member(find(key)); @@ -1498,7 +1498,7 @@ const } const ircd::json::value & -ircd::json::builder::at(const string_view &key) +ircd::json::iov::at(const string_view &key) const { const auto *const member(find(key)); @@ -1509,7 +1509,7 @@ const } const ircd::json::member * -ircd::json::builder::find(const string_view &key) +ircd::json::iov::find(const string_view &key) const { const member *ret; @@ -1530,11 +1530,11 @@ const } bool -ircd::json::builder::test(const member_closure_bool &closure) +ircd::json::iov::test(const member_closure_bool &closure) const { - if(ms) - return ircd::until(begin(*ms), end(*ms), closure); + if(b && likely(e)) + return ircd::until(b, e, closure); if(!m.first.empty()) return closure(m); @@ -1542,10 +1542,10 @@ const return true; } -ircd::json::builder::add::add(builder &head, const members &ms) -:builder{{}, &ms, &head} +ircd::json::iov::add::add(iov &head, const members &ms) +:iov{&head, nullptr, ms} { - const auto existing(json::find(&head, [&ms](const builder &existing) + const auto existing(json::find(&head, [&ms](const iov &existing) { return existing.test([&ms](const member &existing) { @@ -1564,10 +1564,10 @@ ircd::json::builder::add::add(builder &head, const members &ms) tail(&head)->child = this; } -ircd::json::builder::add::add(builder &head, member member) -:builder{std::move(member), nullptr, &head} +ircd::json::iov::add::add(iov &head, member member) +:iov{&head, nullptr, std::move(member)} { - const auto existing(json::find(&head, [&member](const builder &existing) + const auto existing(json::find(&head, [&member](const iov &existing) { return existing.test([&member](const auto &existing) { @@ -1582,10 +1582,10 @@ ircd::json::builder::add::add(builder &head, member member) tail(&head)->child = this; } -ircd::json::builder::set::set(builder &head, const members &ms) -:builder{{}, &ms, &head} +ircd::json::iov::set::set(iov &head, const members &ms) +:iov{&head, nullptr, ms} { - const auto existing(json::find(&head, [&ms](const builder &existing) + const auto existing(json::find(&head, [&ms](const iov &existing) { return existing.test([&ms](const member &existing) { @@ -1609,10 +1609,10 @@ ircd::json::builder::set::set(builder &head, const members &ms) else tail(&head)->child = this; } -ircd::json::builder::set::set(builder &head, member member) -:builder{std::move(member), nullptr, &head} +ircd::json::iov::set::set(iov &head, member member) +:iov{&head, nullptr, std::move(member)} { - const auto existing(json::find(&head, [&member](const builder &existing) + const auto existing(json::find(&head, [&member](const iov &existing) { return existing.test([&member](const auto &existing) { diff --git a/ircd/matrix.cc b/ircd/matrix.cc index a45cae93e..df4c2ce22 100644 --- a/ircd/matrix.cc +++ b/ircd/matrix.cc @@ -248,29 +248,24 @@ namespace ircd::m::events ircd::database *ircd::m::events::events; void -ircd::m::events::insert(json::builder &builder) +ircd::m::events::insert(json::iov &iov) { const id::event::buf generated_event_id { - builder.has("event_id")? id::event::buf{} : id::event::buf{id::generate, "cdc.z"} + iov.has("event_id")? id::event::buf{} : id::event::buf{id::generate, "cdc.z"} }; - const json::builder::add event_id + const json::iov::add event_id { - builder, { "event_id", generated_event_id } + iov, { "event_id", generated_event_id } }; - const json::builder::set event_id2 + const json::iov::add origin_server_ts { - builder, { "event_id", generated_event_id } + iov, { "origin_server_ts", time() } }; - const json::builder::add origin_server_ts - { - builder, { "origin_server_ts", time() } - }; - - insert(event{builder}); + insert(event{iov}); } void @@ -408,9 +403,9 @@ ircd::m::events::write(const event &event) void ircd::m::room::join(const m::id::user &user_id, - json::builder &content) + json::iov &content) { - json::builder::set membership_join + json::iov::set membership_join { content, { "membership", "join" } }; @@ -420,7 +415,7 @@ ircd::m::room::join(const m::id::user &user_id, void ircd::m::room::membership(const m::id::user &user_id, - json::builder &content) + json::iov &content) { if(is_member(user_id, content.at("membership"))) throw m::ALREADY_MEMBER @@ -434,17 +429,15 @@ ircd::m::room::membership(const m::id::user &user_id, stringify(buffer, content) }; - json::builder event; - json::builder::set fields{event, + json::iov event { - { "room_id", room_id }, { "type", "m.room.member" }, { "state_key", user_id }, { "sender", user_id }, { "content", printed_content } - }}; + }; - m::events::insert(event); + send(event); } bool diff --git a/ircd/resource.cc b/ircd/resource.cc index edbe4da78..e4ef133d9 100644 --- a/ircd/resource.cc +++ b/ircd/resource.cc @@ -247,13 +247,19 @@ ircd::resource::request::request(const http::request::head &head, ircd::resource::response::response(client &client, const http::code &code, - const json::members &members) + const json::iov &members) :response{client, members, code} { } ircd::resource::response::response(client &client, - const json::members &members, + const http::code &code) +:response{client, json::object{"{}"}, code} +{ +} + +ircd::resource::response::response(client &client, + const json::iov &members, const http::code &code) try {