aboutsummaryrefslogtreecommitdiff
path: root/lib/eventstats.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/eventstats.cpp')
-rw-r--r--lib/eventstats.cpp98
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/eventstats.cpp b/lib/eventstats.cpp
new file mode 100644
index 00000000..9fa7f5ff
--- /dev/null
+++ b/lib/eventstats.cpp
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: 2021 Quotient contributors
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "eventstats.h"
+
+using namespace Quotient;
+
+EventStats EventStats::fromRange(const Room* room, const Room::rev_iter_t& from,
+ const Room::rev_iter_t& to,
+ const EventStats& init)
+{
+ Q_ASSERT(to <= room->historyEdge());
+ Q_ASSERT(from >= Room::rev_iter_t(room->syncEdge()));
+ Q_ASSERT(from <= to);
+ QElapsedTimer et;
+ et.start();
+ const auto result =
+ accumulate(from, to, init,
+ [room](EventStats acc, const TimelineItem& ti) {
+ acc.notableCount += room->isEventNotable(ti);
+ acc.highlightCount += room->notificationFor(ti).type
+ == Notification::Highlight;
+ return acc;
+ });
+ if (et.nsecsElapsed() > profilerMinNsecs() / 10)
+ qCDebug(PROFILER).nospace()
+ << "Event statistics collection over index range [" << from->index()
+ << "," << (to - 1)->index() << "] took " << et;
+ return result;
+}
+
+EventStats EventStats::fromMarker(const Room* room,
+ const EventStats::marker_t& marker)
+{
+ const auto s = fromRange(room, marker_t(room->syncEdge()), marker,
+ { 0, 0, marker == room->historyEdge() });
+ Q_ASSERT(s.isValidFor(room, marker));
+ return s;
+}
+
+EventStats EventStats::fromCachedCounters(Omittable<int> notableCount,
+ Omittable<int> highlightCount)
+{
+ const auto hCount = std::max(0, highlightCount.value_or(0));
+ if (!notableCount.has_value())
+ return { 0, hCount, true };
+ auto nCount = notableCount.value_or(0);
+ return { std::max(0, nCount), hCount, nCount != -1 };
+}
+
+bool EventStats::updateOnMarkerMove(const Room* room, const marker_t& oldMarker,
+ const marker_t& newMarker)
+{
+ if (newMarker == oldMarker)
+ return false;
+
+ // Double-check consistency between the old marker and the old stats
+ Q_ASSERT(isValidFor(room, oldMarker));
+ Q_ASSERT(oldMarker > newMarker);
+
+ // A bit of optimisation: only calculate the difference if the marker moved
+ // less than half the remaining timeline ahead; otherwise, recalculation
+ // over the remaining timeline will very likely be faster.
+ if (oldMarker != room->historyEdge()
+ && oldMarker - newMarker < newMarker - marker_t(room->syncEdge())) {
+ const auto removedStats = fromRange(room, newMarker, oldMarker);
+ Q_ASSERT(notableCount >= removedStats.notableCount
+ && highlightCount >= removedStats.highlightCount);
+ notableCount -= removedStats.notableCount;
+ highlightCount -= removedStats.highlightCount;
+ return removedStats.notableCount > 0 || removedStats.highlightCount > 0;
+ }
+
+ const auto newStats = EventStats::fromMarker(room, newMarker);
+ if (!isEstimate && newStats == *this)
+ return false;
+ *this = newStats;
+ return true;
+}
+
+bool EventStats::isValidFor(const Room* room, const marker_t& marker) const
+{
+ const auto markerAtHistoryEdge = marker == room->historyEdge();
+ // Either markerAtHistoryEdge and isEstimate are in the same state, or it's
+ // a special case of no notable events and the marker at history edge
+ // (then isEstimate can assume any value).
+ return markerAtHistoryEdge == isEstimate
+ || (markerAtHistoryEdge && notableCount == 0);
+}
+
+QDebug Quotient::operator<<(QDebug dbg, const EventStats& es)
+{
+ QDebugStateSaver _(dbg);
+ dbg.nospace() << es.notableCount << '/' << es.highlightCount;
+ if (es.isEstimate)
+ dbg << " (estimated)";
+ return dbg;
+}