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 | 172368 | for (const auto& bytes : contig_bins) | |
| 63 | { | ||
| 64 | 142389 | if (!bytes.empty()) | |
| 65 | { | ||
| 66 | 142389 | ++count; | |
| 67 | } else | ||
| 68 | { | ||
| 69 | ✗ | break; | |
| 70 | } | ||
| 71 | } | ||
| 72 | 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 | ✗ | if (overlap + static_cast<uint16_t>(curr_size - overlap) != curr_size) | |
| 82 | { | ||
| 83 | ✗ | return false; | |
| 84 | } | ||
| 85 | ✗ | for (uint16_t i = 0; i < overlap; ++i) | |
| 86 | { | ||
| 87 | ✗ | 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 | ✗ | if (prev.empty()) | |
| 105 | { | ||
| 106 | ✗ | result.kind = ContigDeltaKind::FULL; | |
| 107 | ✗ | return result; | |
| 108 | } | ||
| 109 | |||
| 110 | ✗ | const auto curr_size = countContigElements(curr); | |
| 111 | ✗ | const auto prev_size = countContigElements(prev); | |
| 112 | |||
| 113 | ✗ | 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 | ✗ | for (uint16_t i = 0; i < curr_size; ++i) | |
| 119 | { | ||
| 120 | ✗ | if (curr[i] != prev[i]) | |
| 121 | { | ||
| 122 | ✗ | changed_idxs.push_back(i); | |
| 123 | ✗ | changed_idx = i; | |
| 124 | ✗ | ++num_changes; | |
| 125 | ✗ | if (num_changes > 1) | |
| 126 | { | ||
| 127 | ✗ | changed_idx.clearValid(); | |
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | ✗ | if (num_changes == 0) | |
| 133 | { | ||
| 134 | ✗ | result.kind = ContigDeltaKind::CARRY; | |
| 135 | ✗ | return result; | |
| 136 | } | ||
| 137 | |||
| 138 | ✗ | if (num_changes == 1) | |
| 139 | { | ||
| 140 | ✗ | result.kind = ContigDeltaKind::SWAP; | |
| 141 | ✗ | result.swap_index = changed_idx.getValue(); | |
| 142 | ✗ | result.payload = curr[changed_idx.getValue()]; | |
| 143 | ✗ | return result; | |
| 144 | } | ||
| 145 | |||
| 146 | ✗ | if (curr_size > 0) | |
| 147 | { | ||
| 148 | ✗ | bool bookends = true; | |
| 149 | ✗ | for (size_t i = 0; i + 1 < curr_size; ++i) | |
| 150 | { | ||
| 151 | ✗ | if (curr[i] != prev[i + 1]) | |
| 152 | { | ||
| 153 | ✗ | bookends = false; | |
| 154 | ✗ | break; | |
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | ✗ | if (bookends) | |
| 159 | { | ||
| 160 | ✗ | result.kind = ContigDeltaKind::BOOKENDS; | |
| 161 | ✗ | 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 | ✗ | result.swap_payloads.reserve(result.swap_indices.size()); | |
| 169 | ✗ | for (const auto idx : result.swap_indices) | |
| 170 | { | ||
| 171 | ✗ | result.swap_payloads.push_back(curr[idx]); | |
| 172 | } | ||
| 173 | ✗ | return result; | |
| 174 | ✗ | } | |
| 175 | |||
| 176 | ✗ | if (curr_size == prev_size + 1) | |
| 177 | { | ||
| 178 | ✗ | bool arrive = true; | |
| 179 | ✗ | for (uint16_t i = 0; i < prev_size; ++i) | |
| 180 | { | ||
| 181 | ✗ | if (curr[i] != prev[i]) | |
| 182 | { | ||
| 183 | ✗ | arrive = false; | |
| 184 | ✗ | break; | |
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | ✗ | if (arrive) | |
| 189 | { | ||
| 190 | ✗ | result.kind = ContigDeltaKind::ARRIVE; | |
| 191 | ✗ | result.payload = curr[curr_size - 1]; | |
| 192 | ✗ | return result; | |
| 193 | } | ||
| 194 | ✗ | } else if (prev_size == curr_size + 1) | |
| 195 | { | ||
| 196 | ✗ | bool depart = true; | |
| 197 | ✗ | for (uint16_t i = 0; i < curr_size; ++i) | |
| 198 | { | ||
| 199 | ✗ | if (curr[i] != prev[i + 1]) | |
| 200 | { | ||
| 201 | ✗ | depart = false; | |
| 202 | ✗ | break; | |
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | ✗ | if (depart) | |
| 207 | { | ||
| 208 | ✗ | result.kind = ContigDeltaKind::DEPART; | |
| 209 | ✗ | return result; | |
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | ✗ | 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 | ✗ | 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 | ✗ | if (depart_count + arrive_count <= 1) | |
| 223 | { | ||
| 224 | ✗ | continue; | |
| 225 | } | ||
| 226 | ✗ | if (depart_count == 1 && arrive_count == 1 && curr_size == prev_size) | |
| 227 | { | ||
| 228 | ✗ | continue; | |
| 229 | } | ||
| 230 | |||
| 231 | ✗ | 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 | ✗ | result.arrive_payloads.reserve(arrive_count); | |
| 241 | ✗ | for (uint8_t j = 0; j < arrive_count; ++j) | |
| 242 | { | ||
| 243 | ✗ | 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 | 165831 | for (const auto& [_, bytes] : sparse_bins) | |
| 293 | { | ||
| 294 | 135870 | if (!bytes.empty()) | |
| 295 | { | ||
| 296 | 135870 | ++count; | |
| 297 | } | ||
| 298 | } | ||
| 299 | 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 | ✗ | if (prev.empty()) | |
| 313 | { | ||
| 314 | ✗ | result.kind = SparseDeltaKind::FULL; | |
| 315 | ✗ | return result; | |
| 316 | } | ||
| 317 | |||
| 318 | ✗ | const auto curr_size = countSparseElements(curr); | |
| 319 | ✗ | const auto prev_size = countSparseElements(prev); | |
| 320 | |||
| 321 | ✗ | if (curr == prev) | |
| 322 | { | ||
| 323 | ✗ | result.kind = SparseDeltaKind::CARRY; | |
| 324 | ✗ | return result; | |
| 325 | } | ||
| 326 | |||
| 327 | ✗ | 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 | ✗ | for (const auto& [prev_idx, prev_bytes] : prev) | |
| 333 | { | ||
| 334 | ✗ | if (auto it = curr.find(prev_idx); it == curr.end()) | |
| 335 | { | ||
| 336 | ✗ | ++removed_count; | |
| 337 | ✗ | removed_idx = prev_idx; | |
| 338 | ✗ | } else if (prev_bytes != it->second) | |
| 339 | { | ||
| 340 | ✗ | other_change = true; | |
| 341 | ✗ | break; | |
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | ✗ | if (!other_change && removed_count == 1) | |
| 346 | { | ||
| 347 | ✗ | for (const auto& [curr_idx, _] : curr) | |
| 348 | { | ||
| 349 | ✗ | if (prev.find(curr_idx) == prev.end()) | |
| 350 | { | ||
| 351 | ✗ | other_change = true; | |
| 352 | ✗ | break; | |
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | ✗ | if (!other_change && removed_count == 1) | |
| 358 | { | ||
| 359 | ✗ | result.kind = SparseDeltaKind::REMOVE; | |
| 360 | ✗ | result.bin_index = removed_idx.getValue(); | |
| 361 | ✗ | return result; | |
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 365 | ✗ | 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 | ✗ | for (const auto& [curr_idx, curr_bytes] : curr) | |
| 371 | { | ||
| 372 | ✗ | if (auto it = prev.find(curr_idx); it == prev.end()) | |
| 373 | { | ||
| 374 | ✗ | ++added_count; | |
| 375 | ✗ | added_idx = curr_idx; | |
| 376 | ✗ | result.payload = curr_bytes; | |
| 377 | ✗ | } else if (it->second != curr_bytes) | |
| 378 | { | ||
| 379 | ✗ | other_change = true; | |
| 380 | ✗ | break; | |
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | ✗ | if (!other_change && added_count == 1) | |
| 385 | { | ||
| 386 | ✗ | for (const auto& [prev_idx, _] : prev) | |
| 387 | { | ||
| 388 | ✗ | if (curr.find(prev_idx) == curr.end()) | |
| 389 | { | ||
| 390 | ✗ | other_change = true; | |
| 391 | ✗ | break; | |
| 392 | } | ||
| 393 | } | ||
| 394 | } | ||
| 395 | |||
| 396 | ✗ | if (!other_change && added_count == 1) | |
| 397 | { | ||
| 398 | ✗ | result.kind = SparseDeltaKind::ADD; | |
| 399 | ✗ | result.bin_index = added_idx.getValue(); | |
| 400 | ✗ | return result; | |
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | ✗ | if (curr_size + 1 <= prev_size) | |
| 405 | { | ||
| 406 | ✗ | std::vector<uint16_t> removed_idxs; | |
| 407 | ✗ | bool other_change = false; | |
| 408 | ✗ | for (const auto& [prev_idx, prev_bytes] : prev) | |
| 409 | { | ||
| 410 | ✗ | if (auto it = curr.find(prev_idx); it == curr.end()) | |
| 411 | { | ||
| 412 | ✗ | removed_idxs.push_back(prev_idx); | |
| 413 | ✗ | } else if (prev_bytes != it->second) | |
| 414 | { | ||
| 415 | ✗ | other_change = true; | |
| 416 | ✗ | break; | |
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | ✗ | if (!other_change && removed_idxs.size() >= 2 && | |
| 421 | ✗ | removed_idxs.size() == static_cast<size_t>(prev_size - curr_size)) | |
| 422 | { | ||
| 423 | ✗ | for (const auto& [curr_idx, _] : curr) | |
| 424 | { | ||
| 425 | ✗ | if (prev.find(curr_idx) == prev.end()) | |
| 426 | { | ||
| 427 | ✗ | other_change = true; | |
| 428 | ✗ | break; | |
| 429 | } | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | ✗ | if (!other_change && removed_idxs.size() >= 2 && | |
| 434 | ✗ | 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 | ✗ | 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 | ✗ | for (const auto& [prev_idx, prev_bytes] : prev) | |
| 453 | { | ||
| 454 | ✗ | if (auto it = curr.find(prev_idx); it != curr.end()) | |
| 455 | { | ||
| 456 | ✗ | if (prev_bytes != it->second) | |
| 457 | { | ||
| 458 | ✗ | changed_idxs.push_back(prev_idx); | |
| 459 | } | ||
| 460 | } else | ||
| 461 | { | ||
| 462 | ✗ | removed_idxs.push_back(prev_idx); | |
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | ✗ | for (const auto& [curr_idx, _] : curr) | |
| 467 | { | ||
| 468 | ✗ | if (prev.find(curr_idx) == prev.end()) | |
| 469 | { | ||
| 470 | ✗ | added_idxs.push_back(curr_idx); | |
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | ✗ | if (!added_idxs.empty() || !removed_idxs.empty()) | |
| 475 | { | ||
| 476 | ✗ | 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 | ✗ | result.payload = curr.at(added_idxs[0]); | |
| 481 | ✗ | return result; | |
| 482 | } | ||
| 483 | |||
| 484 | ✗ | 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 | ✗ | 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 | ✗ | if (changed_idxs.size() == 1) | |
| 503 | { | ||
| 504 | ✗ | result.kind = SparseDeltaKind::SWAP; | |
| 505 | ✗ | result.bin_index = changed_idxs[0]; | |
| 506 | ✗ | result.payload = curr.at(changed_idxs[0]); | |
| 507 | ✗ | return result; | |
| 508 | } | ||
| 509 | |||
| 510 | ✗ | if (changed_idxs.size() >= 2) | |
| 511 | { | ||
| 512 | ✗ | result.kind = SparseDeltaKind::MULTI_SWAP; | |
| 513 | ✗ | result.bin_indices = std::move(changed_idxs); | |
| 514 | ✗ | result.payloads.reserve(result.bin_indices.size()); | |
| 515 | ✗ | for (const auto idx : result.bin_indices) | |
| 516 | { | ||
| 517 | ✗ | 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 |