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:
parent
10f6402ada
commit
dc361284f0
2 changed files with 168 additions and 86 deletions
|
@ -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"};
|
||||
|
|
225
ircd/m/state.cc
225
ircd/m/state.cc
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue