| 1 | // SPDX-FileCopyrightText: 2021 Quotient contributors |
| 2 | // SPDX-License-Identifier: LGPL-2.1-or-later |
| 3 | |
| 4 | #include "eventstats.h" |
| 5 | |
| 6 | #include "logging_categories_p.h" |
| 7 | |
| 8 | using namespace Quotient; |
| 9 | |
| 10 | EventStats EventStats::fromRange(const Room* room, const Room::rev_iter_t& from, |
| 11 | const Room::rev_iter_t& to, |
| 12 | const EventStats& init) |
| 13 | { |
| 14 | Q_ASSERT(to <= room->historyEdge()); |
| 15 | Q_ASSERT(from >= Room::rev_iter_t(room->syncEdge())); |
| 16 | Q_ASSERT(from <= to); |
| 17 | QElapsedTimer et; |
| 18 | et.start(); |
| 19 | const auto result = |
| 20 | accumulate(first: from, last: to, init: init, |
| 21 | binary_op: [room](EventStats acc, const TimelineItem& ti) { |
| 22 | acc.notableCount += room->isEventNotable(ti); |
| 23 | acc.highlightCount += room->notificationFor(ti).type |
| 24 | == Notification::Highlight; |
| 25 | return acc; |
| 26 | }); |
| 27 | if (et.nsecsElapsed() > ProfilerMinNsecs / 10) |
| 28 | qCDebug(PROFILER).nospace() |
| 29 | << "Event statistics collection over index range [" << from->index() |
| 30 | << "," << (to - 1)->index() << "] took " << et; |
| 31 | return result; |
| 32 | } |
| 33 | |
| 34 | EventStats EventStats::fromMarker(const Room* room, |
| 35 | const EventStats::marker_t& marker) |
| 36 | { |
| 37 | const auto s = fromRange(room, from: marker_t(room->syncEdge()), to: marker, |
| 38 | init: { .notableCount: 0, .highlightCount: 0, .isEstimate: marker == room->historyEdge() }); |
| 39 | Q_ASSERT(s.isValidFor(room, marker)); |
| 40 | return s; |
| 41 | } |
| 42 | |
| 43 | EventStats EventStats::fromCachedCounters(Omittable<int> notableCount, |
| 44 | Omittable<int> highlightCount) |
| 45 | { |
| 46 | const auto hCount = std::max(a: 0, b: highlightCount.value_or(u: 0)); |
| 47 | if (!notableCount.has_value()) |
| 48 | return { .notableCount: 0, .highlightCount: hCount, .isEstimate: true }; |
| 49 | auto nCount = notableCount.value_or(u: 0); |
| 50 | return { .notableCount: std::max(a: 0, b: nCount), .highlightCount: hCount, .isEstimate: nCount != -1 }; |
| 51 | } |
| 52 | |
| 53 | bool EventStats::updateOnMarkerMove(const Room* room, const marker_t& oldMarker, |
| 54 | const marker_t& newMarker) |
| 55 | { |
| 56 | if (newMarker == oldMarker) |
| 57 | return false; |
| 58 | |
| 59 | // Double-check consistency between the old marker and the old stats |
| 60 | Q_ASSERT(isValidFor(room, oldMarker)); |
| 61 | Q_ASSERT(oldMarker > newMarker); |
| 62 | |
| 63 | // A bit of optimisation: only calculate the difference if the marker moved |
| 64 | // less than half the remaining timeline ahead; otherwise, recalculation |
| 65 | // over the remaining timeline will very likely be faster. |
| 66 | if (oldMarker != room->historyEdge() |
| 67 | && oldMarker - newMarker < newMarker - marker_t(room->syncEdge())) { |
| 68 | const auto removedStats = fromRange(room, from: newMarker, to: oldMarker); |
| 69 | Q_ASSERT(notableCount >= removedStats.notableCount |
| 70 | && highlightCount >= removedStats.highlightCount); |
| 71 | notableCount -= removedStats.notableCount; |
| 72 | highlightCount -= removedStats.highlightCount; |
| 73 | return removedStats.notableCount > 0 || removedStats.highlightCount > 0; |
| 74 | } |
| 75 | |
| 76 | const auto newStats = EventStats::fromMarker(room, marker: newMarker); |
| 77 | if (!isEstimate && newStats == *this) |
| 78 | return false; |
| 79 | *this = newStats; |
| 80 | return true; |
| 81 | } |
| 82 | |
| 83 | bool EventStats::isValidFor(const Room* room, const marker_t& marker) const |
| 84 | { |
| 85 | const auto markerAtHistoryEdge = marker == room->historyEdge(); |
| 86 | // Either markerAtHistoryEdge and isEstimate are in the same state, or it's |
| 87 | // a special case of no notable events and the marker at history edge |
| 88 | // (then isEstimate can assume any value). |
| 89 | return markerAtHistoryEdge == isEstimate |
| 90 | || (markerAtHistoryEdge && notableCount == 0); |
| 91 | } |
| 92 | |
| 93 | QDebug Quotient::operator<<(QDebug dbg, const EventStats& es) |
| 94 | { |
| 95 | QDebugStateSaver _(dbg); |
| 96 | dbg.nospace() << es.notableCount << '/' << es.highlightCount; |
| 97 | if (es.isEstimate) |
| 98 | dbg << " (estimated)" ; |
| 99 | return dbg; |
| 100 | } |
| 101 | |