0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-29 20:28:52 +02:00

ircd:Ⓜ️:state: Add a DFS with closure; comments; cleanup.

This commit is contained in:
Jason Volk 2018-02-04 13:11:26 -08:00
parent 10f6402ada
commit dc361284f0
2 changed files with 168 additions and 86 deletions

View file

@ -11,13 +11,24 @@
#pragma once
#define HAVE_IRCD_M_STATE_H
/// Matrix state machine unit and bus.
///
/// This section deals specifically with the aspect of Matrix called "state"
/// providing tools and utilities as well as local databasing. IO is done for
/// reads, and indirect into db::txn's for writes. No network activities are
/// conducted here.
///
/// These tools allow the user to query aspects of the "state" of a room at
/// the point of any event. Composed out of these queries are a suite of more
/// utilities to efficiently aid the Matrix virtual machine with the rest of
/// its tasks.
///
namespace ircd::m::state
{
struct node;
using id_closure = std::function<void (const string_view &)>;
using node_closure = std::function<void (const json::object &)>;
using key_closure = std::function<void (const json::array &)>;
constexpr size_t ID_MAX_SZ { 64 };
constexpr size_t KEY_MAX_SZ { 256 + 256 + 16 };
@ -40,17 +51,21 @@ namespace ircd::m::state
string_view get_head(const mutable_buffer &out, const id::room &);
void set_head(db::txn &txn, const id::room &, const string_view &head);
void get_value(db::column &, const string_view &head, const json::array &key, const id_closure &);
void get_value(const string_view &head, const json::array &key, const id_closure &);
void get_value(const string_view &head, const string_view &type, const string_view &state_key, const id_closure &);
void get_value__room(const id::room &, const string_view &type, const string_view &state_key, const id_closure &);
void insert(db::txn &txn, const id::room &, const json::array &key, const id::event &);
void insert(db::txn &txn, const id::room &, const string_view &type, const string_view &state_key, const id::event &);
void append_nodes(db::txn &, const event &);
using search_closure = std::function<bool (const json::array &, const string_view &, const uint &, const uint &)>;
bool dfs(db::column &, const string_view &node_id, const search_closure &);
bool dfs(const string_view &node_id, const search_closure &);
void get(db::column &, const string_view &head, const json::array &key, const id_closure &);
void get(const string_view &head, const json::array &key, const id_closure &);
void get(const string_view &head, const string_view &type, const string_view &state_key, const id_closure &);
void get__room(const id::room &, const string_view &type, const string_view &state_key, const id_closure &);
}
/// JSON property name strings specifically for use in m::state
namespace ircd::m::state::name
{
constexpr const char *const k {"k"};

View file

@ -10,6 +10,152 @@
#include <ircd/m/m.h>
/// Convenience to get value from the current room head.
void
ircd::m::state::get__room(const id::room &room_id,
const string_view &type,
const string_view &state_key,
const id_closure &closure)
{
char head[ID_MAX_SZ];
return get(get_head(head, room_id), type, state_key, closure);
}
/// Convenience to get value making a key
void
ircd::m::state::get(const string_view &head,
const string_view &type,
const string_view &state_key,
const id_closure &closure)
{
char key[KEY_MAX_SZ];
return get(head, make_key(key, type, state_key), closure);
}
/// see: get(); user does not have to supply column reference here
void
ircd::m::state::get(const string_view &head,
const json::array &key,
const id_closure &closure)
{
db::column column
{
*event::events, "state_node"
};
get(column, head, key, closure);
}
/// Recursive query to find the leaf value for the given key, starting from
/// the given head node ID. Value can be viewed in the closure. This throws
/// m::NOT_FOUND if the exact key and its value does not exist in the tree;
/// no node ID's are ever returned here.
void
ircd::m::state::get(db::column &column,
const string_view &head,
const json::array &key,
const id_closure &closure)
{
char nextbuf[ID_MAX_SZ];
string_view nextid{head};
while(nextid) get_node(column, nextid, [&](const node &node)
{
auto pos(node.find(key));
if(pos < node.keys() && node.key(pos) == key)
{
nextid = {};
closure(node.val(pos));
return;
}
const auto c(node.childs());
if(c && pos >= c)
pos = c - 1;
if(!node.has_child(pos))
throw m::NOT_FOUND{};
nextid = { nextbuf, strlcpy(nextbuf, node.child(pos)) };
});
}
bool
ircd::m::state::dfs(const string_view &node_id,
const search_closure &closure)
{
db::column column
{
*event::events, "state_node"
};
return dfs(column, node_id, closure);
}
bool
ircd::m::state::dfs(db::column &column,
const string_view &node_id,
const search_closure &closure)
{
int depth(-1);
const std::function<bool (const node &)> recurse{[&depth, &closure, &column, &recurse]
(const node &node)
{
++depth;
const unwind down{[&depth]
{
--depth;
}};
const node::rep rep{node};
for(uint pos(0); pos < rep.kn || pos < rep.cn; ++pos)
{
const auto child{unquote(rep.chld[pos])};
if(!empty(child))
{
bool ret{false};
get_node(column, child, [&ret, &recurse]
(const auto &node)
{
ret = recurse(node);
});
if(ret)
return true;
}
if(rep.kn > pos)
{
const auto &key{rep.keys[pos]};
const auto &val{unquote(rep.vals[pos])};
if(closure(key, val, depth, pos))
return true;
}
}
return false;
}};
bool ret{false};
get_node(column, node_id, [&ret, &recurse]
(const auto &node)
{
ret = recurse(node);
});
return ret;
}
// Internal insertion operations
namespace ircd::m::state
{
string_view _insert_overwrite(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos);
string_view _insert_leaf_nonfull(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos);
json::object _insert_leaf_full(db::txn &txn, const json::array &key, const string_view &val, node::rep &rep, const size_t &pos, node::rep &push, const mutable_buffer &pushbuf);
string_view _insert_branch_nonfull(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos, node::rep &pushed);
json::object _insert_branch_full(db::txn &txn, const json::array &key, const string_view &val, node::rep &rep, const size_t &pos, node::rep &push, const mutable_buffer &pushbuf);
string_view _insert(int8_t &height, db::txn &txn, const json::array &key, const string_view &val, const node &node, const mutable_buffer &idbuf, node::rep &push, const mutable_buffer &pushbuf);
}
void
ircd::m::state::append_nodes(db::txn &txn,
const event &event)
@ -44,85 +190,6 @@ ircd::m::state::append_nodes(db::txn &txn,
state::insert(txn, room_id, type, state_key, event_id);
}
/// Convenience to get value from the current room head.
void
ircd::m::state::get_value__room(const id::room &room_id,
const string_view &type,
const string_view &state_key,
const id_closure &closure)
{
char head[ID_MAX_SZ];
return get_value(get_head(head, room_id), type, state_key, closure);
}
/// Convenience to get value making a key
void
ircd::m::state::get_value(const string_view &head,
const string_view &type,
const string_view &state_key,
const id_closure &closure)
{
char key[KEY_MAX_SZ];
return get_value(head, make_key(key, type, state_key), closure);
}
/// see: get_value(); user does not have to supply column reference here
void
ircd::m::state::get_value(const string_view &head,
const json::array &key,
const id_closure &closure)
{
db::column column
{
*event::events, "state_node"
};
get_value(column, head, key, closure);
}
/// Recursive query to find the leaf value for the given key, starting from
/// the given head node ID. Value can be viewed in the closure. This throws
/// m::NOT_FOUND if the exact key and its value does not exist in the tree;
/// no node ID's are ever returned here.
void
ircd::m::state::get_value(db::column &column,
const string_view &head,
const json::array &key,
const id_closure &closure)
{
char nextbuf[ID_MAX_SZ];
string_view nextid{head};
while(nextid) get_node(column, nextid, [&](const node &node)
{
auto pos(node.find(key));
if(pos < node.keys() && node.key(pos) == key)
{
nextid = {};
closure(node.val(pos));
return;
}
const auto c(node.childs());
if(c && pos >= c)
pos = c - 1;
if(!node.has_child(pos))
throw m::NOT_FOUND{};
nextid = { nextbuf, strlcpy(nextbuf, node.child(pos)) };
});
}
namespace ircd::m::state
{
string_view _insert_overwrite(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos);
string_view _insert_leaf_nonfull(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos);
json::object _insert_leaf_full(db::txn &txn, const json::array &key, const string_view &val, node::rep &rep, const size_t &pos, node::rep &push, const mutable_buffer &pushbuf);
string_view _insert_branch_nonfull(db::txn &txn, const json::array &key, const string_view &val, const mutable_buffer &idbuf, node::rep &rep, const size_t &pos, node::rep &pushed);
json::object _insert_branch_full(db::txn &txn, const json::array &key, const string_view &val, node::rep &rep, const size_t &pos, node::rep &push, const mutable_buffer &pushbuf);
string_view _insert(int8_t &height, db::txn &txn, const json::array &key, const string_view &val, const node &node, const mutable_buffer &idbuf, node::rep &push, const mutable_buffer &pushbuf);
}
void
ircd::m::state::insert(db::txn &txn,
const id::room &room_id,