Age | Commit message (Collapse) | Author |
|
Constrain types to derive from Event (or the chosen class), where
applicable.
|
|
Direct construction (using makeEvent() or explicitly constructing
an event) from JSON may create an event that has a type conflicting
with that stored in JSON. There's no such problem with loadEvent(),
even though it's considerably slower. Driven by the fact that almost
nowhere in the code direct construction is used on checked JSON
(one test is the only valid case), this commit moves all JSON-loading
constructors to the protected section, thereby disabling usage of
makeEvent() in JSON-loading capacity, and switches such cases across
the library to loadEvent().
|
|
These are small enough to comfortably reside in a single translation
unit.
|
|
This gives a more conventional API compared to queryOr() that can be
used for event objects that have content() defined - with the downside
being that content() unpacks the entire object instead of retrieving
one particular piece (but for state events and single key-value content
it's not a problem, and those make for the vast majority of events).
|
|
|
|
We don't expose logging internals to the outside world.
|
|
|
|
This commit introduces a few things to further reduce the boilerplate
across event type definitions:
- Event type is no more separately stored in Event and therefore no more
passed to base event constructors. Until the previous commit, it was
used by is() to quickly match the event type; with the new event
metatype class, the same is achieved even quicker by comparing
metatype pointers.
- EventTemplate is a generalisation of StateEvent for all event types
providing common constructor signatures and content() for (most) leaf
event types. StateEvent therefore has become a partial specialisation
of EventTemplate for types derived from StateEventBase; as the known
client code base does not use it directly, a compatibility alias is
not provided. Also, DEFINE_SIMPLE_EVENT now expands into a class
deriving from EventTemplate.
- On top of StateEvent->EventTemplate specialisation,
KeyedStateEventBase and KeylessStateEventBase types are introduced
with appropriate constructor signatures (with or without state_key,
respectively) to allow `using` of them from derived event types.
To facilitate writing of constraints, concepts for keyed and keyless
state event types are also introduced; RoomStateView, e.g., makes use
of those to provide appropriate method signatures.
- typeId(), unknownEventTypeId(), UnknownEventTypeId are no more
provided - they weren't used throughout the known code base
(Quaternion, NeoChat), and the concept of "unknown event types" is
hereby eliminated entirely.
- RoomKeyEvent no more accepts senderId as a parameter; it has never
been a good practice as the sender is assigned by Connection anyway.
|
|
The new metatype framework replaces
EventFactory/DEFINE_EVENT_TYPEID/REGISTER_EVENT_TYPE; it is faster,
more functional and extensible. Of note:
- EventMetaType mostly reproduces the logic of EventFactory but supports
custom base event types not just for loading (that part EventFactory
also supported) but also for matching - previously you had to have
Event::is*Event() for base type matching. Now Quotient::is() can
match against both base and leaf types.
- Instead of DEFINE_EVENT_TYPEID and REGISTER_EVENT_TYPE there's now
a single macro, QUO_EVENT, intended for use in the way similar to
Q_OBJECT. Actually, the entire framework borrows heavily from
QMetaObject and Q_OBJECT. Making event types full-fledged QObjects
is still not considered because half of QObject functions would not
be applicable (e.g. signals/slots) while another half (in particular,
using Matrix type ids to select event types) would still have to be
done on top of QObject. And QML can just access events as
const QJsonObjects which is arguably more lightweight as well.
- QUO_BASE_EVENT is a new macro replacing EventFactory object
definitions. This was necessary for the same reason why Q_OBJECT is
a macro: aside from a static object definition, this macro
introduces a virtual function override to resolve the metatype at
runtime. This very mechanism is used to make event type
matching/casting as quick as possible
- QUO_BASE_EVENT and QUO_EVENT use the C++20 __VA_OPT__ feature that
is only available with the new MSVC preprocessor (see
https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview);
the respective switch was added to CMakeLists.txt.
|
|
|
|
SingleKeyValue is a tiny wrapper and supposed to be discreet.
Having to explicitly (even if only with braces) construct its objects
stands in the way of readability on the consuming side of the code and
sometimes prevents direct initialisation of event objects without
constructors getting some kind of ContentParamTs parameter pack where
a single content_type argument would suffice otherwise.
|
|
[skip ci]
|
|
|
|
QByteArrays don't like interacting with QStrings
|
|
|
|
|
|
- Use a dedicated structure, EmojiEntry, instead of QVariantMap
(it's Q_GADGET, should be readable from QML just fine)
- While we're at it, QVector is better than QList in Qt 5.15
- Remove language from the session state - it's used in a single method
- Modernise handleKey() code
|
|
- Common switchOnType() piece for key verification events is factored
out into processIfVerificationEvent()
- Bare event JSON removed from KeyVerificationSession into constructors
of respective events
- Connection::sendToDevice() uses assembleEncryptedContent() introduced
in the previous commit
- commonSupportedMethods() moved out to .cpp; error/string converters
made static
|
|
What was partially factored out before into encryptSessionKeyEvent()
is now the complete algorithm converting any event json into encrypted
content.
|
|
- Use std::chrono for the timeout (it's more readable and
less ambiguous) and make it a local variable
- Only pass a Connection object once to constructors
- Ensure buildability even without E2EE (key verification is disabled
in that case)
- Reorder #includes
- Other cleanup following clang-tidy warnings
|
|
# Conflicts:
# autotests/testfilecrypto.cpp
# lib/connection.cpp
# lib/connection.h
# lib/database.cpp
# lib/database.h
# lib/e2ee/qolmoutboundsession.cpp
# lib/e2ee/qolmoutboundsession.h
# lib/eventitem.h
# lib/events/encryptedevent.cpp
# lib/events/encryptedevent.h
# lib/events/encryptedfile.cpp
# lib/events/encryptedfile.h
# lib/events/keyverificationevent.cpp
# lib/events/keyverificationevent.h
# lib/events/roomkeyevent.h
# lib/room.cpp
# lib/room.h
|
|
|
|
There's no particular reason the order of parameters in
StateEventBase::basicJson() should be as it was, and (the only)
loadStateEvent() usage in room.cpp suggests the unified order is more
convenient. Besides, this order is aligned with that in
the StateEventBase constructor.
|
|
The template version has never worked, to the point where instantiating
it would immediately lead to FTBFS. The new version returns an event
pointer as a simpler fix that would make it usable - in particular,
there's no more need to have separate
Connection::Private::unpackAccountData(). To simplify the fix,
eventCast() has been made more tolerating - passing nullptr to it is
processed in an expected (no-op) way now.
|
|
[skip ci]
|
|
|
|
|
|
This is aligned with the non-moving version.
|
|
In a situation where you have an EventPtr that you want to place
somewhere as an `event_ptr_tt<SomeMoreSpecificEventType>` you have to
carefully check that the stored event is actually of
SomeMoreSpecificType and if it is, release() that event pointer,
downcast, and re-wrap it into that new event_ptr_tt - or, as can be seen
from the diff here, re-loadEvent() from JSON, which is simpler but
inefficient. To help clients, and the library, eventCast() can now
accept an rvalue smart pointer and do all the necessary things with it.
|
|
|
|
|
|
The last commit broke it.
|
|
The upcoming event type infrastructure finally helps to detect those
omissions more or less reliably (for event types only though).
|
|
...instead of just the number of them.
|
|
Event type ids don't need a C++ type to be used, and clients might
define those types on their side (NeoChat does that, e.g.).
|
|
Profiling revealed 3 inefficiencies in read receipts code - and given
there are a lot of them coming, these inefficiences quickly add up.
Fixing them allows to slash read receipt processing time by 60%, and
the total time of updating a room by more than a half.
1. Room::lastReadEventChanged() is emitted per receipt. This can be
taxing on initial syncs or in bigger rooms; this commit converts it
to an aggregate signal only emitted once per sync room batch and
carrying the list of all user ids (more on that below) with updated
read receipts.
For that, Room::P::setLastReadEvent() is split into
Room::P::setLocalLastReadEvent() that is called whenever the local
read receipt has to be updated, and setLastReadEvent() proper that is
very fast and only updates the internal data structures, nothing else.
setLocalLastEvent() calls it, as does processEphemeralEvents(); both
take responsibility to emit lastReadEventChanged() depending on the
outcome of setLastReadEvent() invocation(s).
2. Massively aggravating the above point, user id from each read receipt
is turned to a User object - and since most of the users are unknown
at early moments, this causes thousands of allocations. Therefore
the new aggregated lastReadEventChanged() only carries user ids, and
clients will have to resolve them to User objects if they need.
3. Despite fairly tight conditions (note we're talking about thousands
of receipts), Quotient still creates an intermediate C++ structure
(EventsWithReceipts), only for the sake of passing it to
processEphemeralEvent() that immediately disassembles it back again,
converting to a series of calls to set(Local)LastReadEvent(). To fix
this, processEphemeralEvent() now takes the event content JSON
directly and iterates over it instead.
Aside from that, a few extraneous conditions and logging has been
removed and the whole function rewritten with switchOnType() to reduce
cognitive complexity.
|
|
The result of factoring out duplicate code.
|
|
|
|
If userMap only holds valid ids, there's no reason to spend time
validating the sought id: if it's invalid, it won't be found. And
lookups over a hash map are cheap.
|
|
That switch between micro- and milliseconds was pure visual sugaring,
in a potentially time-sensitive context.
Also: there's no sense in using const-ref for a small parameter in
a function that is, to top it off, almost always inlined.
|
|
|
|
This introduces enumTo/FromJsonString() and flagTo/FromJsonString(),
four facility functions to simplify conversion between C++ enums and
JSON, and refactors a couple of places where it's useful.
|
|
JSON conversions are moved out of the class, obviating the need to
define the plain data constructor and gaining default-constructibility
along the way - previously the default constructor was preempted
by user-defined ones.
|
|
EncryptionEvent was marked as Q_GADGET only for the sake of defining
EncryptionType inside of it as Q_ENUM, with aliases also available under
Quotient:: and EncryptionEventContent. This is a legacy from
pre-Q_ENUM_NS times. However, event types are not really made to be
proper Q_GADGETs: Q_GADGET implies access by value or reference
but event types are uncopyable for the former and QML is ill-equipped
for the latter.
This commit moves EncryptionType definition to where other such
enumerations reside - on the namespace level in quotient_common.h; and
the other two places are now deprecated; and EncryptionEvent is no more
Q_GADGET.
With fromJson/toJson refactored in the previous commit there's no more
need to specialise JsonConverter<>: specialising fromJson() is just
enough.
Moving EncryptionType to quotient_common.h exposed the clash
of two Undefined enumerators (in RoomType and EncryptionType),
warranting both enumerations to become scoped (which they ought to be,
anyway). And while we're at it, the base type of enumerations is
specified explicitly, as MSVC apparently uses a signed base type (int?)
by default, unlike other compilers, and the upcoming enum converters
will assume an unsigned base type.
Finally, using fillFromJson() instead of fromJson() in
the EncryptionEventContent constructor allowed to make default values
explicit in the header file, rather than buried in the initialisation
code.
|
|
fromJson() is generalised to accept any JSON-like type while passing
QJsonObject to JsonConverter<>::load (instead of doLoad). This allows to
(still) rely on JsonConverter<> as a customisation point while providing
an opportunity to overload fromJson for custom types in a pointed way
(specifically, by providing the overload for
`fromJson(const QJsonObject&)`), instead of having to go with full-blown
JsonConverter<> specialisation. This will be used in a further commit
to simplify ReceiptEvent definition.
Using if constexpr in combination with constraints (`requires()`) -
the first such case in Quotient codebase - allowed to put the entire
logic in a single JsonConverter<>::load() body instead of having a
facility JsonExporter<> class for SFINAE.
Aside from that, fromJson<QJsonValue, QJsonValue> is entirely dropped
because it's not supposed to be used that way (it's no-op after all);
reflecting that, Event::unsignedPart() and Event::contentPart() no more
default to QJsonValue as the expected return type, you have to
explicitly provide the type instead (and as one can see from the changes
in the commit, it's actually better that way since it's better
to validate the value inside JSON - e.g. check QString or QJsonObject
for emptiness - than the QJsonValue envelope which may still wrap
an empty value).
toJson() is also generalised, replacing 3 functions with one that has
a constexpr if to discern between two kinds of types.
|
|
[skip ci]
|
|
Dropping yet another translation unit.
|
|
|
|
Fixing link errors at non-template RoomStateView::get() when building
with libQuotient as a shared object. There's also a test in quotest.cpp
now to cover that case.
|
|
|