1use std::ops::BitOrAssign;
24
25use bytes::Buf;
26use roaring::bitmap::Iter;
27use roaring::treemap::BitmapIter;
28use roaring::{RoaringBitmap, RoaringTreemap};
29
30use crate::error::invalid_data;
31use crate::{Error, ErrorKind, Result};
32
33const DV_MAGIC: [u8; 4] = [0xD1, 0xD3, 0x39, 0x64];
36const DV_LENGTH_PREFIX_BYTES: usize = 4;
37const DV_MAGIC_BYTES: usize = 4;
38const DV_CRC_BYTES: usize = 4;
39const DV_MIN_BLOB_BYTES: usize = DV_LENGTH_PREFIX_BYTES + DV_MAGIC_BYTES + DV_CRC_BYTES;
40
41#[derive(Debug, Default)]
44pub struct DeleteVector {
45 inner: RoaringTreemap,
46}
47
48impl DeleteVector {
49 pub fn new(roaring_treemap: RoaringTreemap) -> DeleteVector {
51 DeleteVector {
52 inner: roaring_treemap,
53 }
54 }
55
56 pub fn iter(&self) -> DeleteVectorIterator<'_> {
58 let outer = self.inner.bitmaps();
59 DeleteVectorIterator { outer, inner: None }
60 }
61
62 pub fn insert(&mut self, pos: u64) -> bool {
64 self.inner.insert(pos)
65 }
66
67 pub fn insert_positions(&mut self, positions: &[u64]) -> Result<usize> {
75 if let Err(err) = self.inner.append(positions.iter().copied()) {
76 return Err(Error::new(
77 ErrorKind::PreconditionFailed,
78 "failed to marks rows as deleted".to_string(),
79 )
80 .with_source(err));
81 }
82 Ok(positions.len())
83 }
84
85 pub fn len(&self) -> u64 {
87 self.inner.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.inner.is_empty()
93 }
94
95 pub(crate) fn ranges(&self) -> DeleteVectorRanges<'_> {
101 DeleteVectorRanges {
102 bitmaps: self.inner.bitmaps(),
103 high_bits: 0,
104 bitmap_iter: None,
105 buffered: None,
106 }
107 }
108
109 pub fn deserialize(blob: &[u8]) -> Result<Self> {
130 if blob.len() < DV_MIN_BLOB_BYTES {
131 return Err(invalid_data!(
132 "deletion-vector-v1 blob is {} bytes, shorter than the {DV_MIN_BLOB_BYTES}-byte minimum",
133 blob.len()
134 ));
135 }
136
137 let body = &blob[DV_LENGTH_PREFIX_BYTES..blob.len() - DV_CRC_BYTES];
139 verify_length_prefix(&blob[..DV_LENGTH_PREFIX_BYTES], body)?;
140
141 verify_crc(body, &blob[blob.len() - DV_CRC_BYTES..])?;
144
145 let (magic, vector) = body.split_at(DV_MAGIC_BYTES);
146 verify_magic(magic)?;
147
148 let inner = decode_roaring_directory(vector)?;
149
150 Ok(DeleteVector { inner })
151 }
152}
153
154fn verify_length_prefix(mut prefix: &[u8], body: &[u8]) -> Result<()> {
155 let declared_len = prefix.try_get_u32().map_err(|e| {
156 Error::new(
157 ErrorKind::Unexpected,
158 "failed to read the deletion-vector-v1 length prefix",
159 )
160 .with_source(e)
161 })? as usize;
162 if declared_len != body.len() {
163 return Err(invalid_data!(
164 "deletion-vector-v1 length prefix is {declared_len}, expected {}",
165 body.len()
166 ));
167 }
168 Ok(())
169}
170
171fn verify_crc(body: &[u8], mut crc_bytes: &[u8]) -> Result<()> {
172 let stored_crc = crc_bytes.try_get_u32().map_err(|e| {
173 Error::new(
174 ErrorKind::Unexpected,
175 "failed to read the deletion-vector-v1 CRC",
176 )
177 .with_source(e)
178 })?;
179 let computed_crc = crc32fast::hash(body);
180 if computed_crc != stored_crc {
181 return Err(invalid_data!(
182 "deletion-vector-v1 CRC mismatch: computed {computed_crc:#010x}, stored {stored_crc:#010x}"
183 ));
184 }
185 Ok(())
186}
187
188fn verify_magic(magic: &[u8]) -> Result<()> {
189 if magic != DV_MAGIC {
190 return Err(invalid_data!(
191 "deletion-vector-v1 magic mismatch: {magic:02x?}, expected {DV_MAGIC:02x?}"
192 ));
193 }
194 Ok(())
195}
196
197fn decode_roaring_directory(mut reader: &[u8]) -> Result<RoaringTreemap> {
204 let bitmap_count = reader.try_get_u64_le().map_err(|e| {
205 invalid_data!("failed to decode deletion-vector-v1 roaring payload").with_source(e)
206 })?;
207 if bitmap_count > u32::MAX as u64 {
210 return Err(invalid_data!(
211 "deletion-vector-v1 roaring bitmap count {bitmap_count} exceeds the {}-key maximum",
212 u32::MAX
213 ));
214 }
215
216 let mut bitmaps = Vec::new();
217 let mut last_key: Option<u32> = None;
218 for _ in 0..bitmap_count {
219 let key = reader.try_get_u32_le().map_err(|e| {
220 invalid_data!("failed to decode deletion-vector-v1 roaring payload").with_source(e)
221 })?;
222 if let Some(last) = last_key
223 && key <= last
224 {
225 return Err(invalid_data!(
226 "deletion-vector-v1 roaring keys must be ordered by unsigned comparison, got key {key} after {last}"
227 ));
228 }
229 last_key = Some(key);
230
231 let bitmap = RoaringBitmap::deserialize_from(&mut reader).map_err(|e| {
232 invalid_data!("failed to decode deletion-vector-v1 roaring payload").with_source(e)
233 })?;
234 bitmaps.push((key, bitmap));
235 }
236
237 Ok(RoaringTreemap::from_bitmaps(bitmaps))
240}
241
242pub struct DeleteVectorIterator<'a> {
244 outer: BitmapIter<'a>,
245 inner: Option<DeleteVectorIteratorInner<'a>>,
246}
247
248struct DeleteVectorIteratorInner<'a> {
249 high_bits: u32,
250 bitmap_iter: Iter<'a>,
251}
252
253impl Iterator for DeleteVectorIterator<'_> {
254 type Item = u64;
255
256 fn next(&mut self) -> Option<Self::Item> {
257 if let Some(inner) = &mut self.inner
258 && let Some(inner_next) = inner.bitmap_iter.next()
259 {
260 return Some(u64::from(inner.high_bits) << 32 | u64::from(inner_next));
261 }
262
263 if let Some((high_bits, next_bitmap)) = self.outer.next() {
264 self.inner = Some(DeleteVectorIteratorInner {
265 high_bits,
266 bitmap_iter: next_bitmap.iter(),
267 })
268 } else {
269 return None;
270 }
271
272 self.next()
273 }
274}
275
276impl DeleteVectorIterator<'_> {
277 pub fn advance_to(&mut self, pos: u64) {
280 let hi = (pos >> 32) as u32;
281 let lo = pos as u32;
282
283 if let Some(inner) = &mut self.inner {
284 if inner.high_bits > hi {
285 return;
286 }
287 if inner.high_bits == hi {
288 inner.bitmap_iter.advance_to(lo);
289 return;
290 }
291 }
292
293 self.inner = None;
295 for (high_bits, bitmap) in self.outer.by_ref() {
296 if high_bits < hi {
297 continue;
298 }
299 let mut bitmap_iter = bitmap.iter();
300 if high_bits == hi {
301 bitmap_iter.advance_to(lo);
302 }
303 self.inner = Some(DeleteVectorIteratorInner {
304 high_bits,
305 bitmap_iter,
306 });
307 return;
308 }
309 }
310}
311
312const RUN_PROBE_LEN: u32 = 4;
321
322enum Buffered {
324 Pos(u32),
326 Range(u32, u32),
328}
329
330pub(crate) struct DeleteVectorRanges<'a> {
333 bitmaps: BitmapIter<'a>,
334 high_bits: u32,
336 bitmap_iter: Option<Iter<'a>>,
337 buffered: Option<Buffered>,
338}
339
340impl DeleteVectorRanges<'_> {
341 fn full_range(&self, start: u32, end_inclusive: u32) -> Option<(u64, u64)> {
343 let base = u64::from(self.high_bits) << 32;
344 let start = base | u64::from(start);
345 let end = (base | u64::from(end_inclusive)).saturating_add(1);
346 (start < end).then_some((start, end))
347 }
348
349 pub(crate) fn skip_to(&mut self, pos: u64) {
353 let hi = (pos >> 32) as u32;
354 let lo = pos as u32;
355
356 if self.bitmap_iter.is_some() && self.high_bits > hi {
357 return;
358 }
359 if let Some(iter) = self.bitmap_iter.as_mut()
360 && self.high_bits == hi
361 {
362 self.buffered = match self.buffered.take() {
363 Some(Buffered::Pos(start)) if start < lo => None,
364 Some(Buffered::Range(_, end)) if end < lo => None,
365 Some(Buffered::Range(start, end)) if start < lo => Some(Buffered::Range(lo, end)),
366 buffered => buffered,
367 };
368 iter.advance_to(lo);
369 return;
370 }
371
372 self.bitmap_iter = None;
374 self.buffered = None;
375 for (high_bits, bitmap) in self.bitmaps.by_ref() {
376 if high_bits < hi {
377 continue;
378 }
379 let mut iter = bitmap.iter();
380 if high_bits == hi {
381 iter.advance_to(lo);
382 }
383 self.high_bits = high_bits;
384 self.bitmap_iter = Some(iter);
385 return;
386 }
387 }
388}
389
390impl Iterator for DeleteVectorRanges<'_> {
391 type Item = (u64, u64);
392
393 fn next(&mut self) -> Option<Self::Item> {
394 loop {
395 let pending = match self.buffered.take() {
396 Some(Buffered::Range(start, end)) => return self.full_range(start, end),
397 Some(Buffered::Pos(start)) => Some(start),
398 None => None,
399 };
400
401 if let Some(iter) = self.bitmap_iter.as_mut()
402 && let Some(start) = pending.or_else(|| iter.next())
403 {
404 let mut end = start;
406 let mut walked = 0;
407 loop {
408 if walked == RUN_PROBE_LEN {
409 match iter.next_range() {
410 Some(run) if Some(*run.start()) == end.checked_add(1) => {
411 end = *run.end();
412 }
413 Some(run) => {
414 self.buffered = Some(Buffered::Range(*run.start(), *run.end()))
415 }
416 None => {}
417 }
418 break;
419 }
420 match iter.next() {
421 Some(next) if Some(next) == end.checked_add(1) => {
422 end = next;
423 walked += 1;
424 }
425 next => {
426 self.buffered = next.map(Buffered::Pos);
427 break;
428 }
429 }
430 }
431 return self.full_range(start, end);
432 }
433
434 let (high_bits, bitmap) = self.bitmaps.next()?;
435 self.high_bits = high_bits;
436 self.bitmap_iter = Some(bitmap.iter());
437 }
438 }
439}
440
441impl BitOrAssign for DeleteVector {
442 fn bitor_assign(&mut self, other: Self) {
443 self.inner.bitor_assign(&other.inner);
444 }
445}
446
447#[cfg(test)]
452pub(crate) fn frame_dv_blob(vector: &[u8]) -> Vec<u8> {
453 let body_len = DV_MAGIC_BYTES + vector.len();
454 let mut blob = Vec::with_capacity(DV_LENGTH_PREFIX_BYTES + body_len + DV_CRC_BYTES);
455 blob.extend_from_slice(&(body_len as u32).to_be_bytes());
456 blob.extend_from_slice(&DV_MAGIC);
457 blob.extend_from_slice(vector);
458 let crc = crc32fast::hash(&blob[DV_LENGTH_PREFIX_BYTES..]);
459 blob.extend_from_slice(&crc.to_be_bytes());
460 blob
461}
462
463#[cfg(test)]
466pub(crate) fn mixed_delete_vectors() -> Vec<DeleteVector> {
467 use rand::rngs::StdRng;
468 use rand::{Rng, SeedableRng};
469
470 let mut rng = StdRng::seed_from_u64(7);
471 let mut vectors = vec![DeleteVector::default()];
472 for case in 0..40u64 {
473 let mut dv = DeleteVector::default();
474 let base = if case % 4 == 0 { (1u64 << 32) - 40 } else { 0 };
475 let mut pos = base + rng.random_range(0..50);
476 for _ in 0..rng.random_range(0..60) {
477 let run = match rng.random_range(0..5) {
478 0 => 1,
479 1 => 2,
480 2 => u64::from(RUN_PROBE_LEN) + 1,
482 3 => rng.random_range(1..=40),
483 _ => rng.random_range(1..=70_000),
484 };
485 dv.inner.insert_range(pos..pos + run);
486 pos += run + rng.random_range(1..=30);
487 }
488 vectors.push(dv);
489 }
490 let mut boundaries = DeleteVector::default();
491 boundaries.inner.insert_range((1 << 16) - 3..(1 << 16) + 3);
492 boundaries
493 .inner
494 .insert_range((1 << 32) - 20..(1 << 32) + 20);
495 boundaries.inner.insert((2 << 32) + 7);
496 vectors.push(boundaries);
497 let mut gaps = DeleteVector::default();
499 gaps.inner.insert_range(2..12);
500 gaps.inner.insert_range((3 << 32) + 5..(3 << 32) + 70_000);
501 gaps.inner.insert((7 << 32) + 1);
502 vectors.push(gaps);
503 vectors
504}
505
506#[cfg(test)]
507mod tests {
508 use rand::rngs::StdRng;
509 use rand::{Rng, SeedableRng};
510
511 use super::*;
512
513 #[test]
514 fn test_ranges_cover_exactly_the_deleted_positions() {
515 for dv in mixed_delete_vectors() {
516 let positions: Vec<u64> = dv.iter().collect();
517 let mut expanded = Vec::new();
518 let mut last_end = None;
519 for (start, end) in dv.ranges() {
520 assert!(start < end, "empty range {start}..{end}");
521 assert!(
522 last_end.is_none_or(|last| last <= start),
523 "ranges out of order"
524 );
525 last_end = Some(end);
526 expanded.extend(start..end);
527 }
528 assert_eq!(expanded, positions);
529 }
530 }
531
532 #[test]
533 fn test_ranges_skip_to_matches_model() {
534 let mut rng = StdRng::seed_from_u64(7);
535
536 for dv in mixed_delete_vectors() {
537 let deleted: Vec<u64> = dv.iter().collect();
538 let max = deleted.last().copied().unwrap_or(0) + 10;
539 let mut next = 0;
542 let mut done = 0u64;
543 let mut ranges = dv.ranges();
544 loop {
545 match rng.random_range(0..6) {
546 0 | 1 => {
547 let target = done.max(deleted.get(next).copied().unwrap_or(max))
548 + rng.random_range(0..200);
549 ranges.skip_to(target);
550 next = deleted.partition_point(|&p| p < target);
551 done = target;
552 continue;
553 }
554 2 => {
556 let back = if rng.random_bool(0.5) {
557 done.saturating_sub(rng.random_range(0..200))
558 } else {
559 rng.random_range(0..=done)
560 };
561 ranges.skip_to(back);
562 continue;
563 }
564 _ => {}
565 }
566 match ranges.next() {
567 Some((start, end)) => {
568 assert!(start < end, "empty range {start}..{end}");
569 let len = (end - start) as usize;
570 assert_eq!(
571 deleted
572 .get(next..next + len)
573 .map(|run| (run[0], run[len - 1])),
574 Some((start, end - 1)),
575 "returned {start}..{end}"
576 );
577 next += len;
578 done = end;
579 }
580 None => {
581 assert_eq!(next, deleted.len(), "missed {:?}", &deleted[next..]);
582 break;
583 }
584 }
585 }
586 }
587 }
588
589 #[test]
590 fn test_advance_to_matches_model() {
591 let mut rng = StdRng::seed_from_u64(11);
592
593 for dv in mixed_delete_vectors() {
594 let deleted: Vec<u64> = dv.iter().collect();
595 let max = deleted.last().copied().unwrap_or(0) + 10;
596 let mut next = 0;
597 let mut done = 0u64;
598 let mut iter = dv.iter();
599 loop {
600 match rng.random_range(0..6) {
601 0 => {
602 let target = done.max(deleted.get(next).copied().unwrap_or(max))
603 + rng.random_range(0..200);
604 iter.advance_to(target);
605 next = deleted.partition_point(|&p| p < target);
606 done = target;
607 continue;
608 }
609 1 => {
610 let back = if rng.random_bool(0.5) {
611 done.saturating_sub(rng.random_range(0..200))
612 } else {
613 rng.random_range(0..=done)
614 };
615 iter.advance_to(back);
616 continue;
617 }
618 _ => {}
619 }
620 let pos = iter.next();
621 assert_eq!(pos, deleted.get(next).copied());
622 match pos {
623 Some(pos) => {
624 next += 1;
625 done = pos + 1;
626 }
627 None => break,
628 }
629 }
630 }
631 }
632
633 #[test]
634 fn test_ranges_skip_to_inside_buffered_run() {
635 let probe = u64::from(RUN_PROBE_LEN);
637 let mut dv = DeleteVector::default();
638 dv.inner.insert_range(0..probe + 1);
639 dv.inner.insert_range(probe + 3..probe + 13);
640 let mut ranges = dv.ranges();
641 assert_eq!(ranges.next(), Some((0, probe + 1)));
642 ranges.skip_to(probe + 8);
643 assert_eq!(ranges.next(), Some((probe + 8, probe + 13)));
644 assert_eq!(ranges.next(), None);
645 }
646
647 #[test]
648 fn test_ranges_join_runs_across_2_16() {
649 for start in [(1 << 16) - 100, (1 << 16) - 2] {
652 let mut dv = DeleteVector::default();
653 dv.inner.insert_range(start..(3 << 16) + 5);
654 assert_eq!(dv.ranges().collect::<Vec<_>>(), vec![(
655 start,
656 (3 << 16) + 5
657 )]);
658 }
659 }
660
661 #[test]
662 fn test_ranges_end_at_u64_max() {
663 let dv = DeleteVector::new(RoaringTreemap::from_iter([5, u64::MAX - 2, u64::MAX - 1]));
664 assert_eq!(dv.ranges().collect::<Vec<_>>(), vec![
665 (5, 6),
666 (u64::MAX - 2, u64::MAX)
667 ]);
668
669 let dv = DeleteVector::new(RoaringTreemap::from_iter([5, u64::MAX - 1, u64::MAX]));
671 assert_eq!(dv.ranges().collect::<Vec<_>>(), vec![
672 (5, 6),
673 (u64::MAX - 1, u64::MAX)
674 ]);
675 let dv = DeleteVector::new(RoaringTreemap::from_iter([u64::MAX]));
676 assert_eq!(dv.ranges().next(), None);
677 }
678
679 #[test]
682 fn test_ranges_skip_to_missing_later_bitmap() {
683 let dv = DeleteVector::new(RoaringTreemap::from_iter([1u64, 5, 9]));
684 let mut ranges = dv.ranges();
685 assert_eq!(ranges.next(), Some((1, 2)));
686 ranges.skip_to((1 << 32) + 5);
687 assert_eq!(ranges.next(), None);
688 }
689
690 #[test]
691 fn test_ranges_skip_to_below_current_bitmap() {
692 let high = 1u64 << 32;
693 let dv = DeleteVector::new(RoaringTreemap::from_iter([high + 1, high + 2, high + 9]));
694 let mut ranges = dv.ranges();
695 assert_eq!(ranges.next(), Some((high + 1, high + 3)));
696 ranges.skip_to(10);
697 assert_eq!(ranges.next(), Some((high + 9, high + 10)));
698 assert_eq!(ranges.next(), None);
699 }
700
701 #[test]
702 fn test_advance_to_missing_later_bitmap() {
703 let dv = DeleteVector::new(RoaringTreemap::from_iter([5u64, 7]));
704 let mut iter = dv.iter();
705 assert_eq!(iter.next(), Some(5));
706 iter.advance_to((1 << 32) + 10);
707 assert_eq!(iter.next(), None);
708 }
709
710 #[test]
711 fn test_advance_to_below_current_bitmap() {
712 let high = 1u64 << 32;
713 let dv = DeleteVector::new(RoaringTreemap::from_iter([high + 10, high + 20]));
714 let mut iter = dv.iter();
715 assert_eq!(iter.next(), Some(high + 10));
716 iter.advance_to(50);
717 assert_eq!(iter.collect::<Vec<_>>(), vec![high + 20]);
718 }
719
720 #[test]
721 fn test_advance_to_before_first_next() {
722 let dv = DeleteVector::new(RoaringTreemap::from_iter([5u64, 200]));
723 let mut iter = dv.iter();
724 iter.advance_to(100);
725 assert_eq!(iter.collect::<Vec<_>>(), vec![200]);
726 }
727
728 #[test]
729 fn test_insertion_and_iteration() {
730 let mut dv = DeleteVector::default();
731 assert!(dv.insert(42));
732 assert!(dv.insert(100));
733 assert!(!dv.insert(42));
734
735 let mut items: Vec<u64> = dv.iter().collect();
736 items.sort();
737 assert_eq!(items, vec![42, 100]);
738 assert_eq!(dv.len(), 2);
739 }
740
741 #[test]
742 fn test_successful_insert_positions() {
743 let mut dv = DeleteVector::default();
744 let positions = vec![1, 2, 3, 1000, 1 << 33];
745 assert_eq!(dv.insert_positions(&positions).unwrap(), 5);
746
747 let mut collected: Vec<u64> = dv.iter().collect();
748 collected.sort();
749 assert_eq!(collected, positions);
750 }
751
752 #[test]
754 fn test_failed_insertion_unsorted_elements() {
755 let mut dv = DeleteVector::default();
756 let positions = vec![1, 3, 5, 4];
757 let res = dv.insert_positions(&positions);
758 assert!(res.is_err());
759 }
760
761 #[test]
763 fn test_failed_insertion_with_intersection() {
764 let mut dv = DeleteVector::default();
765 let positions = vec![1, 3, 5];
766 assert_eq!(dv.insert_positions(&positions).unwrap(), 3);
767
768 let res = dv.insert_positions(&[2, 4]);
769 assert!(res.is_err());
770 }
771
772 #[test]
774 fn test_failed_insertion_duplicate_elements() {
775 let mut dv = DeleteVector::default();
776 let positions = vec![1, 3, 5, 5];
777 let res = dv.insert_positions(&positions);
778 assert!(res.is_err());
779 }
780
781 fn encode_dv_blob(dv: &DeleteVector) -> Vec<u8> {
782 let mut vector = Vec::with_capacity(dv.inner.serialized_size());
783 dv.inner.serialize_into(&mut vector).unwrap();
784 frame_dv_blob(&vector)
785 }
786
787 fn dv_of(positions: impl IntoIterator<Item = u64>) -> DeleteVector {
788 let mut dv = DeleteVector::default();
789 for pos in positions {
790 dv.insert(pos);
791 }
792 dv
793 }
794
795 fn sorted(dv: &DeleteVector) -> Vec<u64> {
796 let mut positions: Vec<u64> = dv.iter().collect();
797 positions.sort_unstable();
798 positions
799 }
800
801 #[test]
802 fn test_deserialize_roundtrip_empty() {
803 let blob = encode_dv_blob(&DeleteVector::default());
804 assert_eq!(DeleteVector::deserialize(&blob).unwrap().len(), 0);
805 }
806
807 #[test]
808 fn test_deserialize_roundtrip_small() {
809 let positions = [0u64, 5, 100, 1000];
810 let dv = DeleteVector::deserialize(&encode_dv_blob(&dv_of(positions))).unwrap();
811 assert_eq!(sorted(&dv), positions);
812 }
813
814 #[test]
815 fn test_deserialize_roundtrip_spanning_64bit_keys() {
816 let positions = [1u64, 1 << 33, (1 << 33) + 5, 1 << 34];
817 let dv = DeleteVector::deserialize(&encode_dv_blob(&dv_of(positions))).unwrap();
818 assert_eq!(sorted(&dv), positions);
819 }
820
821 #[test]
825 fn test_deserialize_roundtrip_run_optimized() {
826 let mut dv = dv_of(0..10_000);
827 assert!(
828 dv.inner.optimize(),
829 "expected a dense range to run-length encode"
830 );
831 let decoded = DeleteVector::deserialize(&encode_dv_blob(&dv)).unwrap();
832 assert_eq!(decoded.len(), 10_000);
833 let positions = sorted(&decoded);
834 assert_eq!(positions.first(), Some(&0));
835 assert_eq!(positions.last(), Some(&9_999));
836 }
837
838 #[test]
839 fn test_deserialize_rejects_short_blob() {
840 let err = DeleteVector::deserialize(&[0u8; DV_MIN_BLOB_BYTES - 1]).unwrap_err();
841 assert_eq!(err.kind(), ErrorKind::DataInvalid);
842 }
843
844 #[test]
845 fn test_deserialize_rejects_bad_magic() {
846 let mut blob = encode_dv_blob(&dv_of([1]));
847 blob[DV_LENGTH_PREFIX_BYTES] ^= 0xFF;
848 let end = blob.len() - DV_CRC_BYTES;
850 let crc = crc32fast::hash(&blob[DV_LENGTH_PREFIX_BYTES..end]);
851 blob[end..].copy_from_slice(&crc.to_be_bytes());
852 let err = DeleteVector::deserialize(&blob).unwrap_err();
853 assert!(err.message().contains("magic mismatch"), "got: {err}");
854 }
855
856 #[test]
857 fn test_deserialize_rejects_bad_crc() {
858 let mut blob = encode_dv_blob(&dv_of([1, 2, 3]));
859 let end = blob.len() - DV_CRC_BYTES;
860 blob[end] ^= 0xFF;
861 let err = DeleteVector::deserialize(&blob).unwrap_err();
862 assert!(err.message().contains("CRC mismatch"), "got: {err}");
863 }
864
865 #[test]
866 fn test_deserialize_rejects_length_prefix_mismatch() {
867 let mut blob = encode_dv_blob(&dv_of([1]));
868 let declared = u32::from_be_bytes(blob[..DV_LENGTH_PREFIX_BYTES].try_into().unwrap());
869 blob[..DV_LENGTH_PREFIX_BYTES].copy_from_slice(&(declared + 1).to_be_bytes());
870 let err = DeleteVector::deserialize(&blob).unwrap_err();
871 assert!(err.message().contains("length prefix"), "got: {err}");
872 }
873
874 fn raw_roaring_vector(entries: &[(u32, &[u32])]) -> Vec<u8> {
878 let mut vector = Vec::new();
879 vector.extend_from_slice(&(entries.len() as u64).to_le_bytes());
880 for (key, positions) in entries {
881 let mut bitmap = RoaringBitmap::new();
882 for &pos in *positions {
883 bitmap.insert(pos);
884 }
885 vector.extend_from_slice(&key.to_le_bytes());
886 bitmap.serialize_into(&mut vector).unwrap();
887 }
888 vector
889 }
890
891 #[test]
895 fn test_deserialize_rejects_duplicate_keys() {
896 let vector = raw_roaring_vector(&[(5, &[1]), (5, &[2])]);
897 let err = DeleteVector::deserialize(&frame_dv_blob(&vector)).unwrap_err();
898 assert!(err.message().contains("unsigned comparison"), "got: {err}");
899 }
900
901 #[test]
905 fn test_deserialize_rejects_out_of_order_keys() {
906 let vector = raw_roaring_vector(&[(5, &[1]), (3, &[2])]);
907 let err = DeleteVector::deserialize(&frame_dv_blob(&vector)).unwrap_err();
908 assert!(err.message().contains("unsigned comparison"), "got: {err}");
909 }
910
911 #[test]
915 fn test_deserialize_rejects_bitmap_count_overflow() {
916 let vector = (u32::MAX as u64 + 1).to_le_bytes().to_vec();
917 let err = DeleteVector::deserialize(&frame_dv_blob(&vector)).unwrap_err();
918 assert!(err.message().contains("exceeds the"), "got: {err}");
919 }
920}