include/simdb/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
14 / 224
6.2%
Branches:
8 / 256
3.1%
Functions:
2 / 5
40.0%

apps/argos/CheckpointDeltas.hpp
Line Branch Exec Source
1 // <CheckpointDeltas.hpp> -*- C++ -*-
2
3 #pragma once
4
5 #include "simdb/Exceptions.hpp"
6 #include "simdb/utils/ValidValue.hpp"
7
8 #include <cstdint>
9 #include <iostream>
10 #include <map>
11 #include <vector>
12
13 namespace simdb::argos {
14
15 //! Result of comparing two consecutive scalar payloads.
16 enum class ScalarDeltaKind { UNCHANGED, CHANGED };
17
18 //! Compare previous and current scalar payloads.
19 //! Returns CHANGED when \p prev is empty (first collection has no baseline).
20 //! Scalars only support CARRY when UNCHANGED; CHANGED implies a FULL snapshot.
21 inline ScalarDeltaKind classifyScalarChange(const std::vector<char>& prev, const std::vector<char>& curr)
22 {
23 if (prev.empty())
24 {
25 return ScalarDeltaKind::CHANGED;
26 }
27
28 return prev == curr ? ScalarDeltaKind::UNCHANGED : ScalarDeltaKind::CHANGED;
29 }
30
31 inline std::ostream& operator<<(std::ostream& os, ScalarDeltaKind kind)
32 {
33 switch (kind)
34 {
35 case ScalarDeltaKind::UNCHANGED:
36 return os << "UNCHANGED";
37 case ScalarDeltaKind::CHANGED:
38 return os << "CHANGED";
39 }
40 throw DBException("Invalid ScalarDeltaKind");
41 }
42
43 //! Contiguous-container delta kinds.
44 enum class ContigDeltaKind { CARRY, SWAP, MULTI_SWAP, BOOKENDS, ARRIVE, DEPART, MIMO, FULL };
45
46 //! Result of classifying a contig container transition.
47 struct ContigDeltaClassification
48 {
49 ContigDeltaKind kind = ContigDeltaKind::FULL;
50 simdb::ValidValue<uint16_t> swap_index;
51 std::vector<char> payload;
52 uint8_t depart_count = 0;
53 uint8_t arrive_count = 0;
54 std::vector<std::vector<char>> arrive_payloads;
55 std::vector<uint16_t> swap_indices;
56 std::vector<std::vector<char>> swap_payloads;
57 };
58
59 29979 inline uint16_t countContigElements(const std::vector<std::vector<char>>& contig_bins)
60 {
61 29979 uint64_t count = 0;
62
2/2
✓ Branch 11 → 4 taken 142389 times.
✓ Branch 11 → 12 taken 29979 times.
172368 for (const auto& bytes : contig_bins)
63 {
64
1/2
✓ Branch 6 → 7 taken 142389 times.
✗ Branch 6 → 8 not taken.
142389 if (!bytes.empty())
65 {
66 142389 ++count;
67 } else
68 {
69 ✗ break;
70 }
71 }
72
1/9
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 23 taken 29979 times.
✗ Branch 14 → 15 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 21 → 22 not taken.
29979 simdb_assert(count <= UINT16_MAX);
73 29979 return count;
74 }
75
76 ✗ inline bool contigPrefixMatchesAfterDepart(const std::vector<std::vector<char>>& prev,
77 const std::vector<std::vector<char>>& curr, uint16_t prev_size,
78 uint16_t curr_size, uint16_t depart_count)
79 {
80 ✗ const auto overlap = static_cast<uint16_t>(prev_size - depart_count);
81
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✗ if (overlap + static_cast<uint16_t>(curr_size - overlap) != curr_size)
82 {
83 ✗ return false;
84 }
85
0/2
✗ Branch 11 → 5 not taken.
✗ Branch 11 → 12 not taken.
✗ for (uint16_t i = 0; i < overlap; ++i)
86 {
87
0/2
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 10 not taken.
✗ if (curr[i] != prev[i + depart_count])
88 {
89 ✗ return false;
90 }
91 }
92 ✗ return true;
93 }
94
95 //! Classify how a contiguous container changed between consecutive collections.
96 //!
97 //! Returns FULL when \p prev is empty (no baseline). Heartbeat forcing is handled
98 //! by the checkpointer, not this helper.
99 ✗ inline ContigDeltaClassification classifyContigChange(const std::vector<std::vector<char>>& prev,
100 const std::vector<std::vector<char>>& curr)
101 {
102 ✗ ContigDeltaClassification result;
103
104
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
✗ if (prev.empty())
105 {
106 ✗ result.kind = ContigDeltaKind::FULL;
107 ✗ return result;
108 }
109
110
0/1
✗ Branch 6 → 7 not taken.
✗ const auto curr_size = countContigElements(curr);
111
0/1
✗ Branch 7 → 8 not taken.
✗ const auto prev_size = countContigElements(prev);
112
113
0/2
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 58 not taken.
✗ if (curr_size == prev_size)
114 {
115 ✗ ValidValue<uint16_t> changed_idx;
116 ✗ uint16_t num_changes = 0;
117 ✗ std::vector<uint16_t> changed_idxs;
118
0/2
✗ Branch 20 → 11 not taken.
✗ Branch 20 → 21 not taken.
✗ for (uint16_t i = 0; i < curr_size; ++i)
119 {
120
0/3
✗ Branch 13 → 14 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 19 not taken.
✗ if (curr[i] != prev[i])
121 {
122
0/1
✗ Branch 15 → 16 not taken.
✗ changed_idxs.push_back(i);
123 ✗ changed_idx = i;
124 ✗ ++num_changes;
125
0/2
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 19 not taken.
✗ if (num_changes > 1)
126 {
127 ✗ changed_idx.clearValid();
128 }
129 }
130 }
131
132
0/2
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 23 not taken.
✗ if (num_changes == 0)
133 {
134 ✗ result.kind = ContigDeltaKind::CARRY;
135 ✗ return result;
136 }
137
138
0/2
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 30 not taken.
✗ if (num_changes == 1)
139 {
140 ✗ result.kind = ContigDeltaKind::SWAP;
141
0/1
✗ Branch 24 → 25 not taken.
✗ result.swap_index = changed_idx.getValue();
142
0/2
✗ Branch 26 → 27 not taken.
✗ Branch 28 → 29 not taken.
✗ result.payload = curr[changed_idx.getValue()];
143 ✗ return result;
144 }
145
146
0/2
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 43 not taken.
✗ if (curr_size > 0)
147 {
148 ✗ bool bookends = true;
149
0/2
✗ Branch 38 → 32 not taken.
✗ Branch 38 → 39 not taken.
✗ for (size_t i = 0; i + 1 < curr_size; ++i)
150 {
151
0/3
✗ Branch 34 → 35 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 37 not taken.
✗ if (curr[i] != prev[i + 1])
152 {
153 ✗ bookends = false;
154 ✗ break;
155 }
156 }
157
158
0/2
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 43 not taken.
✗ if (bookends)
159 {
160 ✗ result.kind = ContigDeltaKind::BOOKENDS;
161
0/1
✗ Branch 41 → 42 not taken.
✗ result.payload = curr[curr_size - 1];
162 ✗ return result;
163 }
164 }
165
166 ✗ result.kind = ContigDeltaKind::MULTI_SWAP;
167 ✗ result.swap_indices = std::move(changed_idxs);
168
0/1
✗ Branch 46 → 47 not taken.
✗ result.swap_payloads.reserve(result.swap_indices.size());
169
0/2
✗ Branch 54 → 49 not taken.
✗ Branch 54 → 55 not taken.
✗ for (const auto idx : result.swap_indices)
170 {
171
0/1
✗ Branch 51 → 52 not taken.
✗ result.swap_payloads.push_back(curr[idx]);
172 }
173 ✗ return result;
174 ✗ }
175
176
0/2
✗ Branch 58 → 59 not taken.
✗ Branch 58 → 71 not taken.
✗ if (curr_size == prev_size + 1)
177 {
178 ✗ bool arrive = true;
179
0/2
✗ Branch 66 → 60 not taken.
✗ Branch 66 → 67 not taken.
✗ for (uint16_t i = 0; i < prev_size; ++i)
180 {
181
0/3
✗ Branch 62 → 63 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 65 not taken.
✗ if (curr[i] != prev[i])
182 {
183 ✗ arrive = false;
184 ✗ break;
185 }
186 }
187
188
0/2
✗ Branch 67 → 68 not taken.
✗ Branch 67 → 82 not taken.
✗ if (arrive)
189 {
190 ✗ result.kind = ContigDeltaKind::ARRIVE;
191
0/1
✗ Branch 69 → 70 not taken.
✗ result.payload = curr[curr_size - 1];
192 ✗ return result;
193 }
194
0/2
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 82 not taken.
✗ } else if (prev_size == curr_size + 1)
195 {
196 ✗ bool depart = true;
197
0/2
✗ Branch 79 → 73 not taken.
✗ Branch 79 → 80 not taken.
✗ for (uint16_t i = 0; i < curr_size; ++i)
198 {
199
0/3
✗ Branch 75 → 76 not taken.
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 78 not taken.
✗ if (curr[i] != prev[i + 1])
200 {
201 ✗ depart = false;
202 ✗ break;
203 }
204 }
205
206
0/2
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ if (depart)
207 {
208 ✗ result.kind = ContigDeltaKind::DEPART;
209 ✗ return result;
210 }
211 }
212
213
0/2
✗ Branch 103 → 83 not taken.
✗ Branch 103 → 104 not taken.
✗ for (uint16_t depart_count = 0; depart_count <= prev_size; ++depart_count)
214 {
215 ✗ const int64_t arrive_count64 = static_cast<int64_t>(curr_size) - static_cast<int64_t>(prev_size) + depart_count;
216
0/4
✗ Branch 83 → 84 not taken.
✗ Branch 83 → 85 not taken.
✗ Branch 84 → 85 not taken.
✗ Branch 84 → 86 not taken.
✗ if (arrive_count64 < 0 || arrive_count64 > UINT8_MAX)
217 {
218 ✗ continue;
219 }
220
221 ✗ const auto arrive_count = static_cast<uint8_t>(arrive_count64);
222
0/2
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
✗ if (depart_count + arrive_count <= 1)
223 {
224 ✗ continue;
225 }
226
0/6
✗ Branch 88 → 89 not taken.
✗ Branch 88 → 92 not taken.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 92 not taken.
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
✗ if (depart_count == 1 && arrive_count == 1 && curr_size == prev_size)
227 {
228 ✗ continue;
229 }
230
231
0/3
✗ Branch 92 → 93 not taken.
✗ Branch 93 → 94 not taken.
✗ Branch 93 → 95 not taken.
✗ if (!contigPrefixMatchesAfterDepart(prev, curr, prev_size, curr_size, depart_count))
232 {
233 ✗ continue;
234 }
235
236 ✗ const auto overlap = static_cast<uint16_t>(prev_size - depart_count);
237 ✗ result.kind = ContigDeltaKind::MIMO;
238 ✗ result.depart_count = static_cast<uint8_t>(depart_count);
239 ✗ result.arrive_count = arrive_count;
240
0/1
✗ Branch 95 → 96 not taken.
✗ result.arrive_payloads.reserve(arrive_count);
241
0/2
✗ Branch 100 → 97 not taken.
✗ Branch 100 → 101 not taken.
✗ for (uint8_t j = 0; j < arrive_count; ++j)
242 {
243
0/1
✗ Branch 98 → 99 not taken.
✗ result.arrive_payloads.push_back(curr[overlap + j]);
244 }
245 ✗ return result;
246 }
247
248 ✗ result.kind = ContigDeltaKind::FULL;
249 ✗ return result;
250 ✗ }
251
252 inline std::ostream& operator<<(std::ostream& os, ContigDeltaKind kind)
253 {
254 switch (kind)
255 {
256 case ContigDeltaKind::CARRY:
257 return os << "CARRY";
258 case ContigDeltaKind::SWAP:
259 return os << "SWAP";
260 case ContigDeltaKind::MULTI_SWAP:
261 return os << "MULTI_SWAP";
262 case ContigDeltaKind::BOOKENDS:
263 return os << "BOOKENDS";
264 case ContigDeltaKind::ARRIVE:
265 return os << "ARRIVE";
266 case ContigDeltaKind::DEPART:
267 return os << "DEPART";
268 case ContigDeltaKind::MIMO:
269 return os << "MIMO";
270 case ContigDeltaKind::FULL:
271 return os << "FULL";
272 }
273 throw DBException("Invalid ContigDeltaKind");
274 }
275
276 //! Sparse-container delta kinds.
277 enum class SparseDeltaKind { CARRY, SWAP, MULTI_SWAP, REMOVE, ADD, MULTI_REMOVE, FULL };
278
279 //! Result of classifying a sparse container transition.
280 struct SparseDeltaClassification
281 {
282 SparseDeltaKind kind = SparseDeltaKind::FULL;
283 simdb::ValidValue<uint16_t> bin_index;
284 std::vector<char> payload;
285 std::vector<uint16_t> bin_indices;
286 std::vector<std::vector<char>> payloads;
287 };
288
289 29961 inline uint16_t countSparseElements(const std::map<uint16_t, std::vector<char>>& sparse_bins)
290 {
291 29961 uint64_t count = 0;
292
2/2
✓ Branch 12 → 4 taken 135870 times.
✓ Branch 12 → 13 taken 29961 times.
165831 for (const auto& [_, bytes] : sparse_bins)
293 {
294
1/2
✓ Branch 8 → 9 taken 135870 times.
✗ Branch 8 → 10 not taken.
135870 if (!bytes.empty())
295 {
296 135870 ++count;
297 }
298 }
299
1/9
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 24 taken 29961 times.
✗ Branch 15 → 16 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 22 → 23 not taken.
29961 simdb_assert(count <= UINT16_MAX);
300 29961 return static_cast<uint16_t>(count);
301 }
302
303 //! Classify how a sparse container changed between consecutive collections.
304 //!
305 //! Returns FULL when \p prev is empty (no baseline). Heartbeat forcing is handled
306 //! by the checkpointer, not this helper.
307 ✗ inline SparseDeltaClassification classifySparseChange(const std::map<uint16_t, std::vector<char>>& prev,
308 const std::map<uint16_t, std::vector<char>>& curr)
309 {
310 ✗ SparseDeltaClassification result;
311
312
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
✗ if (prev.empty())
313 {
314 ✗ result.kind = SparseDeltaKind::FULL;
315 ✗ return result;
316 }
317
318
0/1
✗ Branch 6 → 7 not taken.
✗ const auto curr_size = countSparseElements(curr);
319
0/1
✗ Branch 7 → 8 not taken.
✗ const auto prev_size = countSparseElements(prev);
320
321
0/3
✗ Branch 8 → 9 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 11 not taken.
✗ if (curr == prev)
322 {
323 ✗ result.kind = SparseDeltaKind::CARRY;
324 ✗ return result;
325 }
326
327
0/2
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 51 not taken.
✗ if (curr_size + 1 == prev_size)
328 {
329 ✗ ValidValue<uint16_t> removed_idx;
330 ✗ uint16_t removed_count = 0;
331 ✗ bool other_change = false;
332
0/2
✗ Branch 28 → 14 not taken.
✗ Branch 28 → 29 not taken.
✗ for (const auto& [prev_idx, prev_bytes] : prev)
333 {
334
0/3
✗ Branch 17 → 18 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ if (auto it = curr.find(prev_idx); it == curr.end())
335 {
336 ✗ ++removed_count;
337 ✗ removed_idx = prev_idx;
338
0/3
✗ Branch 23 → 24 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ } else if (prev_bytes != it->second)
339 {
340 ✗ other_change = true;
341 ✗ break;
342 }
343 }
344
345
0/4
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 45 not taken.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 45 not taken.
✗ if (!other_change && removed_count == 1)
346 {
347
0/2
✗ Branch 43 → 33 not taken.
✗ Branch 43 → 44 not taken.
✗ for (const auto& [curr_idx, _] : curr)
348 {
349
0/3
✗ Branch 37 → 38 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 41 not taken.
✗ if (prev.find(curr_idx) == prev.end())
350 {
351 ✗ other_change = true;
352 ✗ break;
353 }
354 }
355 }
356
357
0/4
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 50 not taken.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 50 not taken.
✗ if (!other_change && removed_count == 1)
358 {
359 ✗ result.kind = SparseDeltaKind::REMOVE;
360
0/1
✗ Branch 47 → 48 not taken.
✗ result.bin_index = removed_idx.getValue();
361 ✗ return result;
362 }
363 }
364
365
0/2
✗ Branch 51 → 52 not taken.
✗ Branch 51 → 92 not taken.
✗ if (curr_size == prev_size + 1)
366 {
367 ✗ ValidValue<uint16_t> added_idx;
368 ✗ uint16_t added_count = 0;
369 ✗ bool other_change = false;
370
0/2
✗ Branch 69 → 54 not taken.
✗ Branch 69 → 70 not taken.
✗ for (const auto& [curr_idx, curr_bytes] : curr)
371 {
372
0/3
✗ Branch 57 → 58 not taken.
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 63 not taken.
✗ if (auto it = prev.find(curr_idx); it == prev.end())
373 {
374 ✗ ++added_count;
375 ✗ added_idx = curr_idx;
376
0/1
✗ Branch 62 → 67 not taken.
✗ result.payload = curr_bytes;
377
0/3
✗ Branch 64 → 65 not taken.
✗ Branch 65 → 66 not taken.
✗ Branch 65 → 67 not taken.
✗ } else if (it->second != curr_bytes)
378 {
379 ✗ other_change = true;
380 ✗ break;
381 }
382 }
383
384
0/4
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 86 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 86 not taken.
✗ if (!other_change && added_count == 1)
385 {
386
0/2
✗ Branch 84 → 74 not taken.
✗ Branch 84 → 85 not taken.
✗ for (const auto& [prev_idx, _] : prev)
387 {
388
0/3
✗ Branch 78 → 79 not taken.
✗ Branch 80 → 81 not taken.
✗ Branch 80 → 82 not taken.
✗ if (curr.find(prev_idx) == curr.end())
389 {
390 ✗ other_change = true;
391 ✗ break;
392 }
393 }
394 }
395
396
0/4
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 91 not taken.
✗ Branch 87 → 88 not taken.
✗ Branch 87 → 91 not taken.
✗ if (!other_change && added_count == 1)
397 {
398 ✗ result.kind = SparseDeltaKind::ADD;
399
0/1
✗ Branch 88 → 89 not taken.
✗ result.bin_index = added_idx.getValue();
400 ✗ return result;
401 }
402 }
403
404
0/2
✗ Branch 92 → 93 not taken.
✗ Branch 92 → 150 not taken.
✗ if (curr_size + 1 <= prev_size)
405 {
406 ✗ std::vector<uint16_t> removed_idxs;
407 ✗ bool other_change = false;
408
0/2
✗ Branch 110 → 96 not taken.
✗ Branch 110 → 111 not taken.
✗ for (const auto& [prev_idx, prev_bytes] : prev)
409 {
410
0/3
✗ Branch 99 → 100 not taken.
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 104 not taken.
✗ if (auto it = curr.find(prev_idx); it == curr.end())
411 {
412
0/1
✗ Branch 103 → 108 not taken.
✗ removed_idxs.push_back(prev_idx);
413
0/3
✗ Branch 105 → 106 not taken.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 108 not taken.
✗ } else if (prev_bytes != it->second)
414 {
415 ✗ other_change = true;
416 ✗ break;
417 }
418 }
419
420
0/6
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 117 not taken.
✗ Branch 113 → 114 not taken.
✗ Branch 113 → 117 not taken.
✗ Branch 118 → 119 not taken.
✗ Branch 118 → 133 not taken.
✗ if (!other_change && removed_idxs.size() >= 2 &&
421
0/2
✗ Branch 115 → 116 not taken.
✗ Branch 115 → 117 not taken.
✗ removed_idxs.size() == static_cast<size_t>(prev_size - curr_size))
422 {
423
0/2
✗ Branch 131 → 121 not taken.
✗ Branch 131 → 132 not taken.
✗ for (const auto& [curr_idx, _] : curr)
424 {
425
0/3
✗ Branch 125 → 126 not taken.
✗ Branch 127 → 128 not taken.
✗ Branch 127 → 129 not taken.
✗ if (prev.find(curr_idx) == prev.end())
426 {
427 ✗ other_change = true;
428 ✗ break;
429 }
430 }
431 }
432
433
0/6
✗ Branch 133 → 134 not taken.
✗ Branch 133 → 139 not taken.
✗ Branch 135 → 136 not taken.
✗ Branch 135 → 139 not taken.
✗ Branch 140 → 141 not taken.
✗ Branch 140 → 144 not taken.
✗ if (!other_change && removed_idxs.size() >= 2 &&
434
0/2
✗ Branch 137 → 138 not taken.
✗ Branch 137 → 139 not taken.
✗ removed_idxs.size() == static_cast<size_t>(prev_size - curr_size))
435 {
436 ✗ result.kind = SparseDeltaKind::MULTI_REMOVE;
437 ✗ result.bin_indices = std::move(removed_idxs);
438 ✗ return result;
439 }
440 ✗ }
441
442
0/2
✗ Branch 150 → 151 not taken.
✗ Branch 150 → 152 not taken.
✗ if (curr_size != prev_size)
443 {
444 ✗ result.kind = SparseDeltaKind::FULL;
445 ✗ return result;
446 }
447
448 ✗ std::vector<uint16_t> changed_idxs;
449 ✗ std::vector<uint16_t> removed_idxs;
450 ✗ std::vector<uint16_t> added_idxs;
451
452
0/2
✗ Branch 171 → 157 not taken.
✗ Branch 171 → 172 not taken.
✗ for (const auto& [prev_idx, prev_bytes] : prev)
453 {
454
0/3
✗ Branch 160 → 161 not taken.
✗ Branch 163 → 164 not taken.
✗ Branch 163 → 168 not taken.
✗ if (auto it = curr.find(prev_idx); it != curr.end())
455 {
456
0/3
✗ Branch 165 → 166 not taken.
✗ Branch 166 → 167 not taken.
✗ Branch 166 → 169 not taken.
✗ if (prev_bytes != it->second)
457 {
458
0/1
✗ Branch 167 → 169 not taken.
✗ changed_idxs.push_back(prev_idx);
459 }
460 } else
461 {
462
0/1
✗ Branch 168 → 169 not taken.
✗ removed_idxs.push_back(prev_idx);
463 }
464 }
465
466
0/2
✗ Branch 184 → 174 not taken.
✗ Branch 184 → 185 not taken.
✗ for (const auto& [curr_idx, _] : curr)
467 {
468
0/3
✗ Branch 178 → 179 not taken.
✗ Branch 180 → 181 not taken.
✗ Branch 180 → 182 not taken.
✗ if (prev.find(curr_idx) == prev.end())
469 {
470
0/1
✗ Branch 181 → 182 not taken.
✗ added_idxs.push_back(curr_idx);
471 }
472 }
473
474
0/6
✗ Branch 186 → 187 not taken.
✗ Branch 186 → 189 not taken.
✗ Branch 188 → 189 not taken.
✗ Branch 188 → 190 not taken.
✗ Branch 191 → 192 not taken.
✗ Branch 191 → 232 not taken.
✗ if (!added_idxs.empty() || !removed_idxs.empty())
475 {
476
0/8
✗ Branch 193 → 194 not taken.
✗ Branch 193 → 199 not taken.
✗ Branch 195 → 196 not taken.
✗ Branch 195 → 199 not taken.
✗ Branch 197 → 198 not taken.
✗ Branch 197 → 199 not taken.
✗ Branch 200 → 201 not taken.
✗ Branch 200 → 207 not taken.
✗ if (added_idxs.size() == 1 && removed_idxs.empty() && changed_idxs.empty())
477 {
478 ✗ result.kind = SparseDeltaKind::ADD;
479 ✗ result.bin_index = added_idxs[0];
480
0/2
✗ Branch 204 → 205 not taken.
✗ Branch 205 → 206 not taken.
✗ result.payload = curr.at(added_idxs[0]);
481 ✗ return result;
482 }
483
484
0/8
✗ Branch 208 → 209 not taken.
✗ Branch 208 → 214 not taken.
✗ Branch 210 → 211 not taken.
✗ Branch 210 → 214 not taken.
✗ Branch 212 → 213 not taken.
✗ Branch 212 → 214 not taken.
✗ Branch 215 → 216 not taken.
✗ Branch 215 → 219 not taken.
✗ if (removed_idxs.size() == 1 && added_idxs.empty() && changed_idxs.empty())
485 {
486 ✗ result.kind = SparseDeltaKind::REMOVE;
487 ✗ result.bin_index = removed_idxs[0];
488 ✗ return result;
489 }
490
491
0/8
✗ Branch 220 → 221 not taken.
✗ Branch 220 → 226 not taken.
✗ Branch 222 → 223 not taken.
✗ Branch 222 → 226 not taken.
✗ Branch 224 → 225 not taken.
✗ Branch 224 → 226 not taken.
✗ Branch 227 → 228 not taken.
✗ Branch 227 → 231 not taken.
✗ if (removed_idxs.size() >= 2 && added_idxs.empty() && changed_idxs.empty())
492 {
493 ✗ result.kind = SparseDeltaKind::MULTI_REMOVE;
494 ✗ result.bin_indices = std::move(removed_idxs);
495 ✗ return result;
496 }
497
498 ✗ result.kind = SparseDeltaKind::FULL;
499 ✗ return result;
500 }
501
502
0/2
✗ Branch 233 → 234 not taken.
✗ Branch 233 → 240 not taken.
✗ if (changed_idxs.size() == 1)
503 {
504 ✗ result.kind = SparseDeltaKind::SWAP;
505 ✗ result.bin_index = changed_idxs[0];
506
0/2
✗ Branch 237 → 238 not taken.
✗ Branch 238 → 239 not taken.
✗ result.payload = curr.at(changed_idxs[0]);
507 ✗ return result;
508 }
509
510
0/2
✗ Branch 241 → 242 not taken.
✗ Branch 241 → 255 not taken.
✗ if (changed_idxs.size() >= 2)
511 {
512 ✗ result.kind = SparseDeltaKind::MULTI_SWAP;
513 ✗ result.bin_indices = std::move(changed_idxs);
514
0/1
✗ Branch 245 → 246 not taken.
✗ result.payloads.reserve(result.bin_indices.size());
515
0/2
✗ Branch 253 → 248 not taken.
✗ Branch 253 → 254 not taken.
✗ for (const auto idx : result.bin_indices)
516 {
517
0/2
✗ Branch 249 → 250 not taken.
✗ Branch 250 → 251 not taken.
✗ result.payloads.push_back(curr.at(idx));
518 }
519 ✗ return result;
520 }
521
522 ✗ result.kind = SparseDeltaKind::FULL;
523 ✗ return result;
524 ✗ }
525
526 inline std::ostream& operator<<(std::ostream& os, SparseDeltaKind kind)
527 {
528 switch (kind)
529 {
530 case SparseDeltaKind::CARRY:
531 return os << "CARRY";
532 case SparseDeltaKind::SWAP:
533 return os << "SWAP";
534 case SparseDeltaKind::MULTI_SWAP:
535 return os << "MULTI_SWAP";
536 case SparseDeltaKind::REMOVE:
537 return os << "REMOVE";
538 case SparseDeltaKind::ADD:
539 return os << "ADD";
540 case SparseDeltaKind::MULTI_REMOVE:
541 return os << "MULTI_REMOVE";
542 case SparseDeltaKind::FULL:
543 return os << "FULL";
544 }
545 throw DBException("Invalid SparseDeltaKind");
546 }
547
548 } // namespace simdb::argos
549