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:
parent
db748cae81
commit
e95fe7fbd9
6 changed files with 554 additions and 197 deletions
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
267
ircd/json.cc
267
ircd/json.cc
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue