data/MailSelectionSnapshot.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "MailSelectionSnapshot.h" | ||
| 2 | #include <QFile> | ||
| 3 | #include <QDir> | ||
| 4 | #include <QtEndian> | ||
| 5 | #include <limits> | ||
| 6 | |||
| 7 | namespace { | ||
| 8 | 248 | bool open(QFile &file, const std::shared_ptr<const MailSelectionSnapshot> &snapshot, | |
| 9 | MailSelectionResult &result) { | ||
| 10 |
7/8✓ Branch 3 → 4 taken 245 times.
✓ Branch 3 → 12 taken 3 times.
✓ Branch 6 → 7 taken 245 times.
✗ Branch 6 → 12 not taken.
✓ Branch 8 → 9 taken 244 times.
✓ Branch 8 → 12 taken 1 time.
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 22 taken 243 times.
|
492 | if (!snapshot || !snapshot->storage || snapshot->count < 0 || |
| 11 |
2/2✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 243 times.
|
244 | snapshot->count > std::numeric_limits<qint64>::max() / 8) { |
| 12 | 5 | result.error = QStringLiteral("Invalid selection snapshot"); | |
| 13 | 5 | return false; | |
| 14 | } | ||
| 15 |
2/2✓ Branch 23 → 24 taken 243 times.
✓ Branch 24 → 25 taken 243 times.
|
243 | file.setFileName(snapshot->path()); |
| 16 |
7/8✓ Branch 27 → 28 taken 243 times.
✓ Branch 28 → 29 taken 243 times.
✗ Branch 28 → 32 not taken.
✓ Branch 29 → 30 taken 243 times.
✓ Branch 31 → 32 taken 4 times.
✓ Branch 31 → 33 taken 239 times.
✓ Branch 34 → 35 taken 4 times.
✓ Branch 34 → 42 taken 239 times.
|
243 | if (!file.open(QIODevice::ReadOnly) || file.size() != snapshot->count * 8) { |
| 17 | 4 | result.error = QStringLiteral("Selection snapshot is unavailable or truncated"); | |
| 18 | 4 | return false; | |
| 19 | } | ||
| 20 | 239 | result.snapshot = snapshot; | |
| 21 | 239 | return true; | |
| 22 | } | ||
| 23 | 11686 | bool readUid(QFile &file, qint64 index, qint64 &uid) { | |
| 24 | uchar bytes[8]; | ||
| 25 |
5/8✓ Branch 2 → 3 taken 11686 times.
✓ Branch 3 → 4 taken 11686 times.
✗ Branch 3 → 6 not taken.
✓ Branch 4 → 5 taken 11686 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 11686 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 11686 times.
|
11686 | if (!file.seek(index * 8) || file.read(reinterpret_cast<char *>(bytes), 8) != 8) return false; |
| 26 |
1/1✓ Branch 10 → 11 taken 11686 times.
|
11686 | uid = qFromLittleEndian<qint64>(bytes); |
| 27 | 11686 | return uid >= 0; | |
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | 154 | MailSelectionResult MailSelectionReader::page(std::shared_ptr<const MailSelectionSnapshot> snapshot, | |
| 32 | qint64 offset, int limit) { | ||
| 33 | 154 | MailSelectionResult result; | |
| 34 |
6/6✓ Branch 2 → 3 taken 152 times.
✓ Branch 2 → 5 taken 2 times.
✓ Branch 3 → 4 taken 151 times.
✓ Branch 3 → 5 taken 1 time.
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 12 taken 149 times.
|
154 | if (offset < 0 || limit < 1 || limit > MaxBatch) { |
| 35 | 5 | result.error = QStringLiteral("Invalid selection page bounds"); | |
| 36 | 5 | return result; | |
| 37 | } | ||
| 38 |
1/1✓ Branch 12 → 13 taken 149 times.
|
149 | QFile file; |
| 39 |
3/3✓ Branch 13 → 14 taken 149 times.
✓ Branch 14 → 15 taken 5 times.
✓ Branch 14 → 16 taken 144 times.
|
149 | if (!open(file, snapshot, result)) return result; |
| 40 |
2/2✓ Branch 17 → 18 taken 142 times.
✓ Branch 17 → 21 taken 2 times.
|
144 | const auto end = offset >= snapshot->count ? offset : offset + qMin<qint64>(limit, snapshot->count - offset); |
| 41 |
2/2✓ Branch 35 → 23 taken 1726 times.
✓ Branch 35 → 36 taken 143 times.
|
1869 | for (qint64 row = offset; row < end; ++row) { |
| 42 | 1726 | qint64 uid = -1; | |
| 43 |
3/3✓ Branch 23 → 24 taken 1726 times.
✓ Branch 24 → 25 taken 1 time.
✓ Branch 24 → 33 taken 1725 times.
|
1726 | if (!readUid(file, row, uid)) { |
| 44 |
1/1✓ Branch 25 → 26 taken 1 time.
|
1 | result.uids.clear(); |
| 45 | 1 | result.error = QStringLiteral("Could not read selection identity"); | |
| 46 | 1 | return result; | |
| 47 | } | ||
| 48 |
1/1✓ Branch 33 → 34 taken 1725 times.
|
1725 | result.uids.append(uid); |
| 49 | } | ||
| 50 | 143 | result.outcome = MailSelectionResult::Outcome::Ready; | |
| 51 | 143 | return result; | |
| 52 | 149 | } | |
| 53 | |||
| 54 | 76 | MailSelectionResult MailSelectionReader::lookup(std::shared_ptr<const MailSelectionSnapshot> snapshot, | |
| 55 | const QList<qint64> &uids) { | ||
| 56 | 76 | MailSelectionResult result; | |
| 57 |
2/2✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 11 taken 74 times.
|
76 | if (uids.size() > MaxBatch) { |
| 58 | 2 | result.error = QStringLiteral("Selection membership batch exceeds limit"); | |
| 59 | 2 | return result; | |
| 60 | } | ||
| 61 |
1/1✓ Branch 11 → 12 taken 74 times.
|
74 | QFile file; |
| 62 |
3/3✓ Branch 12 → 13 taken 74 times.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 72 times.
|
74 | if (!open(file, snapshot, result)) return result; |
| 63 |
2/2✓ Branch 41 → 17 taken 2267 times.
✓ Branch 41 → 42 taken 71 times.
|
2338 | for (const auto uid : uids) { |
| 64 | 2267 | qint64 low = 0, high = snapshot->count; | |
| 65 | 2267 | bool found = false; | |
| 66 |
2/2✓ Branch 37 → 20 taken 9960 times.
✓ Branch 37 → 38 taken 1308 times.
|
11268 | while (low < high) { |
| 67 | 9960 | const qint64 middle = low + (high - low) / 2; | |
| 68 | 9960 | qint64 candidate = -1; | |
| 69 |
3/3✓ Branch 20 → 21 taken 9960 times.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 30 taken 9959 times.
|
9960 | if (!readUid(file, middle, candidate)) { |
| 70 | 1 | result.membership.clear(); | |
| 71 | 1 | result.error = QStringLiteral("Could not read selection membership"); | |
| 72 | 1 | return result; | |
| 73 | } | ||
| 74 |
2/2✓ Branch 30 → 31 taken 958 times.
✓ Branch 30 → 32 taken 9001 times.
|
9959 | if (candidate == uid) { found = true; break; } |
| 75 |
2/2✓ Branch 32 → 33 taken 5687 times.
✓ Branch 32 → 34 taken 3314 times.
|
9001 | if (candidate < uid) low = middle + 1; |
| 76 | 3314 | else high = middle; | |
| 77 | } | ||
| 78 |
1/1✓ Branch 38 → 39 taken 2266 times.
|
2266 | result.membership.insert(uid, found); |
| 79 | } | ||
| 80 | 71 | result.outcome = MailSelectionResult::Outcome::Ready; | |
| 81 | 71 | return result; | |
| 82 | 74 | } | |
| 83 | |||
| 84 | 29 | MailSelectionResult MailSelectionReader::toggle(std::shared_ptr<const MailSelectionSnapshot> snapshot, | |
| 85 | qint64 uid, const std::atomic_bool &cancelled) { | ||
| 86 | 29 | MailSelectionResult result; | |
| 87 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 10 taken 27 times.
|
31 | if (uid < 0) { result.error = QStringLiteral("Invalid selection identity"); return result; } |
| 88 |
2/2✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 13 taken 25 times.
|
27 | if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; } |
| 89 |
1/1✓ Branch 13 → 14 taken 25 times.
|
25 | QFile input; |
| 90 |
3/3✓ Branch 14 → 15 taken 25 times.
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 17 taken 23 times.
|
25 | if (!open(input, snapshot, result)) return result; |
| 91 | 23 | result.snapshot.reset(); | |
| 92 |
1/1✓ Branch 19 → 20 taken 23 times.
|
23 | auto outputSnapshot = std::make_shared<MailSelectionSnapshot>(*snapshot); |
| 93 | 23 | outputSnapshot->count = 0; | |
| 94 |
3/3✓ Branch 21 → 22 taken 23 times.
✓ Branch 22 → 23 taken 23 times.
✓ Branch 23 → 24 taken 23 times.
|
23 | outputSnapshot->storage = std::make_shared<QTemporaryDir>(QDir::tempPath() + "/mailjd-selection-XXXXXX"); |
| 95 |
2/4✓ Branch 31 → 32 taken 23 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 39 taken 23 times.
✗ Branch 35 → 36 not taken.
|
23 | if (!outputSnapshot->storage->isValid()) { result.error = outputSnapshot->storage->errorString(); return result; } |
| 96 |
2/2✓ Branch 40 → 41 taken 23 times.
✓ Branch 41 → 42 taken 23 times.
|
23 | QFile output(outputSnapshot->path()); |
| 97 |
5/8✓ Branch 44 → 45 taken 23 times.
✓ Branch 45 → 46 taken 23 times.
✗ Branch 45 → 49 not taken.
✓ Branch 47 → 48 taken 23 times.
✗ Branch 48 → 49 not taken.
✓ Branch 48 → 50 taken 23 times.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 56 taken 23 times.
|
23 | if (!output.open(QIODevice::WriteOnly) || !output.setPermissions(QFile::ReadOwner | QFile::WriteOwner)) { |
| 98 | ✗ | result.error = output.errorString(); return result; | |
| 99 | } | ||
| 100 | 23 | QByteArray buffer; | |
| 101 |
1/1✓ Branch 56 → 57 taken 23 times.
|
23 | buffer.reserve(64 * 1024); |
| 102 | 36355 | auto append = [&](qint64 value) { | |
| 103 | 36355 | const auto encoded = qToLittleEndian(value); | |
| 104 |
1/1✓ Branch 3 → 4 taken 36355 times.
|
36355 | buffer.append(reinterpret_cast<const char *>(&encoded), 8); |
| 105 | 36355 | ++outputSnapshot->count; | |
| 106 |
2/2✓ Branch 6 → 7 taken 36351 times.
✓ Branch 6 → 8 taken 4 times.
|
36355 | if (buffer.size() < 64 * 1024) return true; |
| 107 |
2/3✓ Branch 8 → 9 taken 4 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 4 times.
|
4 | if (output.write(buffer) != buffer.size()) return false; |
| 108 |
1/1✓ Branch 12 → 13 taken 4 times.
|
4 | buffer.clear(); |
| 109 | 4 | return true; | |
| 110 | 23 | }; | |
| 111 | 23 | bool handled = false; | |
| 112 | 23 | qint64 read = 0, previous = -1; | |
| 113 |
2/2✓ Branch 116 → 58 taken 26 times.
✓ Branch 116 → 117 taken 22 times.
|
48 | while (read < snapshot->count) { |
| 114 |
1/2✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 26 times.
|
27 | if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; } |
| 115 |
1/1✓ Branch 63 → 64 taken 26 times.
|
26 | const auto bytes = input.read(qMin<qint64>(8192, snapshot->count - read) * 8); |
| 116 |
3/6✓ Branch 65 → 66 taken 26 times.
✗ Branch 65 → 68 not taken.
✗ Branch 67 → 68 not taken.
✓ Branch 67 → 69 taken 26 times.
✗ Branch 70 → 71 not taken.
✓ Branch 70 → 78 taken 26 times.
|
26 | if (bytes.isEmpty() || bytes.size() % 8 != 0) { |
| 117 | ✗ | result.error = QStringLiteral("Could not read selection identity"); return result; | |
| 118 | } | ||
| 119 |
2/2✓ Branch 108 → 79 taken 36345 times.
✓ Branch 108 → 109 taken 25 times.
|
36370 | for (qsizetype offset = 0; offset < bytes.size(); offset += 8) { |
| 120 |
1/1✓ Branch 80 → 81 taken 36345 times.
|
36345 | const auto value = qFromLittleEndian<qint64>(bytes.constData() + offset); |
| 121 |
2/2✓ Branch 81 → 82 taken 1 time.
✓ Branch 81 → 89 taken 36344 times.
|
36346 | if (value <= previous) { result.error = QStringLiteral("Invalid selection ordering"); return result; } |
| 122 | 36344 | previous = value; | |
| 123 | 36344 | ++read; | |
| 124 |
2/2✓ Branch 89 → 90 taken 6 times.
✓ Branch 89 → 91 taken 36338 times.
|
36344 | if (value == uid) { handled = true; continue; } |
| 125 |
4/4✓ Branch 91 → 92 taken 25518 times.
✓ Branch 91 → 100 taken 10820 times.
✓ Branch 92 → 93 taken 12 times.
✓ Branch 92 → 100 taken 25506 times.
|
36338 | if (!handled && value > uid) { |
| 126 |
2/4✓ Branch 93 → 94 taken 12 times.
✗ Branch 94 → 95 not taken.
✓ Branch 94 → 99 taken 12 times.
✗ Branch 95 → 96 not taken.
|
12 | if (!append(uid)) { result.error = output.errorString(); return result; } |
| 127 | 12 | handled = true; | |
| 128 | } | ||
| 129 |
2/4✓ Branch 100 → 101 taken 36338 times.
✗ Branch 101 → 102 not taken.
✓ Branch 101 → 106 taken 36338 times.
✗ Branch 102 → 103 not taken.
|
36338 | if (!append(value)) { result.error = output.errorString(); return result; } |
| 130 | } | ||
| 131 | 26 | } | |
| 132 |
9/13✓ Branch 117 → 118 taken 5 times.
✓ Branch 117 → 120 taken 17 times.
✓ Branch 118 → 119 taken 5 times.
✓ Branch 119 → 120 taken 5 times.
✗ Branch 119 → 125 not taken.
✓ Branch 120 → 121 taken 22 times.
✓ Branch 122 → 123 taken 22 times.
✗ Branch 122 → 125 not taken.
✓ Branch 123 → 124 taken 22 times.
✗ Branch 124 → 125 not taken.
✓ Branch 124 → 126 taken 22 times.
✗ Branch 127 → 128 not taken.
✓ Branch 127 → 132 taken 22 times.
|
22 | if ((!handled && !append(uid)) || output.write(buffer) != buffer.size() || !output.flush()) { |
| 133 | ✗ | result.error = output.errorString(); return result; | |
| 134 | } | ||
| 135 |
1/2✗ Branch 133 → 134 not taken.
✓ Branch 133 → 135 taken 22 times.
|
22 | if (cancelled.load()) { result.outcome = MailSelectionResult::Outcome::Cancelled; return result; } |
| 136 | 22 | result.snapshot = std::move(outputSnapshot); | |
| 137 | 22 | result.outcome = MailSelectionResult::Outcome::Ready; | |
| 138 | 22 | return result; | |
| 139 | 25 | } | |
| 140 |