0
0
Fork 0
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:
Jason Volk 2017-09-12 10:03:06 -07:00
parent f8fc03e57b
commit 6a5159499f
3 changed files with 133 additions and 331 deletions

View file

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

View file

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

View file

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