From 8454ae0275d92e6cf1a58b53a77d6dee209a4c59 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sun, 18 Aug 2019 00:02:59 -0700 Subject: [PATCH] ircd::m::room::head: Simplify interface; move to module. --- include/ircd/m/room/head.h | 22 +-- ircd/m.cc | 1 + ircd/m_room.cc | 314 ------------------------------- modules/Makefile.am | 2 + modules/console.cc | 3 +- modules/federation/make_join.cc | 2 +- modules/federation/make_leave.cc | 2 +- modules/m_room_head.cc | 308 ++++++++++++++++++++++++++++++ modules/m_vm.cc | 2 +- 9 files changed, 324 insertions(+), 332 deletions(-) create mode 100644 modules/m_room_head.cc diff --git a/include/ircd/m/room/head.h b/include/ircd/m/room/head.h index 4b37510c3..7c39c7ab7 100644 --- a/include/ircd/m/room/head.h +++ b/include/ircd/m/room/head.h @@ -40,29 +40,23 @@ namespace ircd::m /// struct ircd::m::room::head { - using closure = std::function; - using closure_bool = std::function; + using closure = std::function; - static size_t reset(const head &); - static size_t rebuild(const head &); - static void modify(const event::id &, const db::op &, const bool &); - static int64_t make_refs(const head &, json::stack::array &, const size_t &, const bool &); - static bool for_each(const head &, const closure_bool &); - - public: m::room room; - bool for_each(const closure_bool &) const; - void for_each(const closure &) const; + bool for_each(const closure &) const; bool has(const event::id &) const; size_t count() const; - int64_t make_refs(json::stack::array &, const size_t &, const bool &) const; - std::pair make_refs(const mutable_buffer &, const size_t &, const bool &) const; + int64_t generate(json::stack::array &, const size_t &, const bool &) const; + std::pair generate(const mutable_buffer &, const size_t &, const bool &) const; + head() = default; head(const m::room &room) :room{room} {} - head() = default; + static void modify(const event::id &, const db::op &, const bool &); + static size_t rebuild(const head &); + static size_t reset(const head &); }; diff --git a/ircd/m.cc b/ircd/m.cc index 470f2f4a9..0ed405135 100644 --- a/ircd/m.cc +++ b/ircd/m.cc @@ -212,6 +212,7 @@ ircd::m::module_names "m_user_profile", "m_room", "m_room_auth", + "m_room_head", "m_room_timeline", "m_room_aliases", "m_room_canonical_alias", diff --git a/ircd/m_room.cc b/ircd/m_room.cc index b8ad34937..5fc7922a3 100644 --- a/ircd/m_room.cc +++ b/ircd/m_room.cc @@ -3389,320 +3389,6 @@ ircd::m::room::origins::_for_each(const origins &origins, return true; } -// -// room::head -// - -std::pair -ircd::m::room::head::make_refs(const mutable_buffer &buf, - const size_t &limit, - const bool &need_top) -const -{ - json::stack out{buf}; - json::stack::array array{out}; - const auto depth - { - make_refs(array, limit, need_top) - }; - array.~array(); - return - { - json::array{out.completed()}, - depth - }; -} - -int64_t -ircd::m::room::head::make_refs(json::stack::array &out, - const size_t &limit, - const bool &need_top) -const -{ - return make_refs(*this, out, limit, need_top); -} - -size_t -ircd::m::room::head::count() -const -{ - size_t ret(0); - for_each([&ret] - (const event::idx &event_idx, const event::id &event_id) - { - ++ret; - }); - - return ret; -} - -bool -ircd::m::room::head::has(const event::id &event_id) -const -{ - bool ret{false}; - for_each(closure_bool{[&ret, &event_id] - (const event::idx &event_idx, const event::id &event_id_) - { - ret = event_id_ == event_id; - return !ret; // for_each protocol: false to break - }}); - - return ret; -} - -void -ircd::m::room::head::for_each(const closure &closure) -const -{ - for_each(closure_bool{[&closure] - (const event::idx &event_idx, const event::id &event_id) - { - closure(event_idx, event_id); - return true; - }}); -} - -bool -ircd::m::room::head::for_each(const closure_bool &closure) -const -{ - return for_each(*this, closure); -} - -size_t -ircd::m::room::head::rebuild(const head &head) -{ - size_t ret{0}; - const auto &room{head.room}; - const m::room::state state{room}; - const auto create_idx - { - state.get("m.room.create") - }; - - static const m::event::fetch::opts fopts - { - { db::get::NO_CACHE } - }; - - m::room::messages it - { - room, create_idx, &fopts - }; - - if(!it) - return ret; - - db::txn txn - { - *m::dbs::events - }; - - m::dbs::write_opts opts; - opts.op = db::op::SET; - for(; it; ++it) - { - const m::event &event{*it}; - opts.event_idx = it.event_idx(); - opts.appendix.reset(); - opts.appendix.set(dbs::appendix::ROOM_HEAD); - m::dbs::write(txn, event, opts); - ++ret; - } - - txn(); - return ret; -} - -size_t -ircd::m::room::head::reset(const head &head) -{ - size_t ret{0}; - const auto &room{head.room}; - m::room::messages it{room}; - if(!it) - return ret; - - // Replacement will be the single new head - const m::event replacement - { - *it - }; - - db::txn txn - { - *m::dbs::events - }; - - // Iterate all of the existing heads with a delete operation - m::dbs::write_opts opts; - opts.op = db::op::DELETE; - opts.appendix.reset(); - opts.appendix.set(dbs::appendix::ROOM_HEAD); - m::room::head{room}.for_each([&room, &opts, &txn, &ret] - (const m::event::idx &event_idx, const m::event::id &event_id) - { - const m::event::fetch event - { - event_idx, std::nothrow - }; - - if(!event.valid) - { - log::derror - { - m::log, "Invalid event '%s' idx %lu in head for %s", - string_view{event_id}, - event_idx, - string_view{room.room_id} - }; - - return; - } - - opts.event_idx = event_idx; - m::dbs::write(txn, event, opts); - ++ret; - }); - - // Finally add the replacement to the txn - opts.op = db::op::SET; - opts.event_idx = it.event_idx(); - m::dbs::write(txn, replacement, opts); - - // Commit txn - txn(); - return ret; -} - -void -ircd::m::room::head::modify(const m::event::id &event_id, - const db::op &op, - const bool &refs) -{ - const m::event::fetch event - { - event_id - }; - - db::txn txn - { - *m::dbs::events - }; - - // Iterate all of the existing heads with a delete operation - m::dbs::write_opts opts; - opts.op = op; - opts.event_idx = event.event_idx; - opts.appendix.reset(); - opts.appendix.set(dbs::appendix::ROOM_HEAD); - m::dbs::write(txn, event, opts); - - // Commit txn - txn(); -} - -int64_t -ircd::m::room::head::make_refs(const head &head, - json::stack::array &out, - const size_t &_limit, - const bool &_need_tophead) -{ - const m::event::id::closure &v1_ref{[&out] - (const auto &event_id) - { - json::stack::array prev{out}; - prev.append(event_id); - { - json::stack::object nilly{prev}; - json::stack::member willy - { - nilly, "", "" - }; - } - }}; - - const m::event::id::closure &v3_ref{[&out] - (const auto &event_id) - { - out.append(event_id); - }}; - - char versionbuf[32]; - const auto version - { - m::version(versionbuf, head.room, std::nothrow) - }; - - const auto &append - { - version == "1" || version == "2"? v1_ref : v3_ref - }; - - bool need_tophead{_need_tophead}; - const auto top_head - { - need_tophead? - m::top(std::nothrow, head.room.room_id): - std::tuple{} - }; - - size_t limit{_limit}; - int64_t depth{-1}; - m::event::fetch event; - head.for_each(m::room::head::closure_bool{[&] - (const m::event::idx &idx, const m::event::id &event_id) - { - if(!seek(event, idx, event_id, std::nothrow)) - return true; - - if(need_tophead) - if(event.event_id == std::get<0>(top_head)) - need_tophead = false; - - append(event_id); - depth = std::max(json::get<"depth"_>(event), depth); - return --limit - need_tophead > 0; - }}); - - if(need_tophead) - { - append(std::get<0>(top_head)); - depth = std::get<1>(top_head); - } - - return depth; -} - -bool -ircd::m::room::head::for_each(const head &head, - const closure_bool &closure) -{ - auto it - { - dbs::room_head.begin(head.room.room_id) - }; - - for(; it; ++it) - { - const event::id &event_id - { - dbs::room_head_key(it->first) - }; - - const event::idx &event_idx - { - byte_view{it->second} - }; - - if(!closure(event_idx, event_id)) - return false; - } - - return true; -} - // // room::aliases // diff --git a/modules/Makefile.am b/modules/Makefile.am index 563f383ca..b7038f557 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -130,6 +130,7 @@ m_rooms_la_SOURCES = m_rooms.cc m_rooms_summary_la_SOURCES = m_rooms_summary.cc m_room_la_SOURCES = m_room.cc m_room_auth_la_SOURCES = m_room_auth.cc +m_room_head_la_SOURCES = m_room_head.cc m_room_timeline_la_SOURCES = m_room_timeline.cc m_room_create_la_SOURCES = m_room_create.cc m_room_member_la_SOURCES = m_room_member.cc @@ -181,6 +182,7 @@ m_module_LTLIBRARIES = \ m_rooms_summary.la \ m_room.la \ m_room_auth.la \ + m_room_head.la \ m_room_timeline.la \ m_room_create.la \ m_room_member.la \ diff --git a/modules/console.cc b/modules/console.cc index c6d5ec27f..8d24e6c40 100644 --- a/modules/console.cc +++ b/modules/console.cc @@ -5881,7 +5881,7 @@ console_cmd__stage__make_prev(opt &out, const string_view &line) thread_local char buf[8192]; const auto prev { - head.make_refs(buf, limit, true) + head.generate(buf, limit, true) }; json::get<"prev_events"_>(event) = prev.first; @@ -7819,6 +7819,7 @@ console_cmd__room__head(opt &out, const string_view &line) }; out << pretty_oneline(event) << std::endl; + return true; }); return true; diff --git a/modules/federation/make_join.cc b/modules/federation/make_join.cc index 7831d6e0b..94d8e0c69 100644 --- a/modules/federation/make_join.cc +++ b/modules/federation/make_join.cc @@ -181,7 +181,7 @@ get__make_join(client &client, event, "prev_events" }; - head.make_refs(prev_events, 32, true); + head.generate(prev_events, 32, true); } json::stack::member diff --git a/modules/federation/make_leave.cc b/modules/federation/make_leave.cc index 2f1b3113b..e66e41736 100644 --- a/modules/federation/make_leave.cc +++ b/modules/federation/make_leave.cc @@ -152,7 +152,7 @@ get__make_leave(client &client, event, "prev_events" }; - head.make_refs(prev_events, 32, true); + head.generate(prev_events, 32, true); } json::stack::member diff --git a/modules/m_room_head.cc b/modules/m_room_head.cc new file mode 100644 index 000000000..d1ccbd185 --- /dev/null +++ b/modules/m_room_head.cc @@ -0,0 +1,308 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +ircd::mapi::header +IRCD_MODULE +{ + "Matrix room head" +}; + +// +// room::head +// + +std::pair +IRCD_MODULE_EXPORT +ircd::m::room::head::generate(const mutable_buffer &buf, + const size_t &limit, + const bool &need_top) +const +{ + json::stack out{buf}; + json::stack::array array{out}; + const auto depth + { + generate(array, limit, need_top) + }; + array.~array(); + return + { + json::array{out.completed()}, + depth + }; +} + +int64_t +IRCD_MODULE_EXPORT +ircd::m::room::head::generate(json::stack::array &out, + const size_t &_limit, + const bool &_need_tophead) +const +{ + const m::event::id::closure &v1_ref{[&out] + (const auto &event_id) + { + json::stack::array prev{out}; + prev.append(event_id); + { + json::stack::object nilly{prev}; + json::stack::member willy + { + nilly, "", "" + }; + } + }}; + + const m::event::id::closure &v3_ref{[&out] + (const auto &event_id) + { + out.append(event_id); + }}; + + char versionbuf[32]; + const auto version + { + m::version(versionbuf, room, std::nothrow) + }; + + const auto &append + { + version == "1" || version == "2"? v1_ref : v3_ref + }; + + bool need_tophead{_need_tophead}; + const auto top_head + { + need_tophead? + m::top(std::nothrow, room.room_id): + std::tuple{} + }; + + int64_t depth{-1}; + size_t limit{_limit}; + m::event::fetch event; + for_each([&event, &need_tophead, &top_head, &depth, &append, &limit] + (const m::event::idx &idx, const m::event::id &event_id) + { + if(!seek(event, idx, event_id, std::nothrow)) + return true; + + if(need_tophead) + if(event.event_id == std::get<0>(top_head)) + need_tophead = false; + + append(event_id); + depth = std::max(json::get<"depth"_>(event), depth); + return --limit - need_tophead > 0; + }); + + if(need_tophead) + { + append(std::get<0>(top_head)); + depth = std::get<1>(top_head); + } + + return depth; +} + +size_t +IRCD_MODULE_EXPORT +ircd::m::room::head::count() +const +{ + size_t ret(0); + for_each([&ret] + (const event::idx &event_idx, const event::id &event_id) + { + ++ret; + return true; + }); + + return ret; +} + +bool +IRCD_MODULE_EXPORT +ircd::m::room::head::has(const event::id &event_id) +const +{ + bool ret{false}; + for_each([&ret, &event_id] + (const event::idx &event_idx, const event::id &event_id_) + { + ret = event_id_ == event_id; + return !ret; // for_each protocol: false to break + }); + + return ret; +} + +bool +IRCD_MODULE_EXPORT +ircd::m::room::head::for_each(const closure &closure) +const +{ + auto it + { + dbs::room_head.begin(room.room_id) + }; + + for(; it; ++it) + { + const event::id &event_id + { + dbs::room_head_key(it->first) + }; + + const event::idx &event_idx + { + byte_view{it->second} + }; + + if(!closure(event_idx, event_id)) + return false; + } + + return true; +} + +// +// special tools +// + +size_t +IRCD_MODULE_EXPORT +ircd::m::room::head::reset(const head &head) +{ + size_t ret{0}; + const auto &room{head.room}; + m::room::messages it{room}; + if(!it) + return ret; + + // Replacement will be the single new head + const m::event replacement + { + *it + }; + + db::txn txn + { + *m::dbs::events + }; + + // Iterate all of the existing heads with a delete operation + m::dbs::write_opts opts; + opts.op = db::op::DELETE; + opts.appendix.reset(); + opts.appendix.set(dbs::appendix::ROOM_HEAD); + head.for_each([&room, &opts, &txn, &ret] + (const m::event::idx &event_idx, const m::event::id &event_id) + { + const m::event::fetch event + { + event_idx, std::nothrow + }; + + if(!event.valid) + { + log::derror + { + m::log, "Invalid event '%s' idx %lu in head for %s", + string_view{event_id}, + event_idx, + string_view{room.room_id} + }; + + return true; + } + + opts.event_idx = event_idx; + m::dbs::write(txn, event, opts); + ++ret; + return true; + }); + + // Finally add the replacement to the txn + opts.op = db::op::SET; + opts.event_idx = it.event_idx(); + m::dbs::write(txn, replacement, opts); + + // Commit txn + txn(); + return ret; +} + +size_t +IRCD_MODULE_EXPORT +ircd::m::room::head::rebuild(const head &head) +{ + size_t ret{0}; + static const m::event::fetch::opts fopts + { + { db::get::NO_CACHE } + }; + + m::room::messages it + { + head.room, 0UL, &fopts + }; + + if(!it) + return ret; + + db::txn txn + { + *m::dbs::events + }; + + m::dbs::write_opts opts; + opts.op = db::op::SET; + for(; it; ++it) + { + const m::event &event{*it}; + opts.event_idx = it.event_idx(); + opts.appendix.reset(); + opts.appendix.set(dbs::appendix::ROOM_HEAD); + m::dbs::write(txn, event, opts); + ++ret; + } + + txn(); + return ret; +} + +void +IRCD_MODULE_EXPORT +ircd::m::room::head::modify(const m::event::id &event_id, + const db::op &op, + const bool &refs) +{ + const m::event::fetch event + { + event_id + }; + + db::txn txn + { + *m::dbs::events + }; + + // Iterate all of the existing heads with a delete operation + m::dbs::write_opts opts; + opts.op = op; + opts.event_idx = event.event_idx; + opts.appendix.reset(); + opts.appendix.set(dbs::appendix::ROOM_HEAD); + m::dbs::write(txn, event, opts); + + // Commit txn + txn(); +} diff --git a/modules/m_vm.cc b/modules/m_vm.cc index 993ad25e8..074038dde 100644 --- a/modules/m_vm.cc +++ b/modules/m_vm.cc @@ -372,7 +372,7 @@ ircd::m::vm::inject(eval &eval, const auto &[prev_events, depth] { add_prev_events? - head.make_refs(prev_buf, size_t(prev_limit), true): + head.generate(prev_buf, size_t(prev_limit), true): std::pair{{}, -1} };