mirror of
https://github.com/matrix-construct/construct
synced 2024-06-02 18:18:56 +02:00
ircd::json: Use ircd::iov for json::iov.
This commit is contained in:
parent
f8fc03e57b
commit
6a5159499f
|
@ -22,210 +22,124 @@
|
|||
#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 &);
|
||||
}
|
||||
|
||||
/// A forward list to compose JSON efficiently on the stack.
|
||||
///
|
||||
/// The IOV gathers members for a JSON object being assembled from various
|
||||
/// sources and presents an iteration to a generator. This prevents the need
|
||||
/// for multiple generations and copying to occur before the final JSON is
|
||||
/// realized, if ever.
|
||||
///
|
||||
/// Add and remove items on the IOV by construction and destruction one of
|
||||
/// the node objects. The IOV has a standard forward list interface, only use
|
||||
/// that to observe and sort/rearrange the IOV. Do not add or remove things
|
||||
/// that way.
|
||||
///
|
||||
/// Nodes support a single member each. To support initializer_list syntax
|
||||
/// the iov allocates and internally manages the iov node that should have
|
||||
/// been on your stack.
|
||||
///
|
||||
struct ircd::json::iov
|
||||
:ircd::iov<ircd::json::member>
|
||||
{
|
||||
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;
|
||||
struct push;
|
||||
struct add;
|
||||
struct add_if;
|
||||
struct set;
|
||||
struct set_if;
|
||||
|
||||
bool test(const member_closure_bool &) const;
|
||||
private:
|
||||
std::forward_list<node> allocated;
|
||||
|
||||
// recursive!
|
||||
const member *find(const string_view &key) const;
|
||||
const json::value &at(const string_view &key) const;
|
||||
public:
|
||||
bool has(const string_view &key) const;
|
||||
const value &at(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)}
|
||||
{}
|
||||
iov() = default;
|
||||
iov(member);
|
||||
iov(members);
|
||||
|
||||
friend string_view stringify(mutable_buffer &, const iov &);
|
||||
friend std::ostream &operator<<(std::ostream &, const iov &);
|
||||
friend size_t serialized(const iov &);
|
||||
};
|
||||
|
||||
struct ircd::json::iov::push
|
||||
:private ircd::json::iov
|
||||
:protected ircd::json::iov::node
|
||||
{
|
||||
template<class... args>
|
||||
push(iov &head, args&&... a)
|
||||
:iov{&head, nullptr, std::forward<args>(a)...}
|
||||
{
|
||||
tail(&head)->child = this;
|
||||
}
|
||||
push(iov &iov, args&&... a)
|
||||
:node{iov, std::forward<args>(a)...}
|
||||
{}
|
||||
};
|
||||
|
||||
struct ircd::json::iov::add
|
||||
:private ircd::json::iov
|
||||
:protected ircd::json::iov::node
|
||||
{
|
||||
add(iov &head, const members &ms);
|
||||
add(iov &head, member member);
|
||||
add(iov &, member);
|
||||
add() = default;
|
||||
};
|
||||
|
||||
struct ircd::json::iov::add_if
|
||||
:ircd::json::iov::add
|
||||
{
|
||||
add_if(iov &, const bool &, member);
|
||||
add_if() = default;
|
||||
};
|
||||
|
||||
struct ircd::json::iov::set
|
||||
:private ircd::json::iov
|
||||
:protected ircd::json::iov::node
|
||||
{
|
||||
set(iov &head, const members &ms);
|
||||
set(iov &head, member member);
|
||||
set(iov &, member);
|
||||
set() = default;
|
||||
};
|
||||
|
||||
inline ircd::json::iov *
|
||||
ircd::json::find(iov *iov,
|
||||
const iov_closure_bool &test)
|
||||
struct ircd::json::iov::set_if
|
||||
:ircd::json::iov::set
|
||||
{
|
||||
for(; iov; iov = next(iov))
|
||||
if(test(*iov))
|
||||
return iov;
|
||||
set_if(iov &, const bool &, member);
|
||||
set_if() = default;
|
||||
};
|
||||
|
||||
return nullptr;
|
||||
inline
|
||||
ircd::json::iov::iov(json::member m)
|
||||
{
|
||||
allocated.emplace_front(*this, std::move(m));
|
||||
}
|
||||
|
||||
inline const ircd::json::iov *
|
||||
ircd::json::find(const iov *iov,
|
||||
const iov_closure_bool &test)
|
||||
inline
|
||||
ircd::json::iov::iov(members m)
|
||||
{
|
||||
for(; iov; iov = next(iov))
|
||||
if(test(*iov))
|
||||
return iov;
|
||||
|
||||
return nullptr;
|
||||
for(auto&& member : m)
|
||||
allocated.emplace_front(*this, std::move(member));
|
||||
}
|
||||
|
||||
inline ircd::json::iov *
|
||||
ircd::json::tail(iov *ret)
|
||||
inline size_t
|
||||
ircd::json::serialized(const iov &iov)
|
||||
{
|
||||
while(ret && next(ret))
|
||||
ret = next(ret);
|
||||
const size_t ret
|
||||
{
|
||||
1U + !iov.empty()
|
||||
};
|
||||
|
||||
return ret;
|
||||
return std::accumulate(std::begin(iov), std::end(iov), ret, []
|
||||
(auto ret, const auto &member)
|
||||
{
|
||||
return ret += serialized(member);
|
||||
});
|
||||
}
|
||||
|
||||
inline const ircd::json::iov *
|
||||
ircd::json::tail(const iov *ret)
|
||||
inline std::ostream &
|
||||
ircd::json::operator<<(std::ostream &s, const iov &iov)
|
||||
{
|
||||
while(ret && next(ret))
|
||||
ret = next(ret);
|
||||
|
||||
return ret;
|
||||
s << string(iov);
|
||||
return s;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -580,7 +580,7 @@ template<class... T>
|
|||
tuple<T...>::tuple(const json::iov &iov)
|
||||
{
|
||||
//TODO: is tcmalloc zero-initializing all tuple elements, or is something else doing that?
|
||||
for_each(iov, [this]
|
||||
std::for_each(std::begin(iov), std::end(iov), [this]
|
||||
(const auto &member)
|
||||
{
|
||||
switch(type(member.second))
|
||||
|
|
224
ircd/json.cc
224
ircd/json.cc
|
@ -1489,11 +1489,11 @@ ircd::string_view
|
|||
ircd::json::stringify(mutable_buffer &head,
|
||||
const iov &iov)
|
||||
{
|
||||
const auto num{count(iov)};
|
||||
const auto num{iov.size()};
|
||||
const member *m[num];
|
||||
|
||||
size_t i(0);
|
||||
for_each(iov, [&i, &m]
|
||||
std::for_each(std::begin(iov), std::end(iov), [&i, &m]
|
||||
(const auto &member)
|
||||
{
|
||||
m[i++] = &member;
|
||||
|
@ -1502,200 +1502,88 @@ ircd::json::stringify(mutable_buffer &head,
|
|||
return stringify(head, m, m + num);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::json::count(const iov &iov)
|
||||
{
|
||||
return count(iov, []
|
||||
(const auto &)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::json::count(const iov &iov,
|
||||
const member_closure_bool &closure)
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each(iov, [&closure, &ret]
|
||||
(const auto &member)
|
||||
{
|
||||
ret += closure(member);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::json::for_each(const iov &b,
|
||||
const member_closure &closure)
|
||||
{
|
||||
if(b.b)
|
||||
std::for_each(b.b, b.e, closure);
|
||||
else if(!b.m.first.empty())
|
||||
closure(b.m);
|
||||
|
||||
if(b.child)
|
||||
for_each(*b.child, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::json::until(const iov &b,
|
||||
const member_closure_bool &closure)
|
||||
{
|
||||
if(b.b && b.e)
|
||||
{
|
||||
if(!ircd::until(b.b, b.e, 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::iov::has(const string_view &key)
|
||||
const
|
||||
{
|
||||
const auto *const member(find(key));
|
||||
return member != nullptr;
|
||||
return std::any_of(std::begin(*this), std::end(*this), [&key]
|
||||
(const auto &member)
|
||||
{
|
||||
return string_view{member.first} == key;
|
||||
});
|
||||
}
|
||||
|
||||
const ircd::json::value &
|
||||
ircd::json::iov::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::iov::find(const string_view &key)
|
||||
const
|
||||
{
|
||||
const member *ret;
|
||||
const auto test
|
||||
const auto it(std::find_if(std::begin(*this), std::end(*this), [&key]
|
||||
(const auto &member)
|
||||
{
|
||||
[&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::iov::test(const member_closure_bool &closure)
|
||||
const
|
||||
{
|
||||
if(b && likely(e))
|
||||
return ircd::until(b, e, closure);
|
||||
|
||||
if(!m.first.empty())
|
||||
return closure(m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ircd::json::iov::add::add(iov &head, const members &ms)
|
||||
:iov{&head, nullptr, ms}
|
||||
{
|
||||
const auto existing(json::find(&head, [&ms](const iov &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;
|
||||
});
|
||||
});
|
||||
return string_view{member.first} == key;
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
throw exists("failed to add member '%s': already exists",
|
||||
string_view{existing->m.first}); //TODO BUG
|
||||
|
||||
tail(&head)->child = this;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ircd::json::iov::add::add(iov &head, member member)
|
||||
:iov{&head, nullptr, std::move(member)}
|
||||
ircd::json::iov::add::add(iov &iov,
|
||||
member member)
|
||||
:node
|
||||
{
|
||||
const auto existing(json::find(&head, [&member](const iov &existing)
|
||||
iov, [&iov, &member]
|
||||
{
|
||||
return existing.test([&member](const auto &existing)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
});
|
||||
}));
|
||||
if(iov.has(member.first))
|
||||
throw exists("failed to add member '%s': already exists",
|
||||
string_view{member.first});
|
||||
|
||||
if(existing)
|
||||
throw exists("failed to add member '%s': already exists",
|
||||
string_view{member.first});
|
||||
|
||||
tail(&head)->child = this;
|
||||
return std::move(member);
|
||||
}()
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::iov::set::set(iov &head, const members &ms)
|
||||
:iov{&head, nullptr, ms}
|
||||
ircd::json::iov::add_if::add_if(iov &iov,
|
||||
const bool &b,
|
||||
member member)
|
||||
:add
|
||||
{
|
||||
const auto existing(json::find(&head, [&ms](const iov &existing)
|
||||
iov, std::move(member)
|
||||
}
|
||||
{
|
||||
if(!b)
|
||||
{
|
||||
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;
|
||||
}
|
||||
assert(iov.front() == member);
|
||||
iov.pop_front();
|
||||
}
|
||||
else tail(&head)->child = this;
|
||||
}
|
||||
|
||||
ircd::json::iov::set::set(iov &head, member member)
|
||||
:iov{&head, nullptr, std::move(member)}
|
||||
ircd::json::iov::set::set(iov &iov, member member)
|
||||
:node
|
||||
{
|
||||
const auto existing(json::find(&head, [&member](const iov &existing)
|
||||
iov, [&iov, &member]
|
||||
{
|
||||
return existing.test([&member](const auto &existing)
|
||||
iov.remove_if([&member](const auto &existing)
|
||||
{
|
||||
return member.first == existing.first;
|
||||
return string_view{existing.first} == string_view{member.first};
|
||||
});
|
||||
}));
|
||||
|
||||
if(existing)
|
||||
{
|
||||
const auto p(prev(existing));
|
||||
if(p)
|
||||
{
|
||||
p->child = this;
|
||||
this->child = existing->child;
|
||||
}
|
||||
}
|
||||
else tail(&head)->child = this;
|
||||
return std::move(member);
|
||||
}()
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::json::iov::set_if::set_if(iov &iov,
|
||||
const bool &b,
|
||||
member member)
|
||||
:set
|
||||
{
|
||||
iov, std::move(member)
|
||||
}
|
||||
{
|
||||
if(!b)
|
||||
{
|
||||
assert(iov.front() == member);
|
||||
iov.pop_front();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue