diff --git a/include/ircd/m/room/events.h b/include/ircd/m/room/events.h index cf6a306a8..6464ae23d 100644 --- a/include/ircd/m/room/events.h +++ b/include/ircd/m/room/events.h @@ -11,20 +11,22 @@ #pragma once #define HAVE_IRCD_M_ROOM_EVENTS_H +// The "sounding" is the depth of the first gap. In any attempt to trace +// the room timeline from the tophead to the m.room.create event: the sounding +// is the [highest number] depth preventing that. +// +// The "twain" marks the depth at the end of the first gap; the server is in +// possession of one or more events again at the twain. +// +// The "hazard" is the depth of the first gap starting from the m.room.create +// event toward the tophead. In any attempt to trace the room timeline with +// an increasing depth, the hazard is the next gap to frontfill. + namespace ircd::m { - // [GET] Find gaps in the room's event graph. A gap is where no events - // have been obtained at that depth. Each gap is reported to the closure - // with a separate invocation. The range is [inclusive, exclusive]. - using depth_range = std::pair; - using depth_range_closure = std::function; - bool for_each_depth_gap(const room &, const depth_range_closure &); - bool rfor_each_depth_gap(const room &, const depth_range_closure &); - - bool sounding(const room &, const depth_range_closure &); // Last missing (all) - std::pair hazard(const room &); // First missing (one) std::pair sounding(const room &); // Last missing (one) std::pair twain(const room &); + std::pair hazard(const room &); // First missing (one) } /// Interface to room events @@ -37,6 +39,9 @@ namespace ircd::m /// struct ircd::m::room::events { + struct missing; + struct sounding; + m::room room; db::domain::const_iterator it; event::fetch _event; @@ -88,3 +93,48 @@ struct ircd::m::room::events static size_t count(const event::idx &, const event::idx &); static size_t count(const event::id &, const event::id &); }; + +/// Find missing room events. This is an interface to the event-horizon for +/// this room, organized as a breadth-first iteration of missing references. +/// +/// The closure is invoked with the first argument being the event_id unknown +/// to the server, followed by the depth and event::idx of the event making the +/// reference. +/// +struct ircd::m::room::events::missing +{ + using closure = std::function; + + m::room room; + + public: + bool for_each(const closure &) const; + + missing() = default; + missing(const m::room &room) + :room{room} + {} +}; + +/// Find gaps in the room's events. A gap is where this server has no events +/// at a certain depth. This is a path-finding diagnostic interface, useful to +/// understand what areas of the timeline have not been acquired by the server +/// to calculate backfill requests, etc. This interface is depth-first oriented, +/// rather than the breadth-first room::events::missing interface. +/// +struct ircd::m::room::events::sounding +{ + using range = std::pair; + using closure = std::function; + + m::room room; + + public: + bool for_each(const closure &) const; + bool rfor_each(const closure &) const; + + sounding() = default; + sounding(const m::room &room) + :room{room} + {} +}; diff --git a/modules/console.cc b/modules/console.cc index 8b1a8b2bf..38ed1e858 100644 --- a/modules/console.cc +++ b/modules/console.cc @@ -7807,7 +7807,12 @@ console_cmd__room__top(opt &out, const string_view &line) out << "recent gaps: " << std::endl; size_t gap_count(4); - m::rfor_each_depth_gap(room, [&out, &gap_count] + const m::room::events::sounding gaps + { + room + }; + + gaps.for_each([&out, &gap_count] (const auto &range, const auto &event_idx) { out << std::right << std::setw(8) << range.first @@ -8101,19 +8106,18 @@ console_cmd__room__sounding(opt &out, const string_view &line) m::room::index(room) }; + const auto sounding + { + m::sounding(room) + }; + out << "head: " << std::setw(8) << m::depth(room) << " " << m::event_id(head) << " (" << head << ")" << std::endl; - m::sounding(room, [&out] - (const auto &range, const auto &event_idx) - { - out << "sounding: " << std::setw(8) << range.second - << " " << m::event_id(event_idx) << " (" << event_idx << ")" - << std::endl; - - return true; - }); + out << "sounding: " << std::setw(8) << sounding.first + << " " << m::event_id(sounding.second) << " (" << sounding.second << ")" + << std::endl; out << "twain: " << std::setw(8) << twain.first << std::endl; @@ -8179,10 +8183,15 @@ console_cmd__room__depth__gaps(opt &out, const string_view &line) return true; }}; + const m::room::events::sounding gaps + { + room + }; + if(param["reverse"] == "reverse") - m::rfor_each_depth_gap(room, closure); + gaps.rfor_each(closure); else - m::for_each_depth_gap(room, closure); + gaps.for_each(closure); return true; } diff --git a/modules/m_room_events.cc b/modules/m_room_events.cc index 0f78e63f0..de55373ce 100644 --- a/modules/m_room_events.cc +++ b/modules/m_room_events.cc @@ -14,6 +14,10 @@ IRCD_MODULE "Matrix room library" }; +// +// tools +// + std::pair IRCD_MODULE_EXPORT ircd::m::twain(const room &room) @@ -23,7 +27,12 @@ ircd::m::twain(const room &room) -1, 0 }; - rfor_each_depth_gap(room, [&ret] + const room::events::sounding s + { + room + }; + + s.rfor_each([&ret] (const auto &range, const auto &event_idx) { ret.first = range.first - 1; @@ -43,7 +52,12 @@ ircd::m::sounding(const room &room) -1, 0 }; - rfor_each_depth_gap(room, [&ret] + const room::events::sounding s + { + room + }; + + s.rfor_each([&ret] (const auto &range, const auto &event_idx) { ret.first = range.second; @@ -58,8 +72,17 @@ std::pair IRCD_MODULE_EXPORT ircd::m::hazard(const room &room) { - std::pair ret {0, 0}; - for_each_depth_gap(room, [&ret] + std::pair ret + { + 0, 0 + }; + + const room::events::sounding s + { + room + }; + + s.for_each([&ret] (const auto &range, const auto &event_idx) { ret.first = range.first; @@ -70,96 +93,9 @@ ircd::m::hazard(const room &room) return ret; } -bool -IRCD_MODULE_EXPORT -ircd::m::sounding(const room &room, - const depth_range_closure &closure) -{ - bool ret(true); - int64_t depth(-1); - rfor_each_depth_gap(room, [&depth, &ret, &closure] - (const auto &range, const auto &event_idx) - { - if(depth != -1 && depth != range.second) - return false; - - depth = range.second; - if(!closure(range, event_idx)) - return false; - - return true; - }); - - return ret; -} - -bool -IRCD_MODULE_EXPORT -ircd::m::rfor_each_depth_gap(const room &room, - const depth_range_closure &closure) -{ - room::events it - { - room - }; - - if(!it) - return true; - - event::idx idx{0}; - for(depth_range range{0L, it.depth()}; it; --it) - { - range.first = it.depth(); - if(range.first == range.second) - { - idx = it.event_idx(); - continue; - } - - --range.second; - if(range.first == range.second) - { - idx = it.event_idx(); - continue; - } - - if(!closure({range.first+1, range.second+1}, idx)) - return false; - - range.second = range.first; - } - - return true; -} - -bool -IRCD_MODULE_EXPORT -ircd::m::for_each_depth_gap(const room &room, - const depth_range_closure &closure) -{ - room::events it - { - room, int64_t(0L) - }; - - for(depth_range range{0L, 0L}; it; ++it) - { - range.second = it.depth(); - if(range.first == range.second) - continue; - - ++range.first; - if(range.first == range.second) - continue; - - if(!closure(range, it.event_idx())) - return false; - - range.first = range.second; - } - - return true; -} +// +// room::events +// size_t IRCD_MODULE_EXPORT @@ -228,3 +164,92 @@ ircd::m::room::events::count(const m::room &room, for(++it; it && it.event_idx() < b; ++it, ++ret); return ret; } + +// +// room::events::events +// +// (see: ircd/m_room.cc for now) + +// +// room::events::missing +// + +bool +IRCD_MODULE_EXPORT +ircd::m::room::events::missing::for_each(const closure &closure) +const +{ + return true; +} + +// +// room::events::sounding +// + +bool +IRCD_MODULE_EXPORT +ircd::m::room::events::sounding::rfor_each(const closure &closure) +const +{ + room::events it + { + room + }; + + if(!it) + return true; + + event::idx idx{0}; + for(sounding::range range{0L, it.depth()}; it; --it) + { + range.first = it.depth(); + if(range.first == range.second) + { + idx = it.event_idx(); + continue; + } + + --range.second; + if(range.first == range.second) + { + idx = it.event_idx(); + continue; + } + + if(!closure({range.first+1, range.second+1}, idx)) + return false; + + range.second = range.first; + } + + return true; +} + +bool +IRCD_MODULE_EXPORT +ircd::m::room::events::sounding::for_each(const closure &closure) +const +{ + room::events it + { + room, int64_t(0L) + }; + + for(sounding::range range{0L, 0L}; it; ++it) + { + range.second = it.depth(); + if(range.first == range.second) + continue; + + ++range.first; + if(range.first == range.second) + continue; + + if(!closure(range, it.event_idx())) + return false; + + range.first = range.second; + } + + return true; +}