0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-29 10:12:39 +01:00

ircd::json: Rename builder to iov.

This commit is contained in:
Jason Volk 2017-09-09 12:20:00 -07:00
parent c3a88edeee
commit 8ad134c565
10 changed files with 308 additions and 282 deletions

View file

@ -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

View file

@ -1,218 +0,0 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* 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<void (const member &)>;
using member_closure_bool = std::function<bool (const member &)>;
using builder_closure_bool = std::function<bool (const builder &)>;
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;
}

231
include/ircd/json/iov.h Normal file
View file

@ -0,0 +1,231 @@
/*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk <jason@zemos.net>
*
* 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<void (const member &)>;
using member_closure_bool = std::function<bool (const member &)>;
using iov_closure_bool = std::function<bool (const iov &)>;
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<class... args>
push(iov &head, args&&... a)
:iov{&head, nullptr, std::forward<args>(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;
}

View file

@ -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<member> &);
tuple() = default;
};
@ -580,12 +580,24 @@ tuple<T...>::tuple(const json::object &object)
}
template<class... T>
tuple<T...>::tuple(const json::builder &builder)
tuple<T...>::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)
{

View file

@ -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

View file

@ -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);

View file

@ -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;
};

View file

@ -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)
{

View file

@ -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<milliseconds>() }
};
const json::builder::add origin_server_ts
{
builder, { "origin_server_ts", time<milliseconds>() }
};
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

View file

@ -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
{