mirror of
https://github.com/matrix-construct/construct
synced 2024-12-26 15:33:54 +01:00
matrix: Split room definitions; various reorg.
This commit is contained in:
parent
6f1ff08841
commit
7b4dcbc488
14 changed files with 7362 additions and 2938 deletions
|
@ -70,13 +70,22 @@ libircd_matrix_la_SOURCES += event_prefetch.cc
|
|||
libircd_matrix_la_SOURCES += event_prev.cc
|
||||
libircd_matrix_la_SOURCES += event_refs.cc
|
||||
libircd_matrix_la_SOURCES += room.cc
|
||||
libircd_matrix_la_SOURCES += room_aliases.cc
|
||||
libircd_matrix_la_SOURCES += room_auth.cc
|
||||
libircd_matrix_la_SOURCES += room_aliases.cc
|
||||
libircd_matrix_la_SOURCES += room_bootstrap.cc
|
||||
libircd_matrix_la_SOURCES += room_create.cc
|
||||
libircd_matrix_la_SOURCES += room_events.cc
|
||||
libircd_matrix_la_SOURCES += room_head.cc
|
||||
libircd_matrix_la_SOURCES += room_history_visibility.cc
|
||||
libircd_matrix_la_SOURCES += room_join.cc
|
||||
libircd_matrix_la_SOURCES += room_leave.cc
|
||||
libircd_matrix_la_SOURCES += room_visible.cc
|
||||
libircd_matrix_la_SOURCES += room_members.cc
|
||||
libircd_matrix_la_SOURCES += room_origins.cc
|
||||
libircd_matrix_la_SOURCES += room_power.cc
|
||||
libircd_matrix_la_SOURCES += room_state.cc
|
||||
libircd_matrix_la_SOURCES += room_state_history.cc
|
||||
libircd_matrix_la_SOURCES += room_state_space.cc
|
||||
libircd_matrix_la_SOURCES += room_stats.cc
|
||||
libircd_matrix_la_SOURCES += user.cc
|
||||
libircd_matrix_la_SOURCES += user_account_data.cc
|
||||
libircd_matrix_la_SOURCES += user_events.cc
|
||||
|
@ -88,6 +97,7 @@ libircd_matrix_la_SOURCES += user_profile.cc
|
|||
libircd_matrix_la_SOURCES += user_register.cc
|
||||
libircd_matrix_la_SOURCES += user_room_account_data.cc
|
||||
libircd_matrix_la_SOURCES += user_room_tags.cc
|
||||
libircd_matrix_la_SOURCES += user_ignores.cc
|
||||
libircd_matrix_la_SOURCES += breadcrumb_rooms.cc
|
||||
libircd_matrix_la_SOURCES += device.cc
|
||||
libircd_matrix_la_SOURCES += display_name.cc
|
||||
|
@ -97,12 +107,9 @@ libircd_matrix_la_SOURCES += events.cc
|
|||
libircd_matrix_la_SOURCES += fed.cc
|
||||
libircd_matrix_la_SOURCES += feds.cc
|
||||
libircd_matrix_la_SOURCES += fetch.cc
|
||||
libircd_matrix_la_SOURCES += ignored_user_list.cc
|
||||
libircd_matrix_la_SOURCES += init_backfill.cc
|
||||
libircd_matrix_la_SOURCES += init_bootstrap.cc
|
||||
libircd_matrix_la_SOURCES += join.cc
|
||||
libircd_matrix_la_SOURCES += keys.cc
|
||||
libircd_matrix_la_SOURCES += leave.cc
|
||||
libircd_matrix_la_SOURCES += node.cc
|
||||
libircd_matrix_la_SOURCES += presence.cc
|
||||
libircd_matrix_la_SOURCES += pretty.cc
|
||||
|
|
2932
matrix/room.cc
2932
matrix/room.cc
File diff suppressed because it is too large
Load diff
|
@ -46,8 +46,73 @@ ircd::m::alias_fetch_timeout
|
|||
// m::room::aliases
|
||||
//
|
||||
|
||||
size_t
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::count()
|
||||
const
|
||||
{
|
||||
return count(string_view{});
|
||||
}
|
||||
|
||||
size_t
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::count(const string_view &server)
|
||||
const
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each(server, [&ret](const auto &a)
|
||||
{
|
||||
++ret;
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::has(const alias &alias)
|
||||
const
|
||||
{
|
||||
return !for_each(alias.host(), [&alias]
|
||||
(const id::room_alias &a)
|
||||
{
|
||||
assert(a.host() == alias.host());
|
||||
return a == alias? false : true; // false to break on found
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::for_each(const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
const room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
return state.for_each("m.room.aliases", [this, &closure]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &)
|
||||
{
|
||||
return for_each(state_key, closure);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::for_each(const string_view &server,
|
||||
const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
if(!server)
|
||||
return for_each(closure);
|
||||
|
||||
return for_each(room, server, closure);
|
||||
}
|
||||
|
||||
ebool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::for_each(const m::room &room,
|
||||
const string_view &server,
|
||||
const closure_bool &closure)
|
||||
|
@ -217,6 +282,79 @@ namespace ircd::m
|
|||
thread_local char room_aliases_cache_fetch_hpbuf[384];
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::fetch(std::nothrow_t,
|
||||
const alias &a,
|
||||
const net::hostport &hp)
|
||||
try
|
||||
{
|
||||
fetch(a, hp);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
thread_local char buf[384];
|
||||
log::error
|
||||
{
|
||||
log, "Failed to fetch room_id for %s from %s :%s",
|
||||
string_view{a},
|
||||
string(buf, hp),
|
||||
e.what(),
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ircd::m::room::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::get(const alias &a)
|
||||
{
|
||||
id::buf ret;
|
||||
get(a, [&ret]
|
||||
(const id &room_id)
|
||||
{
|
||||
ret = room_id;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::m::room::id::buf
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::get(std::nothrow_t,
|
||||
const alias &a)
|
||||
{
|
||||
id::buf ret;
|
||||
get(std::nothrow, a, [&ret]
|
||||
(const id &room_id)
|
||||
{
|
||||
ret = room_id;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::get(const alias &a,
|
||||
const id::closure &c)
|
||||
{
|
||||
if(!get(std::nothrow, a, c))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"Cannot find room_id for %s",
|
||||
string_view{a}
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::for_each(const closure_bool &c)
|
||||
{
|
||||
return for_each(string_view{}, c);
|
||||
}
|
||||
|
||||
void
|
||||
IRCD_MODULE_EXPORT
|
||||
ircd::m::room::aliases::cache::fetch(const alias &alias,
|
||||
|
|
|
@ -256,7 +256,206 @@ ircd::m::room::events::preseek(const m::room &room,
|
|||
//
|
||||
// room::events::events
|
||||
//
|
||||
// (see: ircd/m_room.cc for now)
|
||||
|
||||
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(event_id, std::nothrow)
|
||||
};
|
||||
|
||||
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<char *>(&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
|
||||
|
|
2542
matrix/room_members.cc
Normal file
2542
matrix/room_members.cc
Normal file
File diff suppressed because it is too large
Load diff
2323
matrix/room_origins.cc
Normal file
2323
matrix/room_origins.cc
Normal file
File diff suppressed because it is too large
Load diff
584
matrix/room_power.cc
Normal file
584
matrix/room_power.cc
Normal file
|
@ -0,0 +1,584 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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::power::default_creator_level)
|
||||
ircd::m::room::power::default_creator_level
|
||||
{
|
||||
100
|
||||
};
|
||||
|
||||
decltype(ircd::m::room::power::default_power_level)
|
||||
ircd::m::room::power::default_power_level
|
||||
{
|
||||
50
|
||||
};
|
||||
|
||||
decltype(ircd::m::room::power::default_event_level)
|
||||
ircd::m::room::power::default_event_level
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
decltype(ircd::m::room::power::default_user_level)
|
||||
ircd::m::room::power::default_user_level
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
ircd::json::object
|
||||
ircd::m::room::power::default_content(const mutable_buffer &buf,
|
||||
const m::user::id &creator)
|
||||
{
|
||||
return compose_content(buf, [&creator]
|
||||
(const string_view &key, json::stack::object &object)
|
||||
{
|
||||
if(key != "users")
|
||||
return;
|
||||
|
||||
assert(default_creator_level == 100);
|
||||
json::stack::member
|
||||
{
|
||||
object, creator, json::value(default_creator_level)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
ircd::json::object
|
||||
ircd::m::room::power::compose_content(const mutable_buffer &buf,
|
||||
const compose_closure &closure)
|
||||
{
|
||||
json::stack out{buf};
|
||||
json::stack::object content{out};
|
||||
|
||||
assert(default_power_level == 50);
|
||||
json::stack::member
|
||||
{
|
||||
content, "ban", json::value(default_power_level)
|
||||
};
|
||||
|
||||
{
|
||||
json::stack::object events
|
||||
{
|
||||
content, "events"
|
||||
};
|
||||
|
||||
closure("events", events);
|
||||
}
|
||||
|
||||
assert(default_event_level == 0);
|
||||
json::stack::member
|
||||
{
|
||||
content, "events_default", json::value(default_event_level)
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
content, "invite", json::value(default_power_level)
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
content, "kick", json::value(default_power_level)
|
||||
};
|
||||
|
||||
{
|
||||
json::stack::object notifications
|
||||
{
|
||||
content, "notifications"
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
notifications, "room", json::value(default_power_level)
|
||||
};
|
||||
|
||||
closure("notifications", notifications);
|
||||
}
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
content, "redact", json::value(default_power_level)
|
||||
};
|
||||
|
||||
json::stack::member
|
||||
{
|
||||
content, "state_default", json::value(default_power_level)
|
||||
};
|
||||
|
||||
{
|
||||
json::stack::object users
|
||||
{
|
||||
content, "users"
|
||||
};
|
||||
|
||||
closure("users", users);
|
||||
}
|
||||
|
||||
assert(default_user_level == 0);
|
||||
json::stack::member
|
||||
{
|
||||
content, "users_default", json::value(default_user_level)
|
||||
};
|
||||
|
||||
content.~object();
|
||||
return json::object{out.completed()};
|
||||
}
|
||||
|
||||
//
|
||||
// room::power::power
|
||||
//
|
||||
|
||||
ircd::m::room::power::power(const m::room &room)
|
||||
:power
|
||||
{
|
||||
room, room.get(std::nothrow, "m.room.power_levels", "")
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::power::power(const m::room &room,
|
||||
const event::idx &power_event_idx)
|
||||
:room
|
||||
{
|
||||
room
|
||||
}
|
||||
,power_event_idx
|
||||
{
|
||||
power_event_idx
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::power::power(const m::event &power_event,
|
||||
const m::event &create_event)
|
||||
:power
|
||||
{
|
||||
power_event, m::user::id(unquote(json::get<"content"_>(create_event).get("creator")))
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::power::power(const m::event &power_event,
|
||||
const m::user::id &room_creator_id)
|
||||
:power
|
||||
{
|
||||
json::get<"content"_>(power_event), room_creator_id
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::power::power(const json::object &power_event_content,
|
||||
const m::user::id &room_creator_id)
|
||||
:power_event_content
|
||||
{
|
||||
power_event_content
|
||||
}
|
||||
,room_creator_id
|
||||
{
|
||||
room_creator_id
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
/// "all who attain great power and riches make use of either force or fraud"
|
||||
///
|
||||
/// Returns bool for "allow" or "deny"
|
||||
///
|
||||
/// Provide the user invoking the power. The return value indicates whether
|
||||
/// they have the power.
|
||||
///
|
||||
/// Provide the property/event_type. There are two usages here: 1. This is a
|
||||
/// string corresponding to one of the spec top-level properties like "ban"
|
||||
/// and "redact". In this case, the type and state_key parameters to this
|
||||
/// function are not used. 2. This string is empty or "events" in which case
|
||||
/// the type parameter is used to fetch the power threshold for that type.
|
||||
/// For state events of a type, the state_key must be provided for inspection
|
||||
/// here as well.
|
||||
bool
|
||||
ircd::m::room::power::operator()(const m::user::id &user_id,
|
||||
const string_view &prop,
|
||||
const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
const auto &user_level
|
||||
{
|
||||
level_user(user_id)
|
||||
};
|
||||
|
||||
const auto &required_level
|
||||
{
|
||||
empty(prop) || prop == "events"?
|
||||
level_event(type, state_key):
|
||||
level(prop)
|
||||
};
|
||||
|
||||
return user_level >= required_level;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ircd::m::room::power::level_user(const m::user::id &user_id)
|
||||
const try
|
||||
{
|
||||
int64_t ret
|
||||
{
|
||||
default_user_level
|
||||
};
|
||||
|
||||
const auto closure{[&user_id, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto users_default
|
||||
{
|
||||
content.get<int64_t>("users_default", default_user_level)
|
||||
};
|
||||
|
||||
const json::object &users
|
||||
{
|
||||
content.get("users")
|
||||
};
|
||||
|
||||
ret = users.get<int64_t>(user_id, users_default);
|
||||
}};
|
||||
|
||||
const bool has_power_levels_event
|
||||
{
|
||||
view(closure)
|
||||
};
|
||||
|
||||
if(!has_power_levels_event)
|
||||
{
|
||||
if(room_creator_id && user_id == room_creator_id)
|
||||
ret = default_creator_level;
|
||||
|
||||
if(room.room_id && creator(room, user_id))
|
||||
ret = default_creator_level;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &e)
|
||||
{
|
||||
return default_user_level;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ircd::m::room::power::level_event(const string_view &type)
|
||||
const try
|
||||
{
|
||||
int64_t ret
|
||||
{
|
||||
default_event_level
|
||||
};
|
||||
|
||||
const auto closure{[&type, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto &events_default
|
||||
{
|
||||
content.get<int64_t>("events_default", default_event_level)
|
||||
};
|
||||
|
||||
const json::object &events
|
||||
{
|
||||
content.get("events")
|
||||
};
|
||||
|
||||
ret = events.get<int64_t>(type, events_default);
|
||||
}};
|
||||
|
||||
const bool has_power_levels_event
|
||||
{
|
||||
view(closure)
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &e)
|
||||
{
|
||||
return default_event_level;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ircd::m::room::power::level_event(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const try
|
||||
{
|
||||
if(!defined(state_key))
|
||||
return level_event(type);
|
||||
|
||||
int64_t ret
|
||||
{
|
||||
default_power_level
|
||||
};
|
||||
|
||||
const auto closure{[&type, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto &state_default
|
||||
{
|
||||
content.get<int64_t>("state_default", default_power_level)
|
||||
};
|
||||
|
||||
const json::object &events
|
||||
{
|
||||
content.get("events")
|
||||
};
|
||||
|
||||
ret = events.get<int64_t>(type, state_default);
|
||||
}};
|
||||
|
||||
const bool has_power_levels_event
|
||||
{
|
||||
view(closure)
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &e)
|
||||
{
|
||||
return default_power_level;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ircd::m::room::power::level(const string_view &prop)
|
||||
const try
|
||||
{
|
||||
int64_t ret
|
||||
{
|
||||
default_power_level
|
||||
};
|
||||
|
||||
view([&prop, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
ret = content.at<int64_t>(prop);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &e)
|
||||
{
|
||||
return default_power_level;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::power::count_levels()
|
||||
const
|
||||
{
|
||||
size_t ret{0};
|
||||
for_each([&ret]
|
||||
(const string_view &, const int64_t &)
|
||||
{
|
||||
++ret;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::power::count_collections()
|
||||
const
|
||||
{
|
||||
size_t ret{0};
|
||||
view([&ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
for(const auto &member : content)
|
||||
ret += json::type(member.second) == json::OBJECT;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::power::count(const string_view &prop)
|
||||
const
|
||||
{
|
||||
size_t ret{0};
|
||||
for_each(prop, [&ret]
|
||||
(const string_view &, const int64_t &)
|
||||
{
|
||||
++ret;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::has_event(const string_view &type)
|
||||
const try
|
||||
{
|
||||
bool ret{false};
|
||||
view([&type, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const json::object &events
|
||||
{
|
||||
content.at("events")
|
||||
};
|
||||
|
||||
const string_view &level
|
||||
{
|
||||
unquote(events.at(type))
|
||||
};
|
||||
|
||||
ret = json::type(level) == json::NUMBER;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::has_user(const m::user::id &user_id)
|
||||
const try
|
||||
{
|
||||
bool ret{false};
|
||||
view([&user_id, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const json::object &users
|
||||
{
|
||||
content.at("users")
|
||||
};
|
||||
|
||||
const string_view &level
|
||||
{
|
||||
unquote(users.at(user_id))
|
||||
};
|
||||
|
||||
ret = json::type(level) == json::NUMBER;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
catch(const json::error &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::has_collection(const string_view &prop)
|
||||
const
|
||||
{
|
||||
bool ret{false};
|
||||
view([&prop, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto &value{content.get(prop)};
|
||||
if(value && json::type(value) == json::OBJECT)
|
||||
ret = true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::has_level(const string_view &prop)
|
||||
const
|
||||
{
|
||||
bool ret{false};
|
||||
view([&prop, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const auto &value(unquote(content.get(prop)));
|
||||
if(value && json::type(value) == json::NUMBER)
|
||||
ret = true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::power::for_each(const closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(string_view{}, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::for_each(const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return for_each(string_view{}, closure);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::power::for_each(const string_view &prop,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(prop, closure_bool{[&closure]
|
||||
(const string_view &key, const int64_t &level)
|
||||
{
|
||||
closure(key, level);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::for_each(const string_view &prop,
|
||||
const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
bool ret{true};
|
||||
view([&prop, &closure, &ret]
|
||||
(const json::object &content)
|
||||
{
|
||||
const json::object &collection
|
||||
{
|
||||
// This little cmov gimmick sets collection to be the outer object
|
||||
// itself if no property was given, allowing us to reuse this func
|
||||
// for all iterations of key -> level mappings.
|
||||
prop? json::object{content.get(prop)} : content
|
||||
};
|
||||
|
||||
const string_view _collection{collection};
|
||||
if(prop && (!_collection || json::type(_collection) != json::OBJECT))
|
||||
return;
|
||||
|
||||
for(auto it(begin(collection)); it != end(collection) && ret; ++it)
|
||||
{
|
||||
const auto &member(*it);
|
||||
if(json::type(unquote(member.second)) != json::NUMBER)
|
||||
continue;
|
||||
|
||||
const auto &key
|
||||
{
|
||||
unquote(member.first)
|
||||
};
|
||||
|
||||
const auto &val
|
||||
{
|
||||
lex_cast<int64_t>(member.second)
|
||||
};
|
||||
|
||||
ret = closure(key, val);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::power::view(const std::function<void (const json::object &)> &closure)
|
||||
const
|
||||
{
|
||||
if(power_event_idx)
|
||||
if(m::get(std::nothrow, power_event_idx, "content", closure))
|
||||
return true;
|
||||
|
||||
closure(power_event_content);
|
||||
return !empty(power_event_content);
|
||||
}
|
996
matrix/room_state.cc
Normal file
996
matrix/room_state.cc
Normal file
|
@ -0,0 +1,996 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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::state::enable_history)
|
||||
ircd::m::room::state::enable_history
|
||||
{
|
||||
{ "name", "ircd.m.room.state.enable_history" },
|
||||
{ "default", true },
|
||||
};
|
||||
|
||||
decltype(ircd::m::room::state::readahead_size)
|
||||
ircd::m::room::state::readahead_size
|
||||
{
|
||||
{ "name", "ircd.m.room.state.readahead_size" },
|
||||
{ "default", 0L },
|
||||
};
|
||||
|
||||
//
|
||||
// room::state::state
|
||||
//
|
||||
|
||||
ircd::m::room::state::state(const m::room &room,
|
||||
const event::fetch::opts *const &fopts)
|
||||
:room_id
|
||||
{
|
||||
room.room_id
|
||||
}
|
||||
,event_id
|
||||
{
|
||||
room.event_id?
|
||||
event::id::buf{room.event_id}:
|
||||
event::id::buf{}
|
||||
}
|
||||
,fopts
|
||||
{
|
||||
fopts?
|
||||
fopts:
|
||||
room.fopts
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::prefetch(const string_view &type)
|
||||
const
|
||||
{
|
||||
return prefetch(type, string_view{});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::prefetch(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
return history.prefetch(type, state_key);
|
||||
}
|
||||
|
||||
char buf[dbs::ROOM_STATE_KEY_MAX_SIZE];
|
||||
const auto &key
|
||||
{
|
||||
dbs::room_state_key(buf, room_id, type, state_key)
|
||||
};
|
||||
|
||||
return db::prefetch(dbs::room_state, key);
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::get(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
event::idx ret;
|
||||
get(type, state_key, event::closure_idx{[&ret]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
ret = event_idx;
|
||||
}});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::get(std::nothrow_t,
|
||||
const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
event::idx ret{0};
|
||||
get(std::nothrow, type, state_key, event::closure_idx{[&ret]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
ret = event_idx;
|
||||
}});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::get(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::closure &closure)
|
||||
const
|
||||
{
|
||||
get(type, state_key, event::closure_idx{[this, &closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
const event::fetch event
|
||||
{
|
||||
event_idx, fopts? *fopts : event::fetch::default_opts
|
||||
};
|
||||
|
||||
closure(event);
|
||||
}});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::get(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::id::closure &closure)
|
||||
const
|
||||
{
|
||||
get(type, state_key, event::closure_idx{[&]
|
||||
(const event::idx &idx)
|
||||
{
|
||||
if(!m::event_id(idx, std::nothrow, closure))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"(%s,%s) in %s idx:%lu event_id :not found",
|
||||
type,
|
||||
state_key,
|
||||
string_view{room_id},
|
||||
idx,
|
||||
};
|
||||
}});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::get(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::closure_idx &closure)
|
||||
const try
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
closure(history.get(type, state_key));
|
||||
return;
|
||||
}
|
||||
|
||||
auto &column{dbs::room_state};
|
||||
char key[dbs::ROOM_STATE_KEY_MAX_SIZE];
|
||||
column(dbs::room_state_key(key, room_id, type, state_key), [&closure]
|
||||
(const string_view &value)
|
||||
{
|
||||
closure(byte_view<event::idx>(value));
|
||||
});
|
||||
}
|
||||
catch(const db::not_found &e)
|
||||
{
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"(%s,%s) in %s :%s",
|
||||
type,
|
||||
state_key,
|
||||
string_view{room_id},
|
||||
e.what()
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::get(std::nothrow_t,
|
||||
const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::closure &closure)
|
||||
const
|
||||
{
|
||||
return get(std::nothrow, type, state_key, event::closure_idx{[this, &closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
const event::fetch event
|
||||
{
|
||||
event_idx, std::nothrow, fopts? *fopts : event::fetch::default_opts
|
||||
};
|
||||
|
||||
closure(event);
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::get(std::nothrow_t,
|
||||
const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::id::closure &closure)
|
||||
const
|
||||
{
|
||||
return get(std::nothrow, type, state_key, event::closure_idx{[&closure]
|
||||
(const event::idx &idx)
|
||||
{
|
||||
m::event_id(idx, std::nothrow, closure);
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::get(std::nothrow_t,
|
||||
const string_view &type,
|
||||
const string_view &state_key,
|
||||
const event::closure_idx &closure)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
const auto event_idx
|
||||
{
|
||||
history.get(std::nothrow, type, state_key)
|
||||
};
|
||||
|
||||
if(event_idx)
|
||||
{
|
||||
closure(event_idx);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
auto &column{dbs::room_state};
|
||||
char key[dbs::ROOM_STATE_KEY_MAX_SIZE];
|
||||
return column(dbs::room_state_key(key, room_id, type, state_key), std::nothrow, [&closure]
|
||||
(const string_view &value)
|
||||
{
|
||||
closure(byte_view<event::idx>(value));
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::has(const event::idx &event_idx)
|
||||
const
|
||||
{
|
||||
static const event::fetch::opts fopts
|
||||
{
|
||||
event::keys::include { "type", "state_key" },
|
||||
};
|
||||
|
||||
const m::event::fetch event
|
||||
{
|
||||
event_idx, std::nothrow, fopts
|
||||
};
|
||||
|
||||
if(!event.valid)
|
||||
return false;
|
||||
|
||||
const auto state_idx
|
||||
{
|
||||
get(std::nothrow, at<"type"_>(event), at<"state_key"_>(event))
|
||||
};
|
||||
|
||||
assert(event_idx);
|
||||
return event_idx == state_idx;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::has(const string_view &type)
|
||||
const
|
||||
{
|
||||
return for_each(type, event::id::closure_bool{[](const m::event::id &)
|
||||
{
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::has(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
return history.has(type, state_key);
|
||||
}
|
||||
|
||||
auto &column{dbs::room_state};
|
||||
char key[dbs::ROOM_STATE_KEY_MAX_SIZE];
|
||||
return db::has(column, dbs::room_state_key(key, room_id, type, state_key));
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::count()
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
return count(string_view{});
|
||||
|
||||
const db::gopts &opts
|
||||
{
|
||||
this->fopts? this->fopts->gopts : db::gopts{}
|
||||
};
|
||||
|
||||
size_t ret(0);
|
||||
auto &column{dbs::room_state};
|
||||
for(auto it{column.begin(room_id, opts)}; bool(it); ++it)
|
||||
++ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::count(const string_view &type)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
return count(type);
|
||||
|
||||
const db::gopts &opts
|
||||
{
|
||||
this->fopts? this->fopts->gopts : db::gopts{}
|
||||
};
|
||||
|
||||
size_t ret(0);
|
||||
auto &column{dbs::room_state};
|
||||
for(auto it{column.begin(room_id, opts)}; bool(it); ++it)
|
||||
{
|
||||
const auto key(dbs::room_state_key(it->first));
|
||||
ret += std::get<0>(key) == type;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const event::closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(event::closure_bool{[&closure]
|
||||
(const m::event &event)
|
||||
{
|
||||
closure(event);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const event::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
event::fetch event
|
||||
{
|
||||
fopts? *fopts : event::fetch::default_opts
|
||||
};
|
||||
|
||||
return for_each(event::closure_idx_bool{[&event, &closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
if(seek(event, event_idx, std::nothrow))
|
||||
if(!closure(event))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const event::id::closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(event::id::closure_bool{[&closure]
|
||||
(const event::id &event_id)
|
||||
{
|
||||
closure(event_id);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const event::id::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return for_each(event::closure_idx_bool{[&closure]
|
||||
(const event::idx &idx)
|
||||
{
|
||||
bool ret{true};
|
||||
m::event_id(idx, std::nothrow, [&ret, &closure]
|
||||
(const event::id &id)
|
||||
{
|
||||
ret = closure(id);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}});
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const event::closure_idx &closure)
|
||||
const
|
||||
{
|
||||
for_each(event::closure_idx_bool{[&closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
closure(event_idx);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const event::closure_idx_bool &closure)
|
||||
const
|
||||
{
|
||||
return for_each(closure_bool{[&closure]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
return closure(event_idx);
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
return history.for_each([&closure]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
return closure(type, state_key, event_idx);
|
||||
});
|
||||
}
|
||||
|
||||
db::gopts opts
|
||||
{
|
||||
this->fopts? this->fopts->gopts : db::gopts{}
|
||||
};
|
||||
|
||||
if(!opts.readahead)
|
||||
opts.readahead = size_t(readahead_size);
|
||||
|
||||
auto &column{dbs::room_state};
|
||||
for(auto it{column.begin(room_id, opts)}; bool(it); ++it)
|
||||
{
|
||||
const byte_view<event::idx> idx(it->second);
|
||||
const auto key(dbs::room_state_key(it->first));
|
||||
if(!closure(std::get<0>(key), std::get<1>(key), idx))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const type_prefix &prefix,
|
||||
const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
bool ret(true), cont(true);
|
||||
for_each(closure_bool{[&prefix, &closure, &ret, &cont]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
if(!startswith(type, string_view(prefix)))
|
||||
return cont;
|
||||
|
||||
cont = false;
|
||||
ret = closure(type, state_key, event_idx);
|
||||
return ret;
|
||||
}});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(type, event::closure_bool{[&closure]
|
||||
(const m::event &event)
|
||||
{
|
||||
closure(event);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return type?
|
||||
for_each(type, string_view{}, closure):
|
||||
for_each(closure);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::id::closure &closure)
|
||||
const
|
||||
{
|
||||
for_each(type, event::id::closure_bool{[&closure]
|
||||
(const event::id &event_id)
|
||||
{
|
||||
closure(event_id);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::id::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return type?
|
||||
for_each(type, string_view{}, closure):
|
||||
for_each(closure);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::closure_idx &closure)
|
||||
const
|
||||
{
|
||||
for_each(type, event::closure_idx_bool{[&closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
closure(event_idx);
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const event::closure_idx_bool &closure)
|
||||
const
|
||||
{
|
||||
return type?
|
||||
for_each(type, string_view{}, closure):
|
||||
for_each(closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return type?
|
||||
for_each(type, string_view{}, closure):
|
||||
for_each(closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const string_view &state_key_lb,
|
||||
const event::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
event::fetch event
|
||||
{
|
||||
fopts? *fopts : event::fetch::default_opts
|
||||
};
|
||||
|
||||
return for_each(type, state_key_lb, event::closure_idx_bool{[&event, &closure]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
if(seek(event, event_idx, std::nothrow))
|
||||
if(!closure(event))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const string_view &state_key_lb,
|
||||
const event::id::closure_bool &closure)
|
||||
const
|
||||
{
|
||||
return for_each(type, state_key_lb, event::closure_idx_bool{[&closure]
|
||||
(const event::idx &idx)
|
||||
{
|
||||
bool ret{true};
|
||||
m::event_id(idx, std::nothrow, [&ret, &closure]
|
||||
(const event::id &id)
|
||||
{
|
||||
ret = closure(id);
|
||||
});
|
||||
|
||||
return ret;
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const string_view &state_key_lb,
|
||||
const event::closure_idx_bool &closure)
|
||||
const
|
||||
{
|
||||
return for_each(type, state_key_lb, closure_bool{[&closure]
|
||||
(const string_view &type, const string_view &state_key, const event::idx &event_idx)
|
||||
{
|
||||
return closure(event_idx);
|
||||
}});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::for_each(const string_view &type,
|
||||
const string_view &state_key_lb,
|
||||
const closure_bool &closure)
|
||||
const
|
||||
{
|
||||
if(!present())
|
||||
{
|
||||
const history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
return history.for_each(type, state_key_lb, [&closure]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
return closure(type, state_key, event_idx);
|
||||
});
|
||||
}
|
||||
|
||||
char keybuf[dbs::ROOM_STATE_KEY_MAX_SIZE];
|
||||
const auto &key
|
||||
{
|
||||
dbs::room_state_key(keybuf, room_id, type, state_key_lb)
|
||||
};
|
||||
|
||||
db::gopts opts
|
||||
{
|
||||
this->fopts? this->fopts->gopts : db::gopts{}
|
||||
};
|
||||
|
||||
if(!opts.readahead)
|
||||
opts.readahead = size_t(readahead_size);
|
||||
|
||||
auto &column{dbs::room_state};
|
||||
for(auto it{column.begin(key, opts)}; bool(it); ++it)
|
||||
{
|
||||
const auto key
|
||||
{
|
||||
dbs::room_state_key(it->first)
|
||||
};
|
||||
|
||||
if(std::get<0>(key) != type)
|
||||
break;
|
||||
|
||||
const byte_view<event::idx> idx(it->second);
|
||||
if(!closure(std::get<0>(key), std::get<1>(key), idx))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Figure out if this instance of room::state is presenting the current
|
||||
/// "present" state of the room or the state of the room at some previous
|
||||
/// event. This is an important distinction because the present state of
|
||||
/// the room should provide optimal performance for the functions of this
|
||||
/// interface by using the present state table. Prior states will use the
|
||||
/// state btree.
|
||||
bool
|
||||
ircd::m::room::state::present()
|
||||
const
|
||||
{
|
||||
// When no event_id is passed to the state constructor that immediately
|
||||
// indicates the present state of the room is sought.
|
||||
if(!event_id)
|
||||
return true;
|
||||
|
||||
// When the global configuration disables history, always consider the
|
||||
// present state. (disabling may yield unexpected incorrect results by
|
||||
// returning the present state without error).
|
||||
if(!enable_history)
|
||||
return true;
|
||||
|
||||
// Check the cached value from a previous false result of this function
|
||||
// before doing any real work/IO below. If this function ever returned
|
||||
// false it will never return true after.
|
||||
if(_not_present)
|
||||
return false;
|
||||
|
||||
const auto head_id
|
||||
{
|
||||
m::head(std::nothrow, room_id)
|
||||
};
|
||||
|
||||
// If the event_id passed is exactly the latest event we can obviously
|
||||
// consider this the present state.
|
||||
if(!head_id || head_id == event_id)
|
||||
return true;
|
||||
|
||||
// This result is cacheable because once it's no longer the present
|
||||
// it will never be again. Panta chorei kai ouden menei. Note that this
|
||||
// is a const member function; the cache variable is an appropriate case
|
||||
// for the 'mutable' keyword.
|
||||
_not_present = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::is(const event::idx &event_idx)
|
||||
{
|
||||
bool ret{false};
|
||||
m::get(event_idx, "state_key", [&ret]
|
||||
(const string_view &state_key)
|
||||
{
|
||||
ret = true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::is(std::nothrow_t,
|
||||
const event::idx &event_idx)
|
||||
{
|
||||
bool ret{false};
|
||||
m::get(std::nothrow, event_idx, "state_key", [&ret]
|
||||
(const string_view &state_key)
|
||||
{
|
||||
ret = true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::purge_replaced(const room::id &room_id)
|
||||
{
|
||||
db::txn txn
|
||||
{
|
||||
*m::dbs::events
|
||||
};
|
||||
|
||||
size_t ret(0);
|
||||
m::room::events it
|
||||
{
|
||||
room_id, uint64_t(0)
|
||||
};
|
||||
|
||||
if(!it)
|
||||
return ret;
|
||||
|
||||
for(; it; ++it)
|
||||
{
|
||||
const m::event::idx &event_idx(it.event_idx());
|
||||
if(!m::get(std::nothrow, event_idx, "state_key", [](const auto &) {}))
|
||||
continue;
|
||||
|
||||
if(!m::event::refs(event_idx).count(m::dbs::ref::NEXT_STATE))
|
||||
continue;
|
||||
|
||||
// TODO: erase event
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::present(const event::idx &event_idx)
|
||||
{
|
||||
static const event::fetch::opts fopts
|
||||
{
|
||||
event::keys::include { "room_id", "type", "state_key" },
|
||||
};
|
||||
|
||||
const m::event::fetch event
|
||||
{
|
||||
event_idx, fopts
|
||||
};
|
||||
|
||||
const m::room room
|
||||
{
|
||||
at<"room_id"_>(event)
|
||||
};
|
||||
|
||||
const m::room::state state
|
||||
{
|
||||
room
|
||||
};
|
||||
|
||||
const auto state_idx
|
||||
{
|
||||
state.get(std::nothrow, at<"type"_>(event), at<"state_key"_>(event))
|
||||
};
|
||||
|
||||
assert(event_idx);
|
||||
return state_idx == event_idx;
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::prev(const event::idx &event_idx)
|
||||
{
|
||||
event::idx ret{0};
|
||||
prev(event_idx, [&ret]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
if(event_idx > ret)
|
||||
ret = event_idx;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::next(const event::idx &event_idx)
|
||||
{
|
||||
event::idx ret{0};
|
||||
next(event_idx, [&ret]
|
||||
(const event::idx &event_idx)
|
||||
{
|
||||
if(event_idx > ret)
|
||||
ret = event_idx;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::next(const event::idx &event_idx,
|
||||
const event::closure_idx_bool &closure)
|
||||
{
|
||||
const m::event::refs refs
|
||||
{
|
||||
event_idx
|
||||
};
|
||||
|
||||
return refs.for_each(dbs::ref::NEXT_STATE, [&closure]
|
||||
(const event::idx &event_idx, const dbs::ref &ref)
|
||||
{
|
||||
assert(ref == dbs::ref::NEXT_STATE);
|
||||
return closure(event_idx);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::prev(const event::idx &event_idx,
|
||||
const event::closure_idx_bool &closure)
|
||||
{
|
||||
const m::event::refs refs
|
||||
{
|
||||
event_idx
|
||||
};
|
||||
|
||||
return refs.for_each(dbs::ref::PREV_STATE, [&closure]
|
||||
(const event::idx &event_idx, const dbs::ref &ref)
|
||||
{
|
||||
assert(ref == dbs::ref::PREV_STATE);
|
||||
return closure(event_idx);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// state::rebuild
|
||||
//
|
||||
|
||||
ircd::m::room::state::rebuild::rebuild(const room::id &room_id)
|
||||
{
|
||||
const m::event::id::buf event_id
|
||||
{
|
||||
m::head(room_id)
|
||||
};
|
||||
|
||||
const m::room::state::history history
|
||||
{
|
||||
room_id, event_id
|
||||
};
|
||||
|
||||
const m::room::state present_state
|
||||
{
|
||||
room_id
|
||||
};
|
||||
|
||||
const bool check_auth
|
||||
{
|
||||
!m::internal(room_id)
|
||||
};
|
||||
|
||||
m::dbs::write_opts opts;
|
||||
opts.appendix.reset();
|
||||
opts.appendix.set(dbs::appendix::ROOM_STATE);
|
||||
opts.appendix.set(dbs::appendix::ROOM_JOINED);
|
||||
db::txn txn
|
||||
{
|
||||
*m::dbs::events
|
||||
};
|
||||
|
||||
ssize_t deleted(0);
|
||||
present_state.for_each([&opts, &txn, &deleted]
|
||||
(const auto &type, const auto &state_key, const auto &event_idx)
|
||||
{
|
||||
const m::event::fetch &event
|
||||
{
|
||||
event_idx, std::nothrow
|
||||
};
|
||||
|
||||
if(!event.valid)
|
||||
return true;
|
||||
|
||||
auto _opts(opts);
|
||||
_opts.op = db::op::DELETE;
|
||||
_opts.event_idx = event_idx;
|
||||
dbs::write(txn, event, _opts);
|
||||
++deleted;
|
||||
return true;
|
||||
});
|
||||
|
||||
ssize_t added(0);
|
||||
history.for_each([&opts, &txn, &added, &room_id, &check_auth]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
const m::event::fetch &event
|
||||
{
|
||||
event_idx, std::nothrow
|
||||
};
|
||||
|
||||
if(!event.valid)
|
||||
return true;
|
||||
|
||||
const auto &[pass, fail]
|
||||
{
|
||||
check_auth?
|
||||
auth::check_present(event):
|
||||
room::auth::passfail{true, {}}
|
||||
};
|
||||
|
||||
if(!pass)
|
||||
{
|
||||
log::dwarning
|
||||
{
|
||||
log, "%s fails for present state in %s :%s",
|
||||
string_view{event.event_id},
|
||||
string_view{room_id},
|
||||
what(fail),
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto _opts(opts);
|
||||
_opts.op = db::op::SET;
|
||||
_opts.event_idx = event_idx;
|
||||
dbs::write(txn, event, _opts);
|
||||
++added;
|
||||
return true;
|
||||
});
|
||||
|
||||
log::info
|
||||
{
|
||||
log, "Present state of %s @ %s rebuild complete with %zu size:%s del:%zd add:%zd (%zd)",
|
||||
string_view{room_id},
|
||||
string_view{event_id},
|
||||
txn.size(),
|
||||
pretty(iec(txn.bytes())),
|
||||
deleted,
|
||||
added,
|
||||
(added - deleted),
|
||||
};
|
||||
|
||||
txn();
|
||||
}
|
197
matrix/room_state_history.cc
Normal file
197
matrix/room_state_history.cc
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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.
|
||||
|
||||
|
||||
//
|
||||
// room::state::history
|
||||
//
|
||||
|
||||
ircd::m::room::state::history::history(const m::room &room)
|
||||
:history
|
||||
{
|
||||
room, -1
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::state::history::history(const m::room::id &room_id,
|
||||
const m::event::id &event_id)
|
||||
:history
|
||||
{
|
||||
m::room
|
||||
{
|
||||
room_id, event_id
|
||||
}
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
ircd::m::room::state::history::history(const m::room &room,
|
||||
const int64_t &bound)
|
||||
:space
|
||||
{
|
||||
room
|
||||
}
|
||||
,bound
|
||||
{
|
||||
bound < 0 && room.event_id?
|
||||
m::get<int64_t>(m::index(room.event_id), "depth"):
|
||||
bound
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::prefetch(const string_view &type)
|
||||
const
|
||||
{
|
||||
return prefetch(type, string_view{});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::prefetch(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
return space.prefetch(type, state_key, bound);
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::history::get(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
const auto ret
|
||||
{
|
||||
get(std::nothrow, type, state_key)
|
||||
};
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw m::NOT_FOUND
|
||||
{
|
||||
"(%s,%s) in %s @%ld$%s",
|
||||
type,
|
||||
state_key,
|
||||
string_view{space.room.room_id},
|
||||
bound,
|
||||
string_view{space.room.event_id},
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ircd::m::event::idx
|
||||
ircd::m::room::state::history::get(std::nothrow_t,
|
||||
const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
event::idx ret{0};
|
||||
assert(type && defined(state_key));
|
||||
for_each(type, state_key, [&ret]
|
||||
(const auto &, const auto &, const auto &, const auto &event_idx)
|
||||
{
|
||||
ret = event_idx;
|
||||
return false;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::has(const string_view &type)
|
||||
const
|
||||
{
|
||||
return has(type, string_view{});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::has(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
return !for_each(type, state_key, []
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::history::count(const string_view &type)
|
||||
const
|
||||
{
|
||||
return count(type, string_view{});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::history::count(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each(type, state_key, [&ret]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
++ret;
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::for_each(const closure &closure)
|
||||
const
|
||||
{
|
||||
return for_each(string_view{}, string_view{}, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::for_each(const string_view &type,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
return for_each(type, string_view{}, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::history::for_each(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
char type_buf[m::event::TYPE_MAX_SIZE];
|
||||
char state_key_buf[m::event::STATE_KEY_MAX_SIZE];
|
||||
|
||||
string_view last_type;
|
||||
string_view last_state_key;
|
||||
|
||||
return space.for_each(type, state_key, [&]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
if(bound > -1 && depth >= bound)
|
||||
return true;
|
||||
|
||||
if(type == last_type && state_key == last_state_key)
|
||||
return true;
|
||||
|
||||
if(!closure(type, state_key, depth, event_idx))
|
||||
return false;
|
||||
|
||||
if(type != last_type)
|
||||
last_type = { type_buf, copy(type_buf, type) };
|
||||
|
||||
if(state_key != last_state_key)
|
||||
last_state_key = { state_key_buf, copy(state_key_buf, state_key) };
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
304
matrix/room_state_space.cc
Normal file
304
matrix/room_state_space.cc
Normal file
|
@ -0,0 +1,304 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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::m::room::state::space::space(const m::room &room)
|
||||
:room
|
||||
{
|
||||
room
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::prefetch(const string_view &type)
|
||||
const
|
||||
{
|
||||
return prefetch(type, string_view{});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::prefetch(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
return prefetch(type, state_key, -1);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::prefetch(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const int64_t &depth)
|
||||
const
|
||||
{
|
||||
const int64_t &_depth
|
||||
{
|
||||
type? depth : 0L
|
||||
};
|
||||
|
||||
char buf[dbs::ROOM_STATE_SPACE_KEY_MAX_SIZE];
|
||||
const string_view &key
|
||||
{
|
||||
dbs::room_state_space_key(buf, room.room_id, type, state_key, _depth, 0UL)
|
||||
};
|
||||
|
||||
return db::prefetch(dbs::room_state_space, key);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::has(const string_view &type)
|
||||
const
|
||||
{
|
||||
return has(type, string_view{});
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::has(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
return has(type, state_key, -1);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::has(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const int64_t &depth)
|
||||
const
|
||||
{
|
||||
return !for_each(type, state_key, depth, []
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::space::count()
|
||||
const
|
||||
{
|
||||
return count(string_view{});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::space::count(const string_view &type)
|
||||
const
|
||||
{
|
||||
return count(type, string_view{});
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::space::count(const string_view &type,
|
||||
const string_view &state_key)
|
||||
const
|
||||
{
|
||||
return count(type, state_key, -1L);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::state::space::count(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const int64_t &depth)
|
||||
const
|
||||
{
|
||||
size_t ret(0);
|
||||
for_each(type, state_key, depth, [&ret]
|
||||
(const auto &type, const auto &state_key, const auto &depth, const auto &event_idx)
|
||||
{
|
||||
++ret;
|
||||
return true;
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::for_each(const closure &closure)
|
||||
const
|
||||
{
|
||||
return for_each(string_view{}, string_view{}, -1L, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::for_each(const string_view &type,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
return for_each(type, string_view{}, -1L, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::for_each(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
return for_each(type, state_key, -1L, closure);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::m::room::state::space::for_each(const string_view &type,
|
||||
const string_view &state_key,
|
||||
const int64_t &depth,
|
||||
const closure &closure)
|
||||
const
|
||||
{
|
||||
const int64_t &_depth
|
||||
{
|
||||
type? depth : 0L
|
||||
};
|
||||
|
||||
char buf[dbs::ROOM_STATE_SPACE_KEY_MAX_SIZE];
|
||||
const string_view &key
|
||||
{
|
||||
dbs::room_state_space_key(buf, room.room_id, type, state_key, _depth, 0UL)
|
||||
};
|
||||
|
||||
auto it
|
||||
{
|
||||
dbs::room_state_space.begin(key)
|
||||
};
|
||||
|
||||
for(; it; ++it)
|
||||
{
|
||||
const auto &[_type, _state_key, _depth, _event_idx]
|
||||
{
|
||||
dbs::room_state_space_key(it->first)
|
||||
};
|
||||
|
||||
if(type && type != _type)
|
||||
break;
|
||||
|
||||
if(state_key && state_key != _state_key)
|
||||
break;
|
||||
|
||||
if(depth >= 0 && depth != _depth)
|
||||
break;
|
||||
|
||||
if(!closure(_type, _state_key, _depth, _event_idx))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// room::state::space::rebuild
|
||||
//
|
||||
|
||||
ircd::m::room::state::space::rebuild::rebuild(const room::id &room_id)
|
||||
{
|
||||
db::txn txn
|
||||
{
|
||||
*m::dbs::events
|
||||
};
|
||||
|
||||
m::room::events it
|
||||
{
|
||||
room_id, uint64_t(0)
|
||||
};
|
||||
|
||||
if(!it)
|
||||
return;
|
||||
|
||||
const bool check_auth
|
||||
{
|
||||
!m::internal(room_id)
|
||||
};
|
||||
|
||||
size_t state_count(0), messages_count(0), state_deleted(0);
|
||||
for(; it; ++it, ++messages_count) try
|
||||
{
|
||||
const m::event::idx &event_idx
|
||||
{
|
||||
it.event_idx()
|
||||
};
|
||||
|
||||
if(!state::is(std::nothrow, event_idx))
|
||||
continue;
|
||||
|
||||
++state_count;
|
||||
const m::event &event{*it};
|
||||
const auto &[pass_static, reason_static]
|
||||
{
|
||||
check_auth?
|
||||
room::auth::check_static(event):
|
||||
room::auth::passfail{true, {}}
|
||||
};
|
||||
|
||||
if(!pass_static)
|
||||
log::dwarning
|
||||
{
|
||||
log, "%s in %s erased from state space (static) :%s",
|
||||
string_view{event.event_id},
|
||||
string_view{room_id},
|
||||
what(reason_static),
|
||||
};
|
||||
|
||||
const auto &[pass_relative, reason_relative]
|
||||
{
|
||||
!check_auth?
|
||||
room::auth::passfail{true, {}}:
|
||||
pass_static?
|
||||
room::auth::check_relative(event):
|
||||
room::auth::passfail{false, {}},
|
||||
};
|
||||
|
||||
if(pass_static && !pass_relative)
|
||||
log::dwarning
|
||||
{
|
||||
log, "%s in %s erased from state space (relative) :%s",
|
||||
string_view{event.event_id},
|
||||
string_view{room_id},
|
||||
what(reason_relative),
|
||||
};
|
||||
|
||||
dbs::write_opts opts;
|
||||
opts.event_idx = event_idx;
|
||||
|
||||
opts.appendix.reset();
|
||||
opts.appendix.set(dbs::appendix::ROOM_STATE_SPACE);
|
||||
|
||||
opts.op = pass_static && pass_relative? db::op::SET : db::op::DELETE;
|
||||
state_deleted += opts.op == db::op::DELETE;
|
||||
|
||||
dbs::write(txn, event, opts);
|
||||
}
|
||||
catch(const ctx::interrupted &e)
|
||||
{
|
||||
log::dwarning
|
||||
{
|
||||
log, "room::state::space::rebuild :%s",
|
||||
e.what()
|
||||
};
|
||||
|
||||
throw;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::error
|
||||
{
|
||||
log, "room::state::space::rebuild :%s",
|
||||
e.what()
|
||||
};
|
||||
}
|
||||
|
||||
log::info
|
||||
{
|
||||
log, "room::state::space::rebuild %s complete msgs:%zu state:%zu del:%zu transaction elems:%zu size:%s",
|
||||
string_view{room_id},
|
||||
messages_count,
|
||||
state_count,
|
||||
state_deleted,
|
||||
txn.size(),
|
||||
pretty(iec(txn.bytes()))
|
||||
};
|
||||
|
||||
txn();
|
||||
}
|
66
matrix/room_stats.cc
Normal file
66
matrix/room_stats.cc
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Matrix Construct
|
||||
//
|
||||
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
||||
// Copyright (C) 2016-2019 Jason Volk <jason@zemos.net>
|
||||
//
|
||||
// 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.
|
||||
|
||||
size_t
|
||||
__attribute__((noreturn))
|
||||
ircd::m::room::stats::bytes_total(const m::room &room)
|
||||
{
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Not yet implemented."
|
||||
};
|
||||
}
|
||||
|
||||
size_t
|
||||
__attribute__((noreturn))
|
||||
ircd::m::room::stats::bytes_total_compressed(const m::room &room)
|
||||
{
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Not yet implemented."
|
||||
};
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::m::room::stats::bytes_json(const m::room &room)
|
||||
{
|
||||
size_t ret(0);
|
||||
for(m::room::events it(room); it; --it)
|
||||
{
|
||||
const m::event::idx &event_idx
|
||||
{
|
||||
it.event_idx()
|
||||
};
|
||||
|
||||
const byte_view<string_view> key
|
||||
{
|
||||
event_idx
|
||||
};
|
||||
|
||||
static const db::gopts gopts
|
||||
{
|
||||
db::get::NO_CACHE
|
||||
};
|
||||
|
||||
ret += db::bytes_value(m::dbs::event_json, key, gopts);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t
|
||||
__attribute__((noreturn))
|
||||
ircd::m::room::stats::bytes_json_compressed(const m::room &room)
|
||||
{
|
||||
throw m::UNSUPPORTED
|
||||
{
|
||||
"Not yet implemented."
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue