#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use flashsieve::{BlockIndexBuilder, IncrementalBuilder, NgramFilter};
#[test]
fn test_incremental_append_blocks() {
let index = BlockIndexBuilder::new()
.block_size(1024)
.build(&vec![0xAA; 1024])
.unwrap();
let serialized = index.to_bytes();
let block2 = vec![0xBB; 1024];
let block3 = vec![0xCC; 1024];
let new_serialized =
IncrementalBuilder::append_blocks(&serialized, &[&block2, &block3]).unwrap();
let new_index = flashsieve::BlockIndex::from_bytes_checked(&new_serialized).unwrap();
assert_eq!(new_index.block_count(), 3);
assert_eq!(new_index.total_data_length(), 3072);
}
#[test]
fn test_incremental_append_empty_blocks_list() {
let index = BlockIndexBuilder::new()
.block_size(1024)
.build(&vec![0xAA; 1024])
.unwrap();
let serialized = index.to_bytes();
let new_serialized = IncrementalBuilder::append_blocks(&serialized, &[]).unwrap();
let new_index = flashsieve::BlockIndex::from_bytes_checked(&new_serialized).unwrap();
assert_eq!(new_index.block_count(), 1);
}
#[test]
fn test_incremental_append_with_boundary_byte() {
let block_size = 256;
let mut block1 = vec![b'a'; block_size];
block1[block_size - 2] = b'X';
block1[block_size - 1] = b'Y';
let index = BlockIndexBuilder::new()
.block_size(block_size)
.bloom_bits(4096) .build(&block1)
.unwrap();
let serialized = index.to_bytes();
let mut block2 = vec![b'b'; block_size];
block2[0] = b'Z';
let pattern = b"YZ";
let ngram_filter = NgramFilter::from_patterns(&[pattern.as_slice()]);
let appended_without = IncrementalBuilder::append_blocks(&serialized, &[&block2]).unwrap();
let index_without = flashsieve::BlockIndex::from_bytes_checked(&appended_without).unwrap();
let _candidates_without = index_without.candidate_blocks_ngram(&ngram_filter);
let last_byte_of_old = block1.last().copied(); let appended_with =
IncrementalBuilder::append_blocks_with_boundary(&serialized, last_byte_of_old, &[&block2])
.unwrap();
let index_with = flashsieve::BlockIndex::from_bytes_checked(&appended_with).unwrap();
let candidates_with = index_with.candidate_blocks_ngram(&ngram_filter);
assert_eq!(index_without.block_count(), 2);
assert_eq!(index_with.block_count(), 2);
let found_in_block1 = candidates_with.iter().any(|r| r.offset == block_size);
assert!(
found_in_block1,
"Boundary ngram YZ should be found in block 1"
);
}
#[test]
fn test_mmap_preserves_exact_pairs() {
use flashsieve::{ByteFilter, MmapBlockIndex};
let block_size = 256;
let mut data = vec![b'a'; block_size];
data[100] = b'X';
data[101] = b'Y';
let index = BlockIndexBuilder::new()
.block_size(block_size)
.bloom_bits(8192) .build(&data)
.unwrap();
let serialized = index.to_bytes();
let mmap_index = MmapBlockIndex::from_slice(&serialized).unwrap();
let bloom_ref = mmap_index.try_bloom(0).unwrap();
assert!(
bloom_ref.maybe_contains_exact(b'X', b'Y'),
"Exact-pair table should find XY pattern that was inserted"
);
assert!(
!bloom_ref.maybe_contains_exact(b'Z', b'W'),
"Exact-pair table should reject ZW pattern that was not inserted"
);
let pattern = b"XY";
let byte_filter = ByteFilter::from_patterns(&[pattern.as_slice()]);
let ngram_filter = NgramFilter::from_patterns(&[pattern.as_slice()]);
let mmap_candidates = mmap_index.candidate_blocks(&byte_filter, &ngram_filter);
let heap_candidates = index.candidate_blocks(&byte_filter, &ngram_filter);
assert!(
!mmap_candidates.is_empty(),
"Mmap index with exact-pairs should find XY pattern"
);
assert_eq!(
mmap_candidates, heap_candidates,
"Mmap and heap indexes should produce identical results"
);
}