// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2019 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. decltype(ircd::m::room::events::viewport_size) ircd::m::room::events::viewport_size { { "name", "ircd.m.room.events.viewport.size" }, { "default", 96L }, }; std::pair ircd::m::viewport(const room &room) { std::pair ret { -1, 0 }; m::room::events it { room }; const ssize_t &max { room::events::viewport_size }; for(auto i(0); it && i < max; --it, ++i) { ret.first = it.depth(); ret.second = it.event_idx(); } return ret; } std::pair ircd::m::twain(const room &room) { std::pair ret { -1, 0 }; const room::events::sounding s { room }; s.rfor_each([&ret] (const auto &range, const auto &event_idx) noexcept { ret.first = range.first - 1; return false; }); return ret; } std::pair ircd::m::sounding(const room &room) { std::pair ret { -1, 0 }; const room::events::sounding s { room }; s.rfor_each([&ret] (const auto &range, const auto &event_idx) noexcept { ret.first = range.second; ret.second = event_idx; return false; }); return ret; } std::pair ircd::m::hazard(const room &room) { std::pair ret { 0, 0 }; const room::events::sounding s { room }; s.for_each([&ret] (const auto &range, const auto &event_idx) noexcept { ret.first = range.first; return false; }); return ret; } // // room::events // size_t ircd::m::room::events::count(const m::event::idx_range &range) { const auto &[a, b] { range }; room::id::buf room_id { m::get(std::min(a, b), "room_id", room_id) }; return count(room_id, range); } size_t ircd::m::room::events::count(const m::room &room, const m::event::idx_range &range) { const auto &[a, b] { range }; assert(a <= b); m::room::events it { room, event::id{} }; size_t ret(0); if(a == b) return ret; if(unlikely(!it.seek_idx(b, true))) return ret; for(assert(it); it && it.event_idx() > a; --it) ++ret; return ret; } size_t ircd::m::room::events::prefetch_viewport(const m::room &room) { m::room::events it { room }; const event::fetch::opts &fopts { room.fopts? *room.fopts: event::fetch::default_opts }; ssize_t i(0), ret(0); for(; it && i < viewport_size; --it, ++i) { const auto &event_idx(it.event_idx()); ret += m::prefetch(event_idx, fopts); } return ret; } size_t ircd::m::room::events::prefetch(const m::room &room, const depth_range &range) { m::room::events it { room, std::max(range.first, range.second) }; const event::fetch::opts &fopts { room.fopts? *room.fopts: event::fetch::default_opts }; ssize_t i(0), ret(0); for(; it && i < viewport_size; --it, ++i) { const auto &depth(it.depth()); const auto &event_idx(it.event_idx()); ret += m::prefetch(event_idx, fopts); if(depth <= std::min(range.first, range.second)) break; } return ret; } bool ircd::m::room::events::preseek(const m::room &room, const uint64_t &depth) { char buf[dbs::ROOM_EVENTS_KEY_MAX_SIZE]; const string_view key { depth != uint64_t(-1)? dbs::room_events_key(buf, room.room_id, depth): string_view{room.room_id} }; return db::prefetch(dbs::room_events, key); } // // room::events::events // ircd::m::room::events::events(const m::room &room, const event::fetch::opts *const &fopts) :room{room} ,_event { fopts? *fopts: room.fopts? *room.fopts: event::fetch::default_opts } { assert(room.room_id); const bool found { room.event_id? seek(room.event_id): seek() }; assert(found == bool(*this)); } ircd::m::room::events::events(const m::room &room, const event::id &event_id, const event::fetch::opts *const &fopts) :room{room} ,_event { fopts? *fopts: room.fopts? *room.fopts: event::fetch::default_opts } { assert(room.room_id); const bool found { seek(event_id) }; assert(found == bool(*this)); } ircd::m::room::events::events(const m::room &room, const uint64_t &depth, const event::fetch::opts *const &fopts) :room{room} ,_event { fopts? *fopts: room.fopts? *room.fopts: event::fetch::default_opts } { assert(room.room_id); // As a special convenience for the ctor only, if the depth=0 and // nothing is found another attempt is made for depth=1 for synapse // rooms which start at depth=1. if(!seek(depth) && depth == 0) seek(1); } bool ircd::m::room::events::prefetch() { assert(_event.fopts); return m::prefetch(event_idx(), *_event.fopts); } bool ircd::m::room::events::prefetch(const string_view &event_prop) { return m::prefetch(event_idx(), event_prop); } const ircd::m::event & ircd::m::room::events::fetch() { m::seek(_event, event_idx()); return _event; } const ircd::m::event & ircd::m::room::events::fetch(std::nothrow_t, bool *const valid_) { const bool valid { m::seek(std::nothrow, _event, event_idx()) }; if(valid_) *valid_ = valid; return _event; } bool ircd::m::room::events::preseek(const uint64_t &depth) { char buf[dbs::ROOM_EVENTS_KEY_MAX_SIZE]; const string_view key { depth != uint64_t(-1)? dbs::room_events_key(buf, room.room_id, depth): room.room_id }; return db::prefetch(dbs::room_events, key); } bool ircd::m::room::events::seek(const event::id &event_id) { const event::idx &event_idx { m::index(std::nothrow, event_id) }; return seek_idx(event_idx); } bool ircd::m::room::events::seek(const uint64_t &depth) { char buf[dbs::ROOM_EVENTS_KEY_MAX_SIZE]; const string_view seek_key { depth != uint64_t(-1)? dbs::room_events_key(buf, room.room_id, depth): room.room_id }; this->it = dbs::room_events.begin(seek_key); return bool(*this); } bool ircd::m::room::events::seek_idx(const event::idx &event_idx, const bool &lower_bound) { if(!event_idx) return false; const uint64_t depth { m::get(std::nothrow, event_idx, "depth", -1UL) }; char buf[dbs::ROOM_EVENTS_KEY_MAX_SIZE]; const auto &seek_key { dbs::room_events_key(buf, room.room_id, depth, event_idx) }; this->it = dbs::room_events.begin(seek_key); if(!bool(*this)) return false; // When lower_bound=false we need to find the exact event being sought if(!lower_bound && event_idx != this->event_idx()) return false; return true; } ircd::m::room::events::operator ircd::m::event::idx() const { return event_idx(); } ircd::m::event::idx ircd::m::room::events::event_idx() const { assert(bool(*this)); const auto part { dbs::room_events_key(it->first) }; return std::get<1>(part); } uint64_t ircd::m::room::events::depth() const { assert(bool(*this)); const auto part { dbs::room_events_key(it->first) }; return std::get<0>(part); } // // room::events::missing // size_t ircd::m::room::events::missing::count() const { size_t ret{0}; for_each([&ret] (const auto &event_id, const auto &depth, const auto &event_idx) noexcept { ++ret; return true; }); return ret; } bool ircd::m::room::events::missing::for_each(const closure &closure) const { return for_each({0L, 0L}, closure); } bool ircd::m::room::events::missing::for_each(const pair &depth, const closure &closure) const { room::events it { room, uint64_t(depth.first) }; for(; it; ++it) { if(depth.second && int64_t(it.depth()) > depth.second) break; if(!_each(it, closure)) return false; } return true; } bool ircd::m::room::events::missing::rfor_each(const pair &depth, const closure &closure) const { room::events it { room, depth.second?: -1UL }; for(; it; --it) { if(depth.second && int64_t(it.depth()) > depth.second) continue; if(int64_t(it.depth()) < depth.first) break; if(!_each(it, closure)) return false; } return true; } bool ircd::m::room::events::missing::_each(m::room::events &it, const closure &closure) const { const m::event event { *it }; const event::prev prev { event }; event::idx idx_buf[event::prev::MAX]; const auto idx { prev.idxs(idx_buf) }; for(size_t i(0); i < idx.size(); ++i) { if(idx[i]) continue; if(!closure(prev.prev_event(i), it.depth(), it.event_idx())) return false; } return true; } // // room::events::sounding // bool ircd::m::room::events::sounding::rfor_each(const closure &closure) const { const int64_t depth { room.event_id? m::get(std::nothrow, m::index(std::nothrow, room.event_id), "depth", -1L): -1L }; room::events it { room, uint64_t(depth) }; 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::m::room::events::sounding::for_each(const closure &closure) const { const int64_t depth { room.event_id? m::get(std::nothrow, m::index(std::nothrow, room.event_id), "depth", 0L): 0L }; room::events it { room, uint64_t(depth) }; for(sounding::range range{depth, 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::horizon // //TODO: XXX remove fwd decl namespace ircd::m::dbs { void _index_event_horizon(db::txn &, const event &, const opts &, const m::event::id &); } size_t ircd::m::room::events::horizon::rebuild() { m::dbs::opts opts; opts.appendix.reset(); opts.appendix.set(dbs::appendix::EVENT_HORIZON); db::txn txn { *dbs::events }; size_t ret(0); m::room::events it { room }; for(; it; --it) { const m::event &event{*it}; const event::prev prev_events{event}; opts.event_idx = it.event_idx(); m::for_each(prev_events, [&] (const m::event::id &event_id) { if(m::exists(event_id)) return true; m::dbs::_index_event_horizon(txn, event, opts, event_id); ++ret; return true; }); } txn(); return ret; } size_t ircd::m::room::events::horizon::count() const { size_t ret{0}; for_each([&ret] (const auto &event_id, const auto &depth, const auto &event_idx) noexcept { ++ret; return true; }); return ret; } bool ircd::m::room::events::horizon::for_each(const closure &closure) const { const std::function in_room { [this](const string_view &room_id) noexcept { return room_id == this->room.room_id; } }; return event::horizon::for_every([&in_room, &closure] (const event::id &event_id, const event::idx &event_idx) { if(!m::query(event_idx, "room_id", false, in_room)) return true; if(m::exists(event_id)) return true; uint64_t depth; if(!m::get(event_idx, "depth", depth)) return true; if(!closure(event_id, depth, event_idx)) return false; return true; }); }