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/member.h"
#include "json/index.h" #include "json/index.h"
#include "json/property.h" #include "json/property.h"
#include "json/tuple.h"
#include "json/builder.h" #include "json/builder.h"
#include "json/tuple.h"
namespace ircd namespace ircd
{ {

View file

@ -22,153 +22,197 @@
#pragma once #pragma once
#define HAVE_IRCD_JSON_BUILDER_H #define HAVE_IRCD_JSON_BUILDER_H
// ircd::json::builder is an interface to compose JSON dynamically and // ircd::json::builder is forward list to compose JSON dynamically and
// efficiently. The product of the builder is an iteration of the added members // efficiently on the stack. The product of the builder is an iteration of the
// for use by stringifying and iovectoring. This gathers the members on a trip // added members for use by stringifying and iovectoring. This gathers the
// up the stack without rewriting a JSON string at each frame. // 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 = std::function<void (const member &)>;
using member_closure_bool = std::function<bool (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; member m;
const members *ms; const members *ms;
builder *head;
builder *child;
void for_each(const member_closure &) const; bool test(const member_closure_bool &) const;
bool until(const member_closure_bool &) const;
size_t count(const member_closure_bool &) const;
size_t count() const;
// recursive!
const member *find(const string_view &key) const; const member *find(const string_view &key) const;
const json::value &at(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(member m = {},
builder(const builder *const &parent, member); 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 &); friend string_view stringify(mutable_buffer &, const builder &);
}; };
inline ircd::string_view struct ircd::json::builder::push
ircd::json::stringify(mutable_buffer &head, :private ircd::json::builder
const builder &builder)
{ {
const auto num{builder.count()}; push(builder &head, const members &ms)
const member *m[num]; :builder{{}, &ms, &head}
size_t i(0);
builder.for_each([&i, &m]
(const auto &member)
{ {
m[i++] = &member; tail(&head)->child = this;
}); }
return stringify(head, m, m + num); push(builder &head, member m)
} :builder{std::move(m), nullptr, &head}
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 &)
{ {
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 inline const ircd::json::builder *
ircd::json::builder::count(const member_closure_bool &closure) ircd::json::find(const builder *builder,
const const builder_closure_bool &test)
{ {
size_t ret(0); for(; builder; builder = next(builder))
for_each([&closure, &ret] if(test(*builder))
(const auto &member) return builder;
{
ret += closure(member); return nullptr;
}); }
inline ircd::json::builder *
ircd::json::tail(builder *ret)
{
while(ret && next(ret))
ret = next(ret);
return ret; return ret;
} }
inline const ircd::json::member * inline const ircd::json::builder *
ircd::json::builder::find(const string_view &key) ircd::json::tail(const builder *ret)
const
{ {
const member *ret; while(ret && next(ret))
const auto test ret = next(ret);
{
[&key, &ret](const auto &member)
{
if(key == string_view{member.first})
{
ret = &member;
return false;
}
else return true;
}
};
return !until(test)? ret : nullptr; return ret;
} }
inline void inline ircd::json::builder *
ircd::json::builder::for_each(const member_closure &closure) ircd::json::prev(builder *const &builder)
const
{ {
if(ms) assert(builder);
std::for_each(begin(*ms), end(*ms), closure); auto *ret(builder->head);
else for(; ret; ret = next(ret))
closure(m); if(next(ret) == builder)
return ret;
if(parent) return nullptr;
parent->for_each(closure);
} }
inline bool inline const ircd::json::builder *
ircd::json::builder::until(const member_closure_bool &closure) ircd::json::prev(const builder *const &builder)
const
{ {
if(ms) assert(builder);
{ const auto *ret(builder->head);
if(!ircd::until(begin(*ms), end(*ms), closure)) for(; ret; ret = next(ret))
return false; if(next(ret) == builder)
} return ret;
else if(!closure(m))
return false;
if(parent) return nullptr;
return parent->until(closure); }
return true; 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 // ircd::json::tuple template. Create your own struct inheriting this
// class template with the members. // class template with the members.
// //
struct tuple_base
{
// EBO tag
};
template<class... T> template<class... T>
struct tuple struct tuple
:std::tuple<T...> :std::tuple<T...>
,tuple_base
{ {
using tuple_type = std::tuple<T...>; using tuple_type = std::tuple<T...>;
using super_type = tuple<T...>; using super_type = tuple<T...>;
@ -62,10 +67,21 @@ struct tuple
static constexpr size_t size(); static constexpr size_t size();
tuple(const json::object &); tuple(const json::object &);
tuple(const json::builder &);
tuple(const std::initializer_list<member> &); tuple(const std::initializer_list<member> &);
tuple() = default; 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> template<class tuple>
using tuple_type = typename tuple::tuple_type; using tuple_type = typename tuple::tuple_type;
@ -102,7 +118,7 @@ stdcast(tuple &o)
} }
template<class tuple> template<class tuple>
constexpr size_t constexpr enable_if_tuple<tuple, size_t>
size() size()
{ {
return tuple_size<tuple>::value; return tuple_size<tuple>::value;
@ -110,18 +126,18 @@ size()
template<class tuple, template<class tuple,
size_t i> size_t i>
constexpr auto & constexpr enable_if_tuple<tuple, const char *const &>
key() key()
{ {
return tuple_element<tuple, i>::key; return tuple_element<tuple, i>::key;
} }
template<size_t i, template<size_t i,
class... T> class tuple>
auto & enable_if_tuple<tuple, const char *const &>
key(const tuple<T...> &t) key(const tuple &t)
{ {
return get<i>(t).key; return std::get<i>(t).key;
} }
template<class tuple, template<class tuple,
@ -167,65 +183,63 @@ indexof(const string_view &name)
} }
template<size_t i, template<size_t i,
class... T> class tuple>
auto & enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
get(const tuple<T...> &t) get(tuple &t)
{ {
return std::get<i>(t); return std::get<i>(t);
} }
template<size_t i, template<size_t i,
class... T> class tuple>
auto & enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
get(tuple<T...> &t) get(const tuple &t)
{ {
return std::get<i>(t); return std::get<i>(t);
} }
template<size_t i, template<size_t i,
class... T> class tuple>
auto & enable_if_tuple<tuple, const tuple_value_type<tuple, i> &>
val(const tuple<T...> &t) 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)); return static_cast<const value_type &>(get<i>(t));
} }
template<size_t i, template<size_t i,
class... T> class tuple>
auto & enable_if_tuple<tuple, tuple_value_type<tuple, i> &>
val(tuple<T...> &t) 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)); return static_cast<value_type &>(get<i>(t));
} }
template<const char *const &name, template<const char *const &name,
class... T> class tuple>
auto & enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
val(const tuple<T...> &t) val(const tuple &t)
{ {
return val<indexof<tuple<T...>>(name)>(t); return val<indexof<tuple>(name)>(t);
} }
template<const char *const &name, template<const char *const &name,
class... T> class tuple>
auto & enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
val(tuple<T...> &t) val(tuple &t)
{ {
return val<indexof<tuple<T...>>(name)>(t); return val<indexof<tuple>(name)>(t);
} }
template<const char *const &name, template<const char *const &name,
class... T> class tuple>
const tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> & enable_if_tuple<tuple, const tuple_value_type<tuple, indexof<tuple>(name)> &>
at(const tuple<T...> &t) at(const tuple &t)
{ {
constexpr size_t idx constexpr size_t idx
{ {
indexof<tuple<T...>>(name) indexof<tuple>(name)
}; };
auto &ret auto &ret
@ -233,7 +247,7 @@ at(const tuple<T...> &t)
val<idx>(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? //TODO: does tcmalloc zero this or huh?
if(ret == value_type{}) if(ret == value_type{})
@ -243,13 +257,13 @@ at(const tuple<T...> &t)
} }
template<const char *const &name, template<const char *const &name,
class... T> class tuple>
tuple_value_type<tuple<T...>, indexof<tuple<T...>>(name)> & enable_if_tuple<tuple, tuple_value_type<tuple, indexof<tuple>(name)> &>
at(tuple<T...> &t) at(tuple &t)
{ {
constexpr size_t idx constexpr size_t idx
{ {
indexof<tuple<T...>>(name) indexof<tuple>(name)
}; };
auto &ret auto &ret
@ -257,7 +271,7 @@ at(tuple<T...> &t)
val<idx>(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? //TODO: does tcmalloc zero this or huh?
if(ret == value_type{}) 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> template<class... T>
tuple<T...>::tuple(const std::initializer_list<member> &members) tuple<T...>::tuple(const std::initializer_list<member> &members)
{ {
@ -748,7 +787,7 @@ template<class... T>
std::ostream & std::ostream &
operator<<(std::ostream &s, const tuple<T...> &t) operator<<(std::ostream &s, const tuple<T...> &t)
{ {
s << string(t); s << json::string(t);
return s; return s;
} }

View file

@ -57,6 +57,8 @@ struct ircd::json::value
bool empty() const; bool empty() const;
size_t serialized() const; size_t serialized() const;
operator string_view() const; operator string_view() const;
explicit operator double() const;
explicit operator int64_t() const;
explicit operator std::string() const; explicit operator std::string() const;
template<class T> explicit value(const T &specialized); 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); 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::string_view
ircd::json::stringify(mutable_buffer &buf, ircd::json::stringify(mutable_buffer &buf,
const member *const &begin, const member *const &begin,
@ -992,6 +999,46 @@ const
throw type_error("value type[%d] is not a string", int(type)); 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 bool
ircd::json::value::empty() ircd::json::value::empty()
const const
@ -1364,3 +1411,223 @@ ircd::json::type(const string_view &buf,
return ret; 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; ircd::database *ircd::m::events::events;
void void
ircd::m::events::insert(const event &a) ircd::m::events::insert(json::builder &builder)
{ {
event b(a); const id::event::buf generated_event_id
insert(b); {
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 void
ircd::m::events::insert(event &event) ircd::m::events::insert(const event &event)
{ {
if(!json::val<name::type>(event)) if(!json::val<name::type>(event))
throw BAD_JSON("Required event field: '%s'", name::type); 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)) if(!json::val<name::sender>(event))
throw BAD_JSON("Required event field: '%s'", name::sender); throw BAD_JSON("Required event field: '%s'", name::sender);
bool has_event_id if(!json::val<name::event_id>(event))
{ throw BAD_JSON("Required event field: '%s'", name::event_id);
!json::val<name::event_id>(event).empty()
};
const id::event::buf generated_event_id if(!json::val<name::origin_server_ts>(event))
{ throw BAD_JSON("Required event field: '%s'", name::origin_server_ts);
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>();
for(const auto &transition : events::transitions) for(const auto &transition : events::transitions)
if(!transition->valid(event)) if(!transition->valid(event))
@ -410,20 +408,19 @@ ircd::m::events::write(const event &event)
void void
ircd::m::room::join(const m::id::user &user_id, 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, content, { "membership", "join" }
{ "membership", "join" }
}; };
membership(user_id, content_with_membership); membership(user_id, content);
} }
void void
ircd::m::room::membership(const m::id::user &user_id, 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"))) if(is_member(user_id, content.at("membership")))
throw m::ALREADY_MEMBER throw m::ALREADY_MEMBER
@ -431,15 +428,23 @@ ircd::m::room::membership(const m::id::user &user_id,
"Already a member with this membership." "Already a member with this membership."
}; };
char cbuf[512]; char buffer[512];
m::events::insert(m::event const auto printed_content
{ {
{ "room_id", room_id }, stringify(buffer, content)
{ "type", "m.room.member" }, };
{ "state_key", user_id },
{ "sender", user_id }, json::builder event;
{ "content", stringify(cbuf, content) } 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 bool