summary refs log tree commit diff
path: root/synapse/events/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'synapse/events/__init__.py')
-rw-r--r--synapse/events/__init__.py245
1 files changed, 166 insertions, 79 deletions
diff --git a/synapse/events/__init__.py b/synapse/events/__init__.py

index c8b52cbc7a..c04905dfed 100644 --- a/synapse/events/__init__.py +++ b/synapse/events/__init__.py
@@ -16,12 +16,15 @@ import abc import os -from typing import Dict, Optional, Tuple, Type +import zlib +from typing import Dict, List, Optional, Tuple, Type, Union -from unpaddedbase64 import encode_base64 +import attr +from unpaddedbase64 import decode_base64, encode_base64 from synapse.api.room_versions import EventFormatVersions, RoomVersion, RoomVersions from synapse.types import JsonDict, RoomStreamToken +from synapse.util import json_decoder, json_encoder from synapse.util.caches import intern_dict from synapse.util.frozenutils import freeze from synapse.util.stringutils import strtobool @@ -37,6 +40,26 @@ from synapse.util.stringutils import strtobool USE_FROZEN_DICTS = strtobool(os.environ.get("SYNAPSE_USE_FROZEN_DICTS", "0")) +_PRESET_ZDICT = b"""{"auth_events":[],"prev_events":[],"type":"m.room.member",m.room.message"room_id":,"sender":,"content":{"msgtype":"m.text","body":""room_version":"creator":"depth":"prev_state":"state_key":""origin":"origin_server_ts":"hashes":{"sha256":"signatures":,"unsigned":{"age_ts":"ed25519""" + + +def _encode_dict(d: JsonDict) -> bytes: + json_bytes = json_encoder.encode(d).encode("utf-8") + c = zlib.compressobj(1, zdict=_PRESET_ZDICT) + result_bytes = c.compress(json_bytes) + result_bytes += c.flush() + return result_bytes + + +def _decode_dict(b: bytes) -> JsonDict: + d = zlib.decompressobj(zdict=_PRESET_ZDICT) + + result_bytes = d.decompress(b) + result_bytes += d.flush() + + return json_decoder.decode(result_bytes.decode("utf-8")) + + class DictProperty: """An object property which delegates to the `_dict` within its parent object.""" @@ -205,7 +228,81 @@ class _EventInternalMetadata: return self._dict.get("redacted", False) +@attr.s(slots=True, auto_attribs=True) +class _Signatures: + _signatures_bytes: bytes + + @staticmethod + def from_dict(signature_dict: JsonDict) -> "_Signatures": + return _Signatures(_encode_dict(signature_dict)) + + def get_dict(self) -> JsonDict: + return _decode_dict(self._signatures_bytes) + + def get(self, server_name): + return self.get_dict().get(server_name) + + def update(self, other: Union[JsonDict, "_Signatures"]): + if isinstance(other, _Signatures): + other_dict = _decode_dict(other._signatures_bytes) + else: + other_dict = other + + signatures = self.get_dict() + signatures.update(other_dict) + self._signatures_bytes = _encode_dict(signatures) + + +class _SmallListV1(str): + __slots__ = [] + + def get(self): + return self.split(",") + + @staticmethod + def create(event_ids): + return _SmallListV1(",".join(event_ids)) + + +class _SmallListV2_V3(bytes): + __slots__ = [] + + def get(self, url_safe): + i = 0 + while i * 32 < len(self): + bit = self[i * 32 : (i + 1) * 32] + i += 1 + yield "$" + encode_base64(bit, urlsafe=url_safe) + + @staticmethod + def create(event_ids): + return _SmallListV2_V3( + b"".join(decode_base64(event_id[1:]) for event_id in event_ids) + ) + + class EventBase(metaclass=abc.ABCMeta): + __slots__ = [ + "room_version", + "signatures", + "unsigned", + "rejected_reason", + "_encoded_dict", + "_auth_event_ids", + "depth", + "_content", + "_hashes", + "origin", + "origin_server_ts", + "_prev_event_ids", + "redacts", + "room_id", + "sender", + "type", + "state_key", + "internal_metadata", + ] + @property @abc.abstractmethod def format_version(self) -> int: @@ -224,33 +321,45 @@ class EventBase(metaclass=abc.ABCMeta): assert room_version.event_format == self.format_version self.room_version = room_version - self.signatures = signatures + self.signatures = _Signatures.from_dict(signatures) self.unsigned = unsigned self.rejected_reason = rejected_reason - self._dict = event_dict + self._encoded_dict = _encode_dict(event_dict) + + self.depth = event_dict["depth"] + self.origin = event_dict["origin"] + self.origin_server_ts = event_dict["origin_server_ts"] + self.redacts = event_dict.get("redacts") + self.room_id = event_dict["room_id"] + self.sender = event_dict["sender"] + self.type = event_dict["type"] + if "state_key" in event_dict: + self.state_key = event_dict["state_key"] self.internal_metadata = _EventInternalMetadata(internal_metadata_dict) - auth_events = DictProperty("auth_events") - depth = DictProperty("depth") - content = DictProperty("content") - hashes = DictProperty("hashes") - origin = DictProperty("origin") - origin_server_ts = DictProperty("origin_server_ts") - prev_events = DictProperty("prev_events") - redacts = DefaultDictProperty("redacts", None) - room_id = DictProperty("room_id") - sender = DictProperty("sender") - state_key = DictProperty("state_key") - type = DictProperty("type") - user_id = DictProperty("sender") + @property + def content(self) -> JsonDict: + return self.get_dict()["content"] + + @property + def hashes(self) -> JsonDict: + return self.get_dict()["hashes"] + + @property + def prev_events(self) -> List[str]: + return list(self._prev_events) @property def event_id(self) -> str: raise NotImplementedError() @property + def user_id(self) -> str: + return self.sender + + @property def membership(self): return self.content["membership"] @@ -258,17 +367,13 @@ class EventBase(metaclass=abc.ABCMeta): return hasattr(self, "state_key") and self.state_key is not None def get_dict(self) -> JsonDict: - d = dict(self._dict) - d.update({"signatures": self.signatures, "unsigned": dict(self.unsigned)}) + d = _decode_dict(self._encoded_dict) + d.update( + {"signatures": self.signatures.get_dict(), "unsigned": dict(self.unsigned)} + ) return d - def get(self, key, default=None): - return self._dict.get(key, default) - - def get_internal_metadata_dict(self): - return self.internal_metadata.get_dict() - def get_pdu_json(self, time_now=None) -> JsonDict: pdu_json = self.get_dict() @@ -285,41 +390,11 @@ class EventBase(metaclass=abc.ABCMeta): def __set__(self, instance, value): raise AttributeError("Unrecognized attribute %s" % (instance,)) - def __getitem__(self, field): - return self._dict[field] - - def __contains__(self, field): - return field in self._dict - - def items(self): - return list(self._dict.items()) - - def keys(self): - return self._dict.keys() - - def prev_event_ids(self): - """Returns the list of prev event IDs. The order matches the order - specified in the event, though there is no meaning to it. - - Returns: - list[str]: The list of event IDs of this event's prev_events - """ - return [e for e, _ in self.prev_events] - - def auth_event_ids(self): - """Returns the list of auth event IDs. The order matches the order - specified in the event, though there is no meaning to it. - - Returns: - list[str]: The list of event IDs of this event's auth_events - """ - return [e for e, _ in self.auth_events] - def freeze(self): """'Freeze' the event dict, so it cannot be modified by accident""" # this will be a no-op if the event dict is already frozen. - self._dict = freeze(self._dict) + # self._dict = freeze(self._dict) class FrozenEvent(EventBase): @@ -355,6 +430,12 @@ class FrozenEvent(EventBase): frozen_dict = event_dict self._event_id = event_dict["event_id"] + self._auth_event_ids = _SmallListV1.create( + e for e, _ in event_dict["auth_events"] + ) + self._prev_event_ids = _SmallListV1.create( + e for e, _ in event_dict["prev_events"] + ) super().__init__( frozen_dict, @@ -369,18 +450,26 @@ class FrozenEvent(EventBase): def event_id(self) -> str: return self._event_id + def auth_event_ids(self): + return list(self._auth_event_ids.get()) + + def prev_event_ids(self): + return list(self._prev_event_ids.get()) + def __str__(self): return self.__repr__() def __repr__(self): return "<FrozenEvent event_id=%r, type=%r, state_key=%r>" % ( - self.get("event_id", None), - self.get("type", None), - self.get("state_key", None), + self.event_id, + self.type, + getattr(self, "state_key", None), ) class FrozenEventV2(EventBase): + __slots__ = ["_event_id"] + format_version = EventFormatVersions.V2 # All events of this type are V2 def __init__( @@ -415,6 +504,8 @@ class FrozenEventV2(EventBase): frozen_dict = event_dict self._event_id = None + self._auth_event_ids = _SmallListV2_V3.create(event_dict["auth_events"]) + self._prev_event_ids = _SmallListV2_V3.create(event_dict["prev_events"]) super().__init__( frozen_dict, @@ -436,24 +527,6 @@ class FrozenEventV2(EventBase): self._event_id = "$" + encode_base64(compute_event_reference_hash(self)[1]) return self._event_id - def prev_event_ids(self): - """Returns the list of prev event IDs. The order matches the order - specified in the event, though there is no meaning to it. - - Returns: - list[str]: The list of event IDs of this event's prev_events - """ - return self.prev_events - - def auth_event_ids(self): - """Returns the list of auth event IDs. The order matches the order - specified in the event, though there is no meaning to it. - - Returns: - list[str]: The list of event IDs of this event's auth_events - """ - return self.auth_events - def __str__(self): return self.__repr__() @@ -461,14 +534,22 @@ class FrozenEventV2(EventBase): return "<%s event_id=%r, type=%r, state_key=%r>" % ( self.__class__.__name__, self.event_id, - self.get("type", None), - self.get("state_key", None), + self.type, + self.state_key if self.is_state() else None, ) + def auth_event_ids(self): + return list(self._auth_event_ids.get(False)) + + def prev_event_ids(self): + return list(self._prev_event_ids.get(False)) + class FrozenEventV3(FrozenEventV2): """FrozenEventV3, which differs from FrozenEventV2 only in the event_id format""" + __slots__ = ["_event_id"] + format_version = EventFormatVersions.V3 # All events of this type are V3 @property @@ -484,6 +565,12 @@ class FrozenEventV3(FrozenEventV2): ) return self._event_id + def auth_event_ids(self): + return list(self._auth_event_ids.get(True)) + + def prev_event_ids(self): + return list(self._prev_event_ids.get(True)) + def _event_type_from_format_version(format_version: int) -> Type[EventBase]: """Returns the python type to use to construct an Event object for the