// 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", 48L }, }; 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) { ret.first = range.first - 1; ret.second = event_idx; 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) { 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) { ret.first = range.first; ret.second = event_idx; return false; }); return ret; } // // room::events // size_t ircd::m::room::events::count(const m::event::idx_range &range) { const auto &[a, b] { range }; // Get the room_id from b here; a might not be in the same room but downstream // the counter seeks to a in the given room and will properly fail there. 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 }; m::room::events it { room }; assert(a <= b); it.seek_idx(a); if(!it && !exists(room)) throw m::NOT_FOUND { "Cannot find room '%s' to count events in", string_view{room.room_id} }; else if(!it) throw m::NOT_FOUND { "Event @ idx:%lu or idx:%lu not found in room '%s' or at all", a, b, string_view{room.room_id} }; size_t ret{0}; // Hit the iterator once first otherwise the count will always increment // to `1` erroneously when it ought to show `0`. for(++it; it && it.event_idx() < b; ++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); if(room.event_id) seek(room.event_id); else seek(); } 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); seek(event_id); } 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) { m::seek(_event, event_idx(), std::nothrow); return _event; } const ircd::m::event & ircd::m::room::events::operator*() { return fetch(std::nothrow); }; 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 event_idx? seek_idx(event_idx): false; } 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) try { uint64_t depth(0); if(event_idx) m::get(event_idx, "depth", mutable_buffer { reinterpret_cast(&depth), sizeof(depth) }); 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; // Check if this event_idx is actually in this room if(event_idx && event_idx != this->event_idx()) return false; return true; } catch(const db::not_found &e) { return false; } 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) { ++ret; return true; }); return ret; } bool ircd::m::room::events::missing::for_each(const closure &closure) const { return for_each(0L, closure); } bool ircd::m::room::events::missing::for_each(const int64_t &min_depth, const closure &closure) const { bool ret{true}; room::events it { room }; for(; it && ret; --it) { if(int64_t(it.depth()) < min_depth) break; const m::event &event{*it}; const m::event::prev prev{event}; ret = m::for_each(prev, [&](const m::event::id &event_id) { if(m::exists(event_id)) return true; if(!closure(event_id, it.depth(), it.event_idx())) return false; return true; }); } return ret; } // // room::events::sounding // bool 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::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; } // // room::events::horizon // //TODO: XXX remove fwd decl namespace ircd::m::dbs { void _index_event_horizon(db::txn &, const event &, const write_opts &, const m::event::id &); } size_t ircd::m::room::events::horizon::rebuild() { m::dbs::write_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) { ++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) { 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; }); }