0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 08:12:37 +01:00

ircd::json: Improve tuple/builder related.

This commit is contained in:
Jason Volk 2017-09-08 12:29:21 -07:00
parent db748cae81
commit e95fe7fbd9
6 changed files with 554 additions and 197 deletions

View file

@ -96,8 +96,8 @@ namespace ircd::json
#include "json/member.h"
#include "json/index.h"
#include "json/property.h"
#include "json/tuple.h"
#include "json/builder.h"
#include "json/tuple.h"
namespace ircd
{

View file

@ -22,153 +22,197 @@
#pragma once
#define HAVE_IRCD_JSON_BUILDER_H
// ircd::json::builder is an interface to compose JSON dynamically and
// efficiently. 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.
// 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.
//
struct ircd::json::builder
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);
const builder *parent;
member m;
const members *ms;
builder *head;
builder *child;
void for_each(const member_closure &) const;
bool until(const member_closure_bool &) const;
size_t count(const member_closure_bool &) const;
size_t count() const;
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(const builder *const &parent, const members *const &);
builder(const builder *const &parent, member);
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 &);
};
inline ircd::string_view
ircd::json::stringify(mutable_buffer &head,
const builder &builder)
struct ircd::json::builder::push
:private ircd::json::builder
{
const auto num{builder.count()};
const member *m[num];
size_t i(0);
builder.for_each([&i, &m]
(const auto &member)
push(builder &head, const members &ms)
:builder{{}, &ms, &head}
{
m[i++] = &member;
});
tail(&head)->child = this;
}
return stringify(head, m, m + num);
}
inline
ircd::json::builder::builder(const builder *const &parent,
member m)
:parent{parent}
,m{std::move(m)}
,ms{nullptr}
{
}
inline
ircd::json::builder::builder(const builder *const &parent,
const members *const &ms)
:parent{parent}
,m{}
,ms{ms}
{
}
inline const ircd::json::value &
ircd::json::builder::at(const string_view &key)
const
{
const auto *const member(find(key));
if(!member)
throw not_found("'%s'", key);
return member->second;
}
inline size_t
ircd::json::builder::count()
const
{
return count([]
(const auto &)
push(builder &head, member m)
:builder{std::move(m), nullptr, &head}
{
return true;
});
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 size_t
ircd::json::builder::count(const member_closure_bool &closure)
const
inline const ircd::json::builder *
ircd::json::find(const builder *builder,
const builder_closure_bool &test)
{
size_t ret(0);
for_each([&closure, &ret]
(const auto &member)
{
ret += closure(member);
});
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::member *
ircd::json::builder::find(const string_view &key)
const
inline const ircd::json::builder *
ircd::json::tail(const builder *ret)
{
const member *ret;
const auto test
{
[&key, &ret](const auto &member)
{
if(key == string_view{member.first})
{
ret = &member;
return false;
}
else return true;
}
};
while(ret && next(ret))
ret = next(ret);
return !until(test)? ret : nullptr;
return ret;
}
inline void
ircd::json::builder::for_each(const member_closure &closure)
const
inline ircd::json::builder *
ircd::json::prev(builder *const &builder)
{
if(ms)
std::for_each(begin(*ms), end(*ms), closure);
else
closure(m);
assert(builder);
auto *ret(builder->head);
for(; ret; ret = next(ret))
if(next(ret) == builder)
return ret;
if(parent)
parent->for_each(closure);
return nullptr;
}
inline bool
ircd::json::builder::until(const member_closure_bool &closure)
const
inline const ircd::json::builder *
ircd::json::prev(const builder *const &builder)
{
if(ms)
{
if(!ircd::until(begin(*ms), end(*ms), closure))
return false;
}
else if(!closure(m))
return false;
assert(builder);
const auto *ret(builder->head);
for(; ret; ret = next(ret))
if(next(ret) == builder)
return ret;
if(parent)
return parent->until(closure);
return true;
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;
}

View file

@ -51,10 +51,15 @@ namespace json {
// ircd::json::tuple template. Create your own struct inheriting this
// class template with the members.
//
struct tuple_base
{
// EBO tag
};
template<class... T>
struct tuple
:std::tuple<T...>
,tuple_base
{
using tuple_type = std::tuple<T...>;
using super_type = tuple<T...>;
@ -62,10 +67,21 @@ struct tuple
static constexpr size_t size();
tuple(const json::object &);
tuple(const json::builder &);
tuple(const std::initializer_list<member> &);
tuple() = default;
};
template<class tuple>
constexpr bool
is_tuple()
{
return std::is_base_of<tuple_base, tuple>::value;
}
template<class tuple, class R>
using enable_if_tuple = typename std::enable_if<is_tuple<tuple>(), R>::type;
template<class tuple>
using tuple_type = typename tuple::tuple_type;
@ -102,7 +118,7 @@ stdcast(tuple &o)
}
template<class tuple>
constexpr size_t
constexpr enable_if_tuple<tuple, size_t>
size()
{
return tuple_size<tuple>::value;
@ -110,18 +126,18 @@ size()
template<class tuple,
size_t i>
constexpr auto &
constexpr enable_if_tuple<tuple, const char *const &>
key()
{
return tuple_element<tuple, i>::key;
}
template<size_t i,
class... T>
auto &
key(const tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, const char *const &>
key(const tuple &t)
{
return get<i>(t).key;
return std::get<i>(t).key;
}
template<class tuple,
@ -167,65 +183,63 @@ indexof(const string_view &name)
}
template<size_t i,
class... T>
auto &
get(const tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
get(tuple &t)
{
return std::get<i>(t);
}
template<size_t i,
class... T>
auto &
get(tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
get(const tuple &t)
{
return std::get<i>(t);
}
template<size_t i,
class... T>
auto &
val(const tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
val(const tuple &t)
{
using value_type = tuple_value_type<tuple<T...>, i>;
using value_type = tuple_value_type<tuple, i>;
return static_cast<const value_type &>(get<i>(t));
}
template<size_t i,
class... T>
auto &
val(tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
val(tuple &t)
{
using value_type = tuple_value_type<tuple<T...>, i>;
using value_type = tuple_value_type<tuple, i>;
return static_cast<value_type &>(get<i>(t));
}
template<const char *const &name,
class... T>
auto &
val(const tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
val(const tuple &t)
{
return val<indexof<tuple<T...>>(name)>(t);
return val<indexof<tuple>(name)>(t);
}
template<const char *const &name,
class... T>
auto &
val(tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
val(tuple &t)
{
return val<indexof<tuple<T...>>(name)>(t);
return val<indexof<tuple>(name)>(t);
}
template<const char *const &name,
class... T>
const tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> &
at(const tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
at(const tuple &t)
{
constexpr size_t idx
{
indexof<tuple<T...>>(name)
indexof<tuple>(name)
};
auto &ret
@ -233,7 +247,7 @@ at(const tuple<T...> &t)
val<idx>(t)
};
using value_type = tuple_value_type<tuple<T...>, idx>;
using value_type = tuple_value_type<tuple, idx>;
//TODO: does tcmalloc zero this or huh?
if(ret == value_type{})
@ -243,13 +257,13 @@ at(const tuple<T...> &t)
}
template<const char *const &name,
class... T>
tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> &
at(tuple<T...> &t)
class tuple>
enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
at(tuple &t)
{
constexpr size_t idx
{
indexof<tuple<T...>>(name)
indexof<tuple>(name)
};
auto &ret
@ -257,7 +271,7 @@ at(tuple<T...> &t)
val<idx>(t)
};
using value_type = tuple_value_type<tuple<T...>, idx>;
using value_type = tuple_value_type<tuple, idx>;
//TODO: does tcmalloc zero this or huh?
if(ret == value_type{})
@ -565,6 +579,31 @@ tuple<T...>::tuple(const json::object &object)
});
}
template<class... T>
tuple<T...>::tuple(const json::builder &builder)
{
//TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that?
for_each(builder, [this]
(const auto &member)
{
at(*this, member.first, [&member]
(auto &target)
{
using target_type = decltype(target);
using cast_type = typename std::remove_reference<target_type>::type; try
{
target = static_cast<cast_type>(member.second);
}
catch(const bad_lex_cast &e)
{
throw parse_error("member '%s' must convert to '%s'",
member.first,
typeid(target_type).name());
}
});
});
}
template<class... T>
tuple<T...>::tuple(const std::initializer_list<member> &members)
{
@ -748,7 +787,7 @@ template<class... T>
std::ostream &
operator<<(std::ostream &s, const tuple<T...> &t)
{
s << string(t);
s << json::string(t);
return s;
}

View file

@ -57,6 +57,8 @@ struct ircd::json::value
bool empty() const;
size_t serialized() const;
operator string_view() const;
explicit operator double() const;
explicit operator int64_t() const;
explicit operator std::string() const;
template<class T> explicit value(const T &specialized);

View file

@ -443,6 +443,13 @@ ircd::json::stringify(mutable_buffer &buf,
return stringify(buf, begin, end);
}
ircd::string_view
ircd::json::stringify(mutable_buffer &buf,
const member &m)
{
return stringify(buf, &m, &m + 1);
}
ircd::string_view
ircd::json::stringify(mutable_buffer &buf,
const member *const &begin,
@ -992,6 +999,46 @@ const
throw type_error("value type[%d] is not a string", int(type));
}
ircd::json::value::operator int64_t()
const
{
switch(type)
{
case NUMBER:
return likely(!floats)? integer : floating;
case STRING:
return lex_cast<int64_t>(string_view(*this));
case ARRAY:
case OBJECT:
case LITERAL:
break;
}
throw type_error("value type[%d] is not an int64_t", int(type));
}
ircd::json::value::operator double()
const
{
switch(type)
{
case NUMBER:
return likely(floats)? floating : integer;
case STRING:
return lex_cast<double>(string_view(*this));
case ARRAY:
case OBJECT:
case LITERAL:
break;
}
throw type_error("value type[%d] is not a float", int(type));
}
bool
ircd::json::value::empty()
const
@ -1364,3 +1411,223 @@ ircd::json::type(const string_view &buf,
return ret;
}
///////////////////////////////////////////////////////////////////////////////
//
// builder.h
//
ircd::string_view
ircd::json::stringify(mutable_buffer &head,
const builder &builder)
{
const auto num{count(builder)};
const member *m[num];
size_t i(0);
for_each(builder, [&i, &m]
(const auto &member)
{
m[i++] = &member;
});
return stringify(head, m, m + num);
}
size_t
ircd::json::count(const builder &builder)
{
return count(builder, []
(const auto &)
{
return true;
});
}
size_t
ircd::json::count(const builder &builder,
const member_closure_bool &closure)
{
size_t ret(0);
for_each(builder, [&closure, &ret]
(const auto &member)
{
ret += closure(member);
});
return ret;
}
void
ircd::json::for_each(const builder &b,
const member_closure &closure)
{
if(b.ms)
std::for_each(begin(*b.ms), end(*b.ms), closure);
else if(!b.m.first.empty())
closure(b.m);
if(b.child)
for_each(*b.child, closure);
}
bool
ircd::json::until(const builder &b,
const member_closure_bool &closure)
{
if(b.ms)
{
if(!ircd::until(begin(*b.ms), end(*b.ms), closure))
return false;
}
else if(!b.m.first.empty() && !closure(b.m))
return false;
if(b.child)
return until(*b.child, closure);
return true;
}
bool
ircd::json::builder::has(const string_view &key)
const
{
const auto *const member(find(key));
return member != nullptr;
}
const ircd::json::value &
ircd::json::builder::at(const string_view &key)
const
{
const auto *const member(find(key));
if(!member)
throw not_found("'%s'", key);
return member->second;
}
const ircd::json::member *
ircd::json::builder::find(const string_view &key)
const
{
const member *ret;
const auto test
{
[&key, &ret](const auto &member) -> bool
{
if(key == string_view{member.first})
{
ret = &member;
return false;
}
else return true;
}
};
return !until(*this, test)? ret : nullptr;
}
bool
ircd::json::builder::test(const member_closure_bool &closure)
const
{
if(ms)
return ircd::until(begin(*ms), end(*ms), closure);
if(!m.first.empty())
return closure(m);
return true;
}
ircd::json::builder::add::add(builder &head, const members &ms)
:builder{{}, &ms, &head}
{
const auto existing(json::find(&head, [&ms](const builder &existing)
{
return existing.test([&ms](const member &existing)
{
return std::all_of(begin(ms), end(ms), [&existing]
(const auto &member)
{
return member.first == existing.first;
});
});
}));
if(existing)
throw exists("failed to add member '%s': already exists",
string_view{existing->m.first}); //TODO BUG
tail(&head)->child = this;
}
ircd::json::builder::add::add(builder &head, member member)
:builder{std::move(member), nullptr, &head}
{
const auto existing(json::find(&head, [&member](const builder &existing)
{
return existing.test([&member](const auto &existing)
{
return member.first == existing.first;
});
}));
if(existing)
throw exists("failed to add member '%s': already exists",
string_view{member.first});
tail(&head)->child = this;
}
ircd::json::builder::set::set(builder &head, const members &ms)
:builder{{}, &ms, &head}
{
const auto existing(json::find(&head, [&ms](const builder &existing)
{
return existing.test([&ms](const member &existing)
{
return std::all_of(begin(ms), end(ms), [&existing]
(const auto &member)
{
return member.first == existing.first;
});
});
}));
if(existing)
{
const auto p(prev(existing));
if(p)
{
p->child = this;
this->child = existing->child;
}
}
else tail(&head)->child = this;
}
ircd::json::builder::set::set(builder &head, member member)
:builder{std::move(member), nullptr, &head}
{
const auto existing(json::find(&head, [&member](const builder &existing)
{
return existing.test([&member](const auto &existing)
{
return member.first == existing.first;
});
}));
if(existing)
{
const auto p(prev(existing));
if(p)
{
p->child = this;
this->child = existing->child;
}
}
else tail(&head)->child = this;
}

View file

@ -248,14 +248,33 @@ namespace ircd::m::events
ircd::database *ircd::m::events::events;
void
ircd::m::events::insert(const event &a)
ircd::m::events::insert(json::builder &builder)
{
event b(a);
insert(b);
const id::event::buf generated_event_id
{
builder.has("event_id")? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
};
const json::builder::add event_id
{
builder, { "event_id", generated_event_id }
};
const json::builder::set event_id2
{
builder, { "event_id", generated_event_id }
};
const json::builder::add origin_server_ts
{
builder, { "origin_server_ts", time<milliseconds>() }
};
insert(event{builder});
}
void
ircd::m::events::insert(event &event)
ircd::m::events::insert(const event &event)
{
if(!json::val<name::type>(event))
throw BAD_JSON("Required event field: '%s'", name::type);
@ -263,32 +282,11 @@ ircd::m::events::insert(event &event)
if(!json::val<name::sender>(event))
throw BAD_JSON("Required event field: '%s'", name::sender);
bool has_event_id
{
!json::val<name::event_id>(event).empty()
};
if(!json::val<name::event_id>(event))
throw BAD_JSON("Required event field: '%s'", name::event_id);
const id::event::buf generated_event_id
{
has_event_id? id::event::buf{} : id::event::buf{id::generate, "cdc.z"}
};
if(!has_event_id)
json::val<name::event_id>(event) = generated_event_id;
const scope remove_our_event_id([&]
{
if(!has_event_id)
json::val<name::event_id>(event) = {};
});
bool has_origin_server_ts
{
json::val<name::origin_server_ts>(event) != 0
};
if(!has_origin_server_ts)
json::val<name::origin_server_ts>(event) = time<milliseconds>();
if(!json::val<name::origin_server_ts>(event))
throw BAD_JSON("Required event field: '%s'", name::origin_server_ts);
for(const auto &transition : events::transitions)
if(!transition->valid(event))
@ -410,20 +408,19 @@ ircd::m::events::write(const event &event)
void
ircd::m::room::join(const m::id::user &user_id,
const json::builder &content)
json::builder &content)
{
const json::builder content_with_membership
json::builder::set membership_join
{
&content,
{ "membership", "join" }
content, { "membership", "join" }
};
membership(user_id, content_with_membership);
membership(user_id, content);
}
void
ircd::m::room::membership(const m::id::user &user_id,
const json::builder &content)
json::builder &content)
{
if(is_member(user_id, content.at("membership")))
throw m::ALREADY_MEMBER
@ -431,15 +428,23 @@ ircd::m::room::membership(const m::id::user &user_id,
"Already a member with this membership."
};
char cbuf[512];
m::events::insert(m::event
char buffer[512];
const auto printed_content
{
{ "room_id", room_id },
{ "type", "m.room.member" },
{ "state_key", user_id },
{ "sender", user_id },
{ "content", stringify(cbuf, content) }
});
stringify(buffer, content)
};
json::builder event;
json::builder::set fields{event,
{
{ "room_id", room_id },
{ "type", "m.room.member" },
{ "state_key", user_id },
{ "sender", user_id },
{ "content", printed_content }
}};
m::events::insert(event);
}
bool