1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
22use parking_lot::RwLock;
23use rustc_hash::FxHashMap;
24use std::io::{self, Read, Write};
25use std::sync::Arc;
26
27#[cfg(feature = "fst-index")]
28use super::sstable_index::FstBlockIndex;
29use super::sstable_index::{BlockAddr, BlockIndex, MmapBlockIndex};
30use crate::compression::{CompressionDict, CompressionLevel};
31use crate::directories::{FileHandle, OwnedBytes};
32
33pub const SSTABLE_MAGIC: u32 = 0x53544234; pub const BLOCK_SIZE: usize = 16 * 1024;
39
40pub const DEFAULT_DICT_SIZE: usize = 64 * 1024;
42
43pub const BLOOM_BITS_PER_KEY: usize = 10;
45
46pub const BLOOM_HASH_COUNT: usize = 7;
48
49const MAX_SSTABLE_BLOCK_BYTES: usize = 64 * 1024 * 1024;
50const MAX_SSTABLE_DICTIONARY_BYTES: u64 = 16 * 1024 * 1024;
51
52pub type PrefixScanResult<V> = (Vec<(Vec<u8>, V)>, bool);
54
55#[derive(Debug, Clone)]
61pub struct BloomFilter {
62 bits: BloomBits,
63 num_bits: usize,
64 num_hashes: usize,
65}
66
67#[derive(Debug, Clone)]
69enum BloomBits {
70 Vec(Vec<u64>),
72 Bytes(OwnedBytes),
74}
75
76impl BloomBits {
77 #[inline]
78 fn len(&self) -> usize {
79 match self {
80 BloomBits::Vec(v) => v.len(),
81 BloomBits::Bytes(b) => b.len() / 8,
82 }
83 }
84
85 #[inline]
86 fn get(&self, word_idx: usize) -> u64 {
87 match self {
88 BloomBits::Vec(v) => v[word_idx],
89 BloomBits::Bytes(b) => {
90 let off = word_idx * 8;
91 u64::from_le_bytes([
92 b[off],
93 b[off + 1],
94 b[off + 2],
95 b[off + 3],
96 b[off + 4],
97 b[off + 5],
98 b[off + 6],
99 b[off + 7],
100 ])
101 }
102 }
103 }
104
105 #[inline]
106 fn set_bit(&mut self, word_idx: usize, bit_idx: usize) {
107 match self {
108 BloomBits::Vec(v) => v[word_idx] |= 1u64 << bit_idx,
109 BloomBits::Bytes(_) => panic!("cannot mutate read-only bloom filter"),
110 }
111 }
112
113 fn size_bytes(&self) -> usize {
114 match self {
115 BloomBits::Vec(v) => v.len() * 8,
116 BloomBits::Bytes(b) => b.len(),
117 }
118 }
119}
120
121impl BloomFilter {
122 pub fn new(expected_keys: usize, bits_per_key: usize) -> Self {
124 let num_bits = expected_keys.saturating_mul(bits_per_key).max(64);
125 let num_words = num_bits.div_ceil(64);
126 Self {
127 bits: BloomBits::Vec(vec![0u64; num_words]),
128 num_bits,
129 num_hashes: BLOOM_HASH_COUNT,
130 }
131 }
132
133 pub fn from_bytes_mutable(data: &[u8]) -> io::Result<Self> {
137 if data.len() < 12 {
138 return Err(io::Error::new(
139 io::ErrorKind::InvalidData,
140 "Bloom filter data too short",
141 ));
142 }
143 let num_bits = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
144 let num_hashes = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as usize;
145 let num_words = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize;
146
147 validate_bloom_header(data.len(), num_bits, num_hashes, num_words)?;
148 let expected_len = 12 + num_words * 8;
149 if data.len() != expected_len {
150 return Err(io::Error::new(
151 io::ErrorKind::InvalidData,
152 "Bloom filter data truncated",
153 ));
154 }
155
156 let mut vec = vec![0u64; num_words];
157 for (i, v) in vec.iter_mut().enumerate() {
158 let off = 12 + i * 8;
159 *v = u64::from_le_bytes(data[off..off + 8].try_into().unwrap());
160 }
161
162 Ok(Self {
163 bits: BloomBits::Vec(vec),
164 num_bits,
165 num_hashes,
166 })
167 }
168
169 pub fn from_owned_bytes(data: OwnedBytes) -> io::Result<Self> {
171 if data.len() < 12 {
172 return Err(io::Error::new(
173 io::ErrorKind::InvalidData,
174 "Bloom filter data too short",
175 ));
176 }
177 let d = data.as_slice();
178 let num_bits = u32::from_le_bytes([d[0], d[1], d[2], d[3]]) as usize;
179 let num_hashes = u32::from_le_bytes([d[4], d[5], d[6], d[7]]) as usize;
180 let num_words = u32::from_le_bytes([d[8], d[9], d[10], d[11]]) as usize;
181
182 validate_bloom_header(d.len(), num_bits, num_hashes, num_words)?;
183 let expected_len = 12 + num_words * 8;
184 if d.len() != expected_len {
185 return Err(io::Error::new(
186 io::ErrorKind::InvalidData,
187 "Bloom filter data truncated",
188 ));
189 }
190
191 let bits_bytes = data.slice(12..12 + num_words * 8);
193
194 Ok(Self {
195 bits: BloomBits::Bytes(bits_bytes),
196 num_bits,
197 num_hashes,
198 })
199 }
200
201 pub fn to_bytes(&self) -> Vec<u8> {
203 let num_words = self.bits.len();
204 let mut data = Vec::with_capacity(12 + num_words * 8);
205 data.write_u32::<LittleEndian>(self.num_bits as u32)
206 .unwrap();
207 data.write_u32::<LittleEndian>(self.num_hashes as u32)
208 .unwrap();
209 data.write_u32::<LittleEndian>(num_words as u32).unwrap();
210 for i in 0..num_words {
211 data.write_u64::<LittleEndian>(self.bits.get(i)).unwrap();
212 }
213 data
214 }
215
216 pub fn insert(&mut self, key: &[u8]) {
218 let (h1, h2) = self.hash_pair(key);
219 for i in 0..self.num_hashes {
220 let bit_pos = self.get_bit_pos(h1, h2, i);
221 let word_idx = bit_pos / 64;
222 let bit_idx = bit_pos % 64;
223 if word_idx < self.bits.len() {
224 self.bits.set_bit(word_idx, bit_idx);
225 }
226 }
227 }
228
229 pub fn may_contain(&self, key: &[u8]) -> bool {
232 let (h1, h2) = self.hash_pair(key);
233 for i in 0..self.num_hashes {
234 let bit_pos = self.get_bit_pos(h1, h2, i);
235 let word_idx = bit_pos / 64;
236 let bit_idx = bit_pos % 64;
237 if word_idx >= self.bits.len() || (self.bits.get(word_idx) & (1u64 << bit_idx)) == 0 {
238 return false;
239 }
240 }
241 true
242 }
243
244 pub fn size_bytes(&self) -> usize {
246 12 + self.bits.size_bytes()
247 }
248
249 pub fn insert_hashed(&mut self, h1: u64, h2: u64) {
251 for i in 0..self.num_hashes {
252 let bit_pos = self.get_bit_pos(h1, h2, i);
253 let word_idx = bit_pos / 64;
254 let bit_idx = bit_pos % 64;
255 if word_idx < self.bits.len() {
256 self.bits.set_bit(word_idx, bit_idx);
257 }
258 }
259 }
260
261 #[inline]
263 fn hash_pair(&self, key: &[u8]) -> (u64, u64) {
264 let mut h1: u64 = 0xcbf29ce484222325;
265 let mut h2: u64 = 0x84222325cbf29ce4;
266 for &byte in key {
267 h1 ^= byte as u64;
268 h1 = h1.wrapping_mul(0x100000001b3);
269 h2 = h2.wrapping_mul(0x100000001b3);
270 h2 ^= byte as u64;
271 }
272 (h1, h2)
273 }
274
275 #[inline]
277 fn get_bit_pos(&self, h1: u64, h2: u64, i: usize) -> usize {
278 (h1.wrapping_add((i as u64).wrapping_mul(h2)) % (self.num_bits as u64)) as usize
279 }
280}
281
282fn validate_bloom_header(
283 data_len: usize,
284 num_bits: usize,
285 num_hashes: usize,
286 num_words: usize,
287) -> io::Result<()> {
288 if num_bits == 0 || num_hashes == 0 || num_hashes > 32 || num_words == 0 {
289 return Err(io::Error::new(
290 io::ErrorKind::InvalidData,
291 "invalid bloom filter parameters",
292 ));
293 }
294 let word_bytes = num_words
295 .checked_mul(8)
296 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "bloom filter size overflow"))?;
297 let expected_len = 12usize
298 .checked_add(word_bytes)
299 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "bloom filter size overflow"))?;
300 let capacity_bits = num_words
301 .checked_mul(64)
302 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "bloom filter size overflow"))?;
303 if expected_len > data_len
304 || num_bits > capacity_bits
305 || num_bits <= capacity_bits.saturating_sub(64)
306 {
307 return Err(io::Error::new(
308 io::ErrorKind::InvalidData,
309 "inconsistent bloom filter dimensions",
310 ));
311 }
312 Ok(())
313}
314
315#[inline]
318fn bloom_hash_pair(key: &[u8]) -> (u64, u64) {
319 let mut h1: u64 = 0xcbf29ce484222325;
320 let mut h2: u64 = 0x84222325cbf29ce4;
321 for &byte in key {
322 h1 ^= byte as u64;
323 h1 = h1.wrapping_mul(0x100000001b3);
324 h2 = h2.wrapping_mul(0x100000001b3);
325 h2 ^= byte as u64;
326 }
327 (h1, h2)
328}
329
330pub trait SSTableValue: Clone + Send + Sync {
332 fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()>;
333 fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self>;
334}
335
336impl SSTableValue for u64 {
338 fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
339 write_vint(writer, *self)
340 }
341
342 fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self> {
343 read_vint(reader)
344 }
345}
346
347impl SSTableValue for Vec<u8> {
349 fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
350 write_vint(writer, self.len() as u64)?;
351 writer.write_all(self)
352 }
353
354 fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self> {
355 let len = usize::try_from(read_vint(reader)?).map_err(|_| {
356 io::Error::new(io::ErrorKind::InvalidData, "SSTable value length too large")
357 })?;
358 if len > MAX_SSTABLE_BLOCK_BYTES {
359 return Err(io::Error::new(
360 io::ErrorKind::InvalidData,
361 "SSTable value exceeds block safety limit",
362 ));
363 }
364 let mut data = vec![0u8; len];
365 reader.read_exact(&mut data)?;
366 Ok(data)
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub struct SparseDimInfo {
374 pub offset: u64,
376 pub length: u32,
378}
379
380impl SparseDimInfo {
381 pub fn new(offset: u64, length: u32) -> Self {
382 Self { offset, length }
383 }
384}
385
386impl SSTableValue for SparseDimInfo {
387 fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
388 write_vint(writer, self.offset)?;
389 write_vint(writer, self.length as u64)
390 }
391
392 fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self> {
393 let offset = read_vint(reader)?;
394 let length = u32::try_from(read_vint(reader)?).map_err(|_| {
395 io::Error::new(
396 io::ErrorKind::InvalidData,
397 "sparse posting length exceeds u32",
398 )
399 })?;
400 Ok(Self { offset, length })
401 }
402}
403
404pub const MAX_INLINE_POSTINGS: usize = 3;
406
407#[derive(Debug, Clone, PartialEq, Eq)]
415pub enum TermInfo {
416 Inline {
419 doc_freq: u8,
421 data: [u8; 16],
424 data_len: u8,
426 },
427 External {
429 posting_offset: u64,
430 posting_len: u64,
431 doc_freq: u32,
432 position_offset: u64,
434 position_len: u64,
436 },
437}
438
439impl TermInfo {
440 pub fn external(posting_offset: u64, posting_len: u64, doc_freq: u32) -> Self {
442 TermInfo::External {
443 posting_offset,
444 posting_len,
445 doc_freq,
446 position_offset: 0,
447 position_len: 0,
448 }
449 }
450
451 pub fn external_with_positions(
453 posting_offset: u64,
454 posting_len: u64,
455 doc_freq: u32,
456 position_offset: u64,
457 position_len: u64,
458 ) -> Self {
459 TermInfo::External {
460 posting_offset,
461 posting_len,
462 doc_freq,
463 position_offset,
464 position_len,
465 }
466 }
467
468 pub fn try_inline(doc_ids: &[u32], term_freqs: &[u32]) -> Option<Self> {
471 if doc_ids.len() > MAX_INLINE_POSTINGS
472 || doc_ids.is_empty()
473 || doc_ids.len() != term_freqs.len()
474 {
475 return None;
476 }
477
478 let mut data = [0u8; 16];
479 let mut cursor = std::io::Cursor::new(&mut data[..]);
480 let mut prev_doc_id = 0u32;
481
482 for (i, &doc_id) in doc_ids.iter().enumerate() {
483 let delta = doc_id.checked_sub(prev_doc_id)?;
484 if write_vint(&mut cursor, delta as u64).is_err() {
485 return None;
486 }
487 if write_vint(&mut cursor, term_freqs[i] as u64).is_err() {
488 return None;
489 }
490 prev_doc_id = doc_id;
491 }
492
493 let data_len = cursor.position() as u8;
494 if data_len > 16 {
495 return None;
496 }
497
498 Some(TermInfo::Inline {
499 doc_freq: doc_ids.len() as u8,
500 data,
501 data_len,
502 })
503 }
504
505 pub fn try_inline_iter(count: usize, iter: impl Iterator<Item = (u32, u32)>) -> Option<Self> {
509 if count > MAX_INLINE_POSTINGS || count == 0 {
510 return None;
511 }
512
513 let mut data = [0u8; 16];
514 let mut cursor = std::io::Cursor::new(&mut data[..]);
515 let mut prev_doc_id = 0u32;
516
517 let mut actual_count = 0usize;
518 for (doc_id, tf) in iter {
519 if actual_count >= count {
520 return None;
521 }
522 let delta = doc_id.checked_sub(prev_doc_id)?;
523 if write_vint(&mut cursor, delta as u64).is_err() {
524 return None;
525 }
526 if write_vint(&mut cursor, tf as u64).is_err() {
527 return None;
528 }
529 prev_doc_id = doc_id;
530 actual_count += 1;
531 }
532
533 if actual_count != count {
534 return None;
535 }
536
537 let data_len = cursor.position() as u8;
538
539 Some(TermInfo::Inline {
540 doc_freq: count as u8,
541 data,
542 data_len,
543 })
544 }
545
546 pub fn doc_freq(&self) -> u32 {
548 match self {
549 TermInfo::Inline { doc_freq, .. } => *doc_freq as u32,
550 TermInfo::External { doc_freq, .. } => *doc_freq,
551 }
552 }
553
554 pub fn is_inline(&self) -> bool {
556 matches!(self, TermInfo::Inline { .. })
557 }
558
559 pub fn external_info(&self) -> Option<(u64, u64)> {
561 match self {
562 TermInfo::External {
563 posting_offset,
564 posting_len,
565 ..
566 } => Some((*posting_offset, *posting_len)),
567 TermInfo::Inline { .. } => None,
568 }
569 }
570
571 pub fn position_info(&self) -> Option<(u64, u64)> {
573 match self {
574 TermInfo::External {
575 position_offset,
576 position_len,
577 ..
578 } if *position_len > 0 => Some((*position_offset, *position_len)),
579 _ => None,
580 }
581 }
582
583 pub fn decode_inline(&self) -> Option<(Vec<u32>, Vec<u32>)> {
586 match self {
587 TermInfo::Inline {
588 doc_freq,
589 data,
590 data_len,
591 } => {
592 if *doc_freq == 0
593 || *doc_freq as usize > MAX_INLINE_POSTINGS
594 || *data_len as usize > data.len()
595 {
596 return None;
597 }
598 let mut doc_ids = Vec::with_capacity(*doc_freq as usize);
599 let mut term_freqs = Vec::with_capacity(*doc_freq as usize);
600 let mut reader = &data[..*data_len as usize];
601 let mut prev_doc_id = 0u32;
602
603 for _ in 0..*doc_freq {
604 let delta = read_vint(&mut reader).ok()? as u32;
605 let tf = read_vint(&mut reader).ok()? as u32;
606 let doc_id = prev_doc_id.checked_add(delta)?;
607 doc_ids.push(doc_id);
608 term_freqs.push(tf);
609 prev_doc_id = doc_id;
610 }
611
612 Some((doc_ids, term_freqs))
613 }
614 TermInfo::External { .. } => None,
615 }
616 }
617}
618
619impl SSTableValue for TermInfo {
620 fn serialize<W: Write>(&self, writer: &mut W) -> io::Result<()> {
621 match self {
622 TermInfo::Inline {
623 doc_freq,
624 data,
625 data_len,
626 } => {
627 if *doc_freq == 0
628 || *doc_freq as usize > MAX_INLINE_POSTINGS
629 || *data_len as usize > data.len()
630 {
631 return Err(io::Error::new(
632 io::ErrorKind::InvalidInput,
633 "invalid inline TermInfo",
634 ));
635 }
636 writer.write_u8(0xFF)?;
638 writer.write_u8(*doc_freq)?;
639 writer.write_u8(*data_len)?;
640 writer.write_all(&data[..*data_len as usize])?;
641 }
642 TermInfo::External {
643 posting_offset,
644 posting_len,
645 doc_freq,
646 position_offset,
647 position_len,
648 } => {
649 if *position_len > 0 {
652 writer.write_u8(0x01)?;
653 write_vint(writer, *doc_freq as u64)?;
654 write_vint(writer, *posting_offset)?;
655 write_vint(writer, *posting_len)?;
656 write_vint(writer, *position_offset)?;
657 write_vint(writer, *position_len)?;
658 } else {
659 writer.write_u8(0x00)?;
660 write_vint(writer, *doc_freq as u64)?;
661 write_vint(writer, *posting_offset)?;
662 write_vint(writer, *posting_len)?;
663 }
664 }
665 }
666 Ok(())
667 }
668
669 fn deserialize<R: Read>(reader: &mut R) -> io::Result<Self> {
670 let tag = reader.read_u8()?;
671
672 if tag == 0xFF {
673 let doc_freq = reader.read_u8()?;
675 let data_len = reader.read_u8()?;
676 if doc_freq == 0 || doc_freq as usize > MAX_INLINE_POSTINGS || data_len as usize > 16 {
677 return Err(io::Error::new(
678 io::ErrorKind::InvalidData,
679 "invalid inline TermInfo lengths",
680 ));
681 }
682 let mut data = [0u8; 16];
683 reader.read_exact(&mut data[..data_len as usize])?;
684 Ok(TermInfo::Inline {
685 doc_freq,
686 data,
687 data_len,
688 })
689 } else if tag == 0x00 {
690 let doc_freq = read_vint(reader)? as u32;
692 let posting_offset = read_vint(reader)?;
693 let posting_len = read_vint(reader)?;
694 Ok(TermInfo::External {
695 posting_offset,
696 posting_len,
697 doc_freq,
698 position_offset: 0,
699 position_len: 0,
700 })
701 } else if tag == 0x01 {
702 let doc_freq = read_vint(reader)? as u32;
704 let posting_offset = read_vint(reader)?;
705 let posting_len = read_vint(reader)?;
706 let position_offset = read_vint(reader)?;
707 let position_len = read_vint(reader)?;
708 Ok(TermInfo::External {
709 posting_offset,
710 posting_len,
711 doc_freq,
712 position_offset,
713 position_len,
714 })
715 } else {
716 Err(io::Error::new(
717 io::ErrorKind::InvalidData,
718 format!("Invalid TermInfo tag: {}", tag),
719 ))
720 }
721 }
722}
723
724pub fn write_vint<W: Write + ?Sized>(writer: &mut W, mut value: u64) -> io::Result<()> {
726 loop {
727 let byte = (value & 0x7F) as u8;
728 value >>= 7;
729 if value == 0 {
730 writer.write_u8(byte)?;
731 return Ok(());
732 } else {
733 writer.write_u8(byte | 0x80)?;
734 }
735 }
736}
737
738pub fn read_vint<R: Read>(reader: &mut R) -> io::Result<u64> {
740 let mut result = 0u64;
741 let mut shift = 0;
742
743 loop {
744 let byte = reader.read_u8()?;
745 result |= ((byte & 0x7F) as u64) << shift;
746 if byte & 0x80 == 0 {
747 return Ok(result);
748 }
749 shift += 7;
750 if shift >= 64 {
751 return Err(io::Error::new(
752 io::ErrorKind::InvalidData,
753 "varint too long",
754 ));
755 }
756 }
757}
758
759pub fn common_prefix_len(a: &[u8], b: &[u8]) -> usize {
761 a.iter().zip(b.iter()).take_while(|(x, y)| x == y).count()
762}
763
764#[derive(Debug, Clone)]
766pub struct SSTableStats {
767 pub num_blocks: usize,
768 pub num_sparse_entries: usize,
769 pub num_entries: u64,
770 pub has_bloom_filter: bool,
771 pub has_dictionary: bool,
772 pub bloom_filter_size: usize,
773 pub dictionary_size: usize,
774}
775
776#[derive(Debug, Clone)]
778pub struct SSTableWriterConfig {
779 pub compression_level: CompressionLevel,
781 pub use_dictionary: bool,
783 pub dict_size: usize,
785 pub use_bloom_filter: bool,
787 pub bloom_bits_per_key: usize,
789}
790
791impl Default for SSTableWriterConfig {
792 fn default() -> Self {
793 Self::from_optimization(crate::structures::IndexOptimization::default())
794 }
795}
796
797impl SSTableWriterConfig {
798 pub fn from_optimization(optimization: crate::structures::IndexOptimization) -> Self {
800 use crate::structures::IndexOptimization;
801 match optimization {
802 IndexOptimization::Adaptive => Self {
803 compression_level: CompressionLevel::BETTER, use_dictionary: false,
805 dict_size: DEFAULT_DICT_SIZE,
806 use_bloom_filter: true, bloom_bits_per_key: BLOOM_BITS_PER_KEY,
808 },
809 IndexOptimization::SizeOptimized => Self {
810 compression_level: CompressionLevel::MAX, use_dictionary: true,
812 dict_size: DEFAULT_DICT_SIZE,
813 use_bloom_filter: true,
814 bloom_bits_per_key: BLOOM_BITS_PER_KEY,
815 },
816 IndexOptimization::PerformanceOptimized => Self {
817 compression_level: CompressionLevel::FAST, use_dictionary: false,
819 dict_size: DEFAULT_DICT_SIZE,
820 use_bloom_filter: true, bloom_bits_per_key: BLOOM_BITS_PER_KEY,
822 },
823 }
824 }
825
826 pub fn fast() -> Self {
828 Self::from_optimization(crate::structures::IndexOptimization::PerformanceOptimized)
829 }
830
831 pub fn max_compression() -> Self {
833 Self::from_optimization(crate::structures::IndexOptimization::SizeOptimized)
834 }
835}
836
837pub struct SSTableWriter<W: Write, V: SSTableValue> {
843 writer: W,
844 block_buffer: Vec<u8>,
845 prev_key: Vec<u8>,
846 index: Vec<BlockIndexEntry>,
847 current_offset: u64,
848 num_entries: u64,
849 block_first_key: Option<Vec<u8>>,
850 config: SSTableWriterConfig,
851 dictionary: Option<CompressionDict>,
853 bloom_hashes: Vec<(u64, u64)>,
856 _phantom: std::marker::PhantomData<V>,
857}
858
859impl<W: Write, V: SSTableValue> SSTableWriter<W, V> {
860 pub fn new(writer: W) -> Self {
862 Self::with_config(writer, SSTableWriterConfig::default())
863 }
864
865 pub fn with_config(writer: W, config: SSTableWriterConfig) -> Self {
867 Self {
868 writer,
869 block_buffer: Vec::with_capacity(BLOCK_SIZE),
870 prev_key: Vec::new(),
871 index: Vec::new(),
872 current_offset: 0,
873 num_entries: 0,
874 block_first_key: None,
875 config,
876 dictionary: None,
877 bloom_hashes: Vec::new(),
878 _phantom: std::marker::PhantomData,
879 }
880 }
881
882 pub fn with_dictionary(
884 writer: W,
885 config: SSTableWriterConfig,
886 dictionary: CompressionDict,
887 ) -> Self {
888 Self {
889 writer,
890 block_buffer: Vec::with_capacity(BLOCK_SIZE),
891 prev_key: Vec::new(),
892 index: Vec::new(),
893 current_offset: 0,
894 num_entries: 0,
895 block_first_key: None,
896 config,
897 dictionary: Some(dictionary),
898 bloom_hashes: Vec::new(),
899 _phantom: std::marker::PhantomData,
900 }
901 }
902
903 pub fn insert(&mut self, key: &[u8], value: &V) -> io::Result<()> {
904 if self.block_first_key.is_none() {
905 self.block_first_key = Some(key.to_vec());
906 }
907
908 if self.config.use_bloom_filter {
910 self.bloom_hashes.push(bloom_hash_pair(key));
911 }
912
913 let prefix_len = common_prefix_len(&self.prev_key, key);
914 let suffix = &key[prefix_len..];
915
916 write_vint(&mut self.block_buffer, prefix_len as u64)?;
917 write_vint(&mut self.block_buffer, suffix.len() as u64)?;
918 self.block_buffer.extend_from_slice(suffix);
919 value.serialize(&mut self.block_buffer)?;
920
921 self.prev_key.clear();
922 self.prev_key.extend_from_slice(key);
923 self.num_entries += 1;
924
925 if self.block_buffer.len() >= BLOCK_SIZE {
926 self.flush_block()?;
927 }
928
929 Ok(())
930 }
931
932 fn flush_block(&mut self) -> io::Result<()> {
934 if self.block_buffer.is_empty() {
935 return Ok(());
936 }
937
938 let compressed = if let Some(ref dict) = self.dictionary {
940 crate::compression::compress_with_dict(
941 &self.block_buffer,
942 self.config.compression_level,
943 dict,
944 )?
945 } else {
946 crate::compression::compress(&self.block_buffer, self.config.compression_level)?
947 };
948
949 if let Some(first_key) = self.block_first_key.take() {
950 self.index.push(BlockIndexEntry {
951 first_key,
952 offset: self.current_offset,
953 length: compressed.len() as u32,
954 });
955 }
956
957 self.writer.write_all(&compressed)?;
958 self.current_offset += compressed.len() as u64;
959 self.block_buffer.clear();
960 self.prev_key.clear();
961
962 Ok(())
963 }
964
965 pub fn finish(mut self) -> io::Result<W> {
966 self.flush_block()?;
968
969 let bloom_filter = if self.config.use_bloom_filter && !self.bloom_hashes.is_empty() {
971 let mut bloom =
972 BloomFilter::new(self.bloom_hashes.len(), self.config.bloom_bits_per_key);
973 for (h1, h2) in &self.bloom_hashes {
974 bloom.insert_hashed(*h1, *h2);
975 }
976 Some(bloom)
977 } else {
978 None
979 };
980
981 let data_end_offset = self.current_offset;
982
983 let entries: Vec<(Vec<u8>, BlockAddr)> = self
986 .index
987 .iter()
988 .map(|e| {
989 (
990 e.first_key.clone(),
991 BlockAddr {
992 offset: e.offset,
993 length: e.length,
994 },
995 )
996 })
997 .collect();
998
999 #[cfg(feature = "native")]
1001 let index_bytes = FstBlockIndex::build(&entries)?;
1002 #[cfg(not(feature = "native"))]
1003 let index_bytes = MmapBlockIndex::build(&entries)?;
1004
1005 self.writer
1007 .write_u32::<LittleEndian>(index_bytes.len() as u32)?;
1008 self.writer.write_all(&index_bytes)?;
1009 self.current_offset += 4 + index_bytes.len() as u64;
1010
1011 let bloom_offset = if let Some(ref bloom) = bloom_filter {
1013 let bloom_data = bloom.to_bytes();
1014 let offset = self.current_offset;
1015 self.writer.write_all(&bloom_data)?;
1016 self.current_offset += bloom_data.len() as u64;
1017 offset
1018 } else {
1019 0
1020 };
1021
1022 let dict_offset = if let Some(ref dict) = self.dictionary {
1024 let dict_bytes = dict.as_bytes();
1025 let offset = self.current_offset;
1026 self.writer
1027 .write_u32::<LittleEndian>(dict_bytes.len() as u32)?;
1028 self.writer.write_all(dict_bytes)?;
1029 self.current_offset += 4 + dict_bytes.len() as u64;
1030 offset
1031 } else {
1032 0
1033 };
1034
1035 self.writer.write_u64::<LittleEndian>(data_end_offset)?;
1037 self.writer.write_u64::<LittleEndian>(self.num_entries)?;
1038 self.writer.write_u64::<LittleEndian>(bloom_offset)?; self.writer.write_u64::<LittleEndian>(dict_offset)?; self.writer
1041 .write_u8(self.config.compression_level.0 as u8)?;
1042 self.writer.write_u32::<LittleEndian>(SSTABLE_MAGIC)?;
1043
1044 Ok(self.writer)
1045 }
1046}
1047
1048#[derive(Debug, Clone)]
1050struct BlockIndexEntry {
1051 first_key: Vec<u8>,
1052 offset: u64,
1053 length: u32,
1054}
1055
1056pub struct AsyncSSTableReader<V: SSTableValue> {
1063 data_slice: FileHandle,
1065 block_index: BlockIndex,
1067 num_entries: u64,
1068 cache: RwLock<BlockCache>,
1070 bloom_filter: Option<BloomFilter>,
1072 dictionary: Option<CompressionDict>,
1074 #[allow(dead_code)]
1076 compression_level: CompressionLevel,
1077 _phantom: std::marker::PhantomData<V>,
1078}
1079
1080struct BlockCache {
1086 blocks: FxHashMap<u64, Arc<[u8]>>,
1087 lru_order: std::collections::VecDeque<u64>,
1088 max_blocks: usize,
1089}
1090
1091impl BlockCache {
1092 fn new(max_blocks: usize) -> Self {
1093 Self {
1094 blocks: FxHashMap::default(),
1095 lru_order: std::collections::VecDeque::with_capacity(max_blocks),
1096 max_blocks,
1097 }
1098 }
1099
1100 fn get(&mut self, offset: u64) -> Option<Arc<[u8]>> {
1101 if self.blocks.contains_key(&offset) {
1102 self.promote(offset);
1103 self.blocks.get(&offset).map(Arc::clone)
1104 } else {
1105 None
1106 }
1107 }
1108
1109 fn peek(&self, offset: u64) -> Option<Arc<[u8]>> {
1111 self.blocks.get(&offset).map(Arc::clone)
1112 }
1113
1114 fn insert(&mut self, offset: u64, block: Arc<[u8]>) {
1115 if self.max_blocks == 0 {
1116 return;
1117 }
1118 if self.blocks.contains_key(&offset) {
1119 self.promote(offset);
1120 return;
1121 }
1122 while self.blocks.len() >= self.max_blocks {
1123 if let Some(evict_offset) = self.lru_order.pop_front() {
1124 self.blocks.remove(&evict_offset);
1125 } else {
1126 break;
1127 }
1128 }
1129 self.blocks.insert(offset, block);
1130 self.lru_order.push_back(offset);
1131 }
1132
1133 fn promote(&mut self, offset: u64) {
1135 if let Some(pos) = self.lru_order.iter().position(|&k| k == offset) {
1136 self.lru_order.remove(pos);
1137 self.lru_order.push_back(offset);
1138 }
1139 }
1140}
1141
1142impl<V: SSTableValue> AsyncSSTableReader<V> {
1143 pub async fn open(file_handle: FileHandle, cache_blocks: usize) -> io::Result<Self> {
1148 let file_len = file_handle.len();
1149 if file_len < 37 {
1150 return Err(io::Error::new(
1151 io::ErrorKind::InvalidData,
1152 "SSTable too small",
1153 ));
1154 }
1155
1156 let footer_bytes = file_handle
1159 .read_bytes_range(file_len - 37..file_len)
1160 .await?;
1161
1162 let mut reader = footer_bytes.as_slice();
1163 let data_end_offset = reader.read_u64::<LittleEndian>()?;
1164 let num_entries = reader.read_u64::<LittleEndian>()?;
1165 let bloom_offset = reader.read_u64::<LittleEndian>()?;
1166 let dict_offset = reader.read_u64::<LittleEndian>()?;
1167 let compression_level = CompressionLevel(reader.read_u8()? as i32);
1168 let magic = reader.read_u32::<LittleEndian>()?;
1169
1170 if magic != SSTABLE_MAGIC {
1171 return Err(io::Error::new(
1172 io::ErrorKind::InvalidData,
1173 format!("Invalid SSTable magic: 0x{:08X}", magic),
1174 ));
1175 }
1176
1177 let footer_start = file_len - 37;
1178 if data_end_offset > footer_start {
1179 return Err(io::Error::new(
1180 io::ErrorKind::InvalidData,
1181 "SSTable data section extends past its footer",
1182 ));
1183 }
1184 if bloom_offset != 0 && (bloom_offset < data_end_offset || bloom_offset >= footer_start) {
1185 return Err(io::Error::new(
1186 io::ErrorKind::InvalidData,
1187 "SSTable bloom filter offset is out of bounds",
1188 ));
1189 }
1190 if dict_offset != 0
1191 && (dict_offset < data_end_offset
1192 || dict_offset >= footer_start
1193 || (bloom_offset != 0 && dict_offset <= bloom_offset))
1194 {
1195 return Err(io::Error::new(
1196 io::ErrorKind::InvalidData,
1197 "SSTable dictionary offset is out of bounds",
1198 ));
1199 }
1200
1201 let index_start = data_end_offset;
1203 let index_end = if bloom_offset != 0 {
1204 bloom_offset
1205 } else if dict_offset != 0 {
1206 dict_offset
1207 } else {
1208 footer_start
1209 };
1210 let index_bytes = file_handle.read_bytes_range(index_start..index_end).await?;
1211
1212 let mut idx_reader = index_bytes.as_slice();
1214 let index_len = idx_reader.read_u32::<LittleEndian>()? as usize;
1215
1216 if index_len != idx_reader.len() {
1217 return Err(io::Error::new(
1218 io::ErrorKind::InvalidData,
1219 "Index data truncated",
1220 ));
1221 }
1222
1223 let index_data = index_bytes.slice(4..4 + index_len);
1224
1225 #[cfg(feature = "fst-index")]
1227 let block_index = match FstBlockIndex::load(index_data.clone()) {
1228 Ok(fst_idx) => BlockIndex::Fst(fst_idx),
1229 Err(_) => BlockIndex::Mmap(MmapBlockIndex::load(index_data)?),
1230 };
1231 #[cfg(not(feature = "fst-index"))]
1232 let block_index = BlockIndex::Mmap(MmapBlockIndex::load(index_data)?);
1233
1234 let mut expected_offset = 0u64;
1235 for addr in block_index.all_addrs() {
1236 let end = addr.offset.checked_add(addr.length as u64).ok_or_else(|| {
1237 io::Error::new(io::ErrorKind::InvalidData, "SSTable block range overflow")
1238 })?;
1239 if addr.length == 0 || addr.offset != expected_offset || end > data_end_offset {
1240 return Err(io::Error::new(
1241 io::ErrorKind::InvalidData,
1242 "SSTable block addresses are inconsistent",
1243 ));
1244 }
1245 expected_offset = end;
1246 }
1247 if expected_offset != data_end_offset {
1248 return Err(io::Error::new(
1249 io::ErrorKind::InvalidData,
1250 "SSTable block addresses do not cover the data section",
1251 ));
1252 }
1253
1254 let bloom_filter = if bloom_offset > 0 {
1256 let bloom_start = bloom_offset;
1257 let bloom_end = if dict_offset != 0 {
1258 dict_offset
1259 } else {
1260 footer_start
1261 };
1262 let bloom_header_end = bloom_start.checked_add(12).ok_or_else(|| {
1264 io::Error::new(io::ErrorKind::InvalidData, "bloom filter range overflow")
1265 })?;
1266 if bloom_header_end > bloom_end {
1267 return Err(io::Error::new(
1268 io::ErrorKind::UnexpectedEof,
1269 "bloom filter header is truncated",
1270 ));
1271 }
1272 let bloom_header = file_handle
1273 .read_bytes_range(bloom_start..bloom_header_end)
1274 .await?;
1275 let num_words = u32::from_le_bytes([
1276 bloom_header[8],
1277 bloom_header[9],
1278 bloom_header[10],
1279 bloom_header[11],
1280 ]) as u64;
1281 let bloom_size = num_words
1282 .checked_mul(8)
1283 .and_then(|bytes| bytes.checked_add(12))
1284 .ok_or_else(|| {
1285 io::Error::new(io::ErrorKind::InvalidData, "bloom filter size overflow")
1286 })?;
1287 let actual_bloom_size = bloom_end - bloom_start;
1288 if bloom_size != actual_bloom_size {
1289 return Err(io::Error::new(
1290 io::ErrorKind::InvalidData,
1291 "bloom filter length is inconsistent",
1292 ));
1293 }
1294 let bloom_data = file_handle.read_bytes_range(bloom_start..bloom_end).await?;
1295 Some(BloomFilter::from_owned_bytes(bloom_data)?)
1296 } else {
1297 None
1298 };
1299
1300 let dictionary = if dict_offset > 0 {
1302 let dict_start = dict_offset;
1303 let dict_header_end = dict_start.checked_add(4).ok_or_else(|| {
1305 io::Error::new(io::ErrorKind::InvalidData, "dictionary range overflow")
1306 })?;
1307 if dict_header_end > footer_start {
1308 return Err(io::Error::new(
1309 io::ErrorKind::UnexpectedEof,
1310 "dictionary header is truncated",
1311 ));
1312 }
1313 let dict_len_bytes = file_handle
1314 .read_bytes_range(dict_start..dict_header_end)
1315 .await?;
1316 let dict_len = u32::from_le_bytes([
1317 dict_len_bytes[0],
1318 dict_len_bytes[1],
1319 dict_len_bytes[2],
1320 dict_len_bytes[3],
1321 ]) as u64;
1322 if dict_len > MAX_SSTABLE_DICTIONARY_BYTES {
1323 return Err(io::Error::new(
1324 io::ErrorKind::InvalidData,
1325 "SSTable dictionary exceeds safety limit",
1326 ));
1327 }
1328 let dict_end = dict_header_end.checked_add(dict_len).ok_or_else(|| {
1329 io::Error::new(io::ErrorKind::InvalidData, "dictionary range overflow")
1330 })?;
1331 if dict_end != footer_start {
1332 return Err(io::Error::new(
1333 io::ErrorKind::InvalidData,
1334 "SSTable dictionary length is inconsistent",
1335 ));
1336 }
1337 let dict_data = file_handle
1338 .read_bytes_range(dict_header_end..dict_end)
1339 .await?;
1340 Some(CompressionDict::from_owned_bytes(dict_data))
1341 } else {
1342 None
1343 };
1344
1345 let data_slice = file_handle.slice(0..data_end_offset);
1347
1348 Ok(Self {
1349 data_slice,
1350 block_index,
1351 num_entries,
1352 cache: RwLock::new(BlockCache::new(cache_blocks)),
1353 bloom_filter,
1354 dictionary,
1355 compression_level,
1356 _phantom: std::marker::PhantomData,
1357 })
1358 }
1359
1360 pub fn num_entries(&self) -> u64 {
1362 self.num_entries
1363 }
1364
1365 pub fn stats(&self) -> SSTableStats {
1367 SSTableStats {
1368 num_blocks: self.block_index.len(),
1369 num_sparse_entries: 0, num_entries: self.num_entries,
1371 has_bloom_filter: self.bloom_filter.is_some(),
1372 has_dictionary: self.dictionary.is_some(),
1373 bloom_filter_size: self
1374 .bloom_filter
1375 .as_ref()
1376 .map(|b| b.size_bytes())
1377 .unwrap_or(0),
1378 dictionary_size: self.dictionary.as_ref().map(|d| d.len()).unwrap_or(0),
1379 }
1380 }
1381
1382 pub fn cached_blocks(&self) -> usize {
1384 self.cache.read().blocks.len()
1385 }
1386
1387 pub fn cached_bytes(&self) -> usize {
1393 self.cache
1394 .read()
1395 .blocks
1396 .values()
1397 .map(|block| block.len())
1398 .sum()
1399 }
1400
1401 pub async fn get(&self, key: &[u8]) -> io::Result<Option<V>> {
1406 log::debug!(
1407 "SSTable::get called, key_len={}, total_blocks={}",
1408 key.len(),
1409 self.block_index.len()
1410 );
1411
1412 if let Some(ref bloom) = self.bloom_filter
1414 && !bloom.may_contain(key)
1415 {
1416 log::debug!("SSTable::get bloom filter negative");
1417 return Ok(None);
1418 }
1419
1420 let block_idx = match self.block_index.locate(key) {
1422 Some(idx) => idx,
1423 None => {
1424 log::debug!("SSTable::get key not found (before first block)");
1425 return Ok(None);
1426 }
1427 };
1428
1429 log::debug!("SSTable::get loading block_idx={}", block_idx);
1430
1431 let block_data = self.load_block(block_idx).await?;
1433 self.search_block(&block_data, key)
1434 }
1435
1436 pub async fn get_batch(&self, keys: &[&[u8]]) -> io::Result<Vec<Option<V>>> {
1442 if keys.is_empty() {
1443 return Ok(Vec::new());
1444 }
1445
1446 let mut key_to_block: Vec<(usize, usize)> = Vec::with_capacity(keys.len());
1448 for (key_idx, key) in keys.iter().enumerate() {
1449 if let Some(ref bloom) = self.bloom_filter
1451 && !bloom.may_contain(key)
1452 {
1453 key_to_block.push((key_idx, usize::MAX)); continue;
1455 }
1456
1457 match self.block_index.locate(key) {
1458 Some(block_idx) => key_to_block.push((key_idx, block_idx)),
1459 None => key_to_block.push((key_idx, usize::MAX)), }
1461 }
1462
1463 let mut blocks_to_load: Vec<usize> = key_to_block
1465 .iter()
1466 .filter(|(_, b)| *b != usize::MAX)
1467 .map(|(_, b)| *b)
1468 .collect();
1469 blocks_to_load.sort_unstable();
1470 blocks_to_load.dedup();
1471
1472 for &block_idx in &blocks_to_load {
1474 let _ = self.load_block(block_idx).await?;
1475 }
1476
1477 let mut results = vec![None; keys.len()];
1479 for (key_idx, block_idx) in key_to_block {
1480 if block_idx == usize::MAX {
1481 continue;
1482 }
1483 let block_data = self.load_block(block_idx).await?; results[key_idx] = self.search_block(&block_data, keys[key_idx])?;
1485 }
1486
1487 Ok(results)
1488 }
1489
1490 pub async fn preload_all_blocks(&self) -> io::Result<()> {
1495 for block_idx in 0..self.block_index.len() {
1496 self.load_block(block_idx).await?;
1497 }
1498 Ok(())
1499 }
1500
1501 pub async fn prefetch_all_data_bulk(&self) -> io::Result<()> {
1507 let num_blocks = self.block_index.len();
1508 if num_blocks == 0 {
1509 return Ok(());
1510 }
1511
1512 let mut max_end: u64 = 0;
1514 for i in 0..num_blocks {
1515 if let Some(addr) = self.block_index.get_addr(i) {
1516 let end = addr.offset.checked_add(addr.length as u64).ok_or_else(|| {
1517 io::Error::new(io::ErrorKind::InvalidData, "SSTable block range overflow")
1518 })?;
1519 max_end = max_end.max(end);
1520 }
1521 }
1522
1523 let all_data = self.data_slice.read_bytes_range(0..max_end).await?;
1525 let buf = all_data.as_slice();
1526
1527 let mut cache = self.cache.write();
1529 cache.max_blocks = cache.max_blocks.max(num_blocks);
1530 for i in 0..num_blocks {
1531 let addr = self.block_index.get_addr(i).unwrap();
1532 if cache.get(addr.offset).is_some() {
1533 continue;
1534 }
1535 let start = usize::try_from(addr.offset).map_err(|_| {
1536 io::Error::new(io::ErrorKind::InvalidData, "SSTable block offset too large")
1537 })?;
1538 let end = addr
1539 .offset
1540 .checked_add(addr.length as u64)
1541 .and_then(|end| usize::try_from(end).ok())
1542 .ok_or_else(|| {
1543 io::Error::new(io::ErrorKind::InvalidData, "SSTable block range overflow")
1544 })?;
1545 let compressed = buf.get(start..end).ok_or_else(|| {
1546 io::Error::new(io::ErrorKind::UnexpectedEof, "SSTable block is truncated")
1547 })?;
1548 let decompressed = if let Some(ref dict) = self.dictionary {
1549 crate::compression::decompress_with_dict_limited(
1550 compressed,
1551 dict,
1552 MAX_SSTABLE_BLOCK_BYTES,
1553 )?
1554 } else {
1555 crate::compression::decompress_limited(compressed, MAX_SSTABLE_BLOCK_BYTES)?
1556 };
1557 cache.insert(addr.offset, Arc::from(decompressed));
1558 }
1559
1560 Ok(())
1561 }
1562
1563 async fn load_block(&self, block_idx: usize) -> io::Result<Arc<[u8]>> {
1566 let addr = self.block_index.get_addr(block_idx).ok_or_else(|| {
1567 io::Error::new(io::ErrorKind::InvalidInput, "Block index out of range")
1568 })?;
1569
1570 {
1572 if let Some(block) = self.cache.read().peek(addr.offset) {
1573 return Ok(block);
1574 }
1575 }
1576
1577 log::debug!(
1578 "SSTable::load_block idx={} CACHE MISS, reading bytes [{}-{}]",
1579 block_idx,
1580 addr.offset,
1581 addr.offset + addr.length as u64
1582 );
1583
1584 let range = addr.byte_range();
1586 let compressed = self.data_slice.read_bytes_range(range).await?;
1587
1588 let decompressed = if let Some(ref dict) = self.dictionary {
1590 crate::compression::decompress_with_dict_limited(
1591 compressed.as_slice(),
1592 dict,
1593 MAX_SSTABLE_BLOCK_BYTES,
1594 )?
1595 } else {
1596 crate::compression::decompress_limited(compressed.as_slice(), MAX_SSTABLE_BLOCK_BYTES)?
1597 };
1598
1599 let block: Arc<[u8]> = Arc::from(decompressed);
1600
1601 {
1603 let mut cache = self.cache.write();
1604 cache.insert(addr.offset, Arc::clone(&block));
1605 }
1606
1607 Ok(block)
1608 }
1609
1610 #[cfg(feature = "sync")]
1612 fn load_block_sync(&self, block_idx: usize) -> io::Result<Arc<[u8]>> {
1613 let addr = self.block_index.get_addr(block_idx).ok_or_else(|| {
1614 io::Error::new(io::ErrorKind::InvalidInput, "Block index out of range")
1615 })?;
1616
1617 {
1619 if let Some(block) = self.cache.read().peek(addr.offset) {
1620 return Ok(block);
1621 }
1622 }
1623
1624 let range = addr.byte_range();
1626 let compressed = self.data_slice.read_bytes_range_sync(range)?;
1627
1628 let decompressed = if let Some(ref dict) = self.dictionary {
1630 crate::compression::decompress_with_dict_limited(
1631 compressed.as_slice(),
1632 dict,
1633 MAX_SSTABLE_BLOCK_BYTES,
1634 )?
1635 } else {
1636 crate::compression::decompress_limited(compressed.as_slice(), MAX_SSTABLE_BLOCK_BYTES)?
1637 };
1638
1639 let block: Arc<[u8]> = Arc::from(decompressed);
1640
1641 {
1643 let mut cache = self.cache.write();
1644 cache.insert(addr.offset, Arc::clone(&block));
1645 }
1646
1647 Ok(block)
1648 }
1649
1650 #[cfg(feature = "sync")]
1652 pub fn get_sync(&self, key: &[u8]) -> io::Result<Option<V>> {
1653 if let Some(ref bloom) = self.bloom_filter
1655 && !bloom.may_contain(key)
1656 {
1657 return Ok(None);
1658 }
1659
1660 let block_idx = match self.block_index.locate(key) {
1662 Some(idx) => idx,
1663 None => {
1664 return Ok(None);
1665 }
1666 };
1667
1668 let block_data = self.load_block_sync(block_idx)?;
1669 self.search_block(&block_data, key)
1670 }
1671
1672 fn search_block(&self, block_data: &[u8], target_key: &[u8]) -> io::Result<Option<V>> {
1673 let mut reader = block_data;
1674 let mut current_key = Vec::new();
1675
1676 while !reader.is_empty() {
1677 let common_prefix_len = read_vint(&mut reader)? as usize;
1678 let suffix_len = read_vint(&mut reader)? as usize;
1679
1680 if suffix_len > reader.len() {
1681 return Err(io::Error::new(
1682 io::ErrorKind::UnexpectedEof,
1683 "SSTable block suffix truncated",
1684 ));
1685 }
1686 current_key.truncate(common_prefix_len);
1687 current_key.extend_from_slice(&reader[..suffix_len]);
1688 reader = &reader[suffix_len..];
1689
1690 let value = V::deserialize(&mut reader)?;
1691
1692 match current_key.as_slice().cmp(target_key) {
1693 std::cmp::Ordering::Equal => return Ok(Some(value)),
1694 std::cmp::Ordering::Greater => return Ok(None),
1695 std::cmp::Ordering::Less => continue,
1696 }
1697 }
1698
1699 Ok(None)
1700 }
1701
1702 pub async fn prefetch_range(&self, start_key: &[u8], end_key: &[u8]) -> io::Result<()> {
1704 let start_block = self.block_index.locate(start_key).unwrap_or(0);
1705 let end_block = self
1706 .block_index
1707 .locate(end_key)
1708 .unwrap_or(self.block_index.len().saturating_sub(1));
1709
1710 for block_idx in start_block..=end_block.min(self.block_index.len().saturating_sub(1)) {
1711 let _ = self.load_block(block_idx).await?;
1712 }
1713
1714 Ok(())
1715 }
1716
1717 pub fn iter(&self) -> AsyncSSTableIterator<'_, V> {
1719 AsyncSSTableIterator::new(self)
1720 }
1721
1722 pub async fn all_entries(&self) -> io::Result<Vec<(Vec<u8>, V)>> {
1724 let mut results = Vec::new();
1725
1726 for block_idx in 0..self.block_index.len() {
1727 let block_data = self.load_block(block_idx).await?;
1728 let mut reader = &block_data[..];
1729 let mut current_key = Vec::new();
1730
1731 while !reader.is_empty() {
1732 let common_prefix_len = read_vint(&mut reader)? as usize;
1733 let suffix_len = read_vint(&mut reader)? as usize;
1734
1735 if suffix_len > reader.len() {
1736 return Err(io::Error::new(
1737 io::ErrorKind::UnexpectedEof,
1738 "SSTable block suffix truncated",
1739 ));
1740 }
1741 current_key.truncate(common_prefix_len);
1742 current_key.extend_from_slice(&reader[..suffix_len]);
1743 reader = &reader[suffix_len..];
1744
1745 let value = V::deserialize(&mut reader)?;
1746 results.push((current_key.clone(), value));
1747 }
1748 }
1749
1750 Ok(results)
1751 }
1752
1753 pub async fn prefix_scan(&self, prefix: &[u8]) -> io::Result<Vec<(Vec<u8>, V)>> {
1759 let (results, _) = self.prefix_scan_limited(prefix, usize::MAX).await?;
1760 Ok(results)
1761 }
1762
1763 pub async fn prefix_scan_limited(
1766 &self,
1767 prefix: &[u8],
1768 max_results: usize,
1769 ) -> io::Result<PrefixScanResult<V>> {
1770 if self.block_index.is_empty() || prefix.is_empty() {
1771 return Ok((Vec::new(), false));
1772 }
1773
1774 let start_block = match self.block_index.locate(prefix) {
1775 Some(idx) => idx,
1776 None => return Ok((Vec::new(), false)),
1777 };
1778
1779 let mut results = Vec::new();
1780
1781 for block_idx in start_block..self.block_index.len() {
1782 let block_data = self.load_block(block_idx).await?;
1783 let mut reader = &block_data[..];
1784 let mut current_key = Vec::new();
1785
1786 while !reader.is_empty() {
1787 let common_prefix_len = read_vint(&mut reader)? as usize;
1788 let suffix_len = read_vint(&mut reader)? as usize;
1789
1790 if suffix_len > reader.len() {
1791 return Err(io::Error::new(
1792 io::ErrorKind::UnexpectedEof,
1793 "SSTable block suffix truncated",
1794 ));
1795 }
1796 current_key.truncate(common_prefix_len);
1797 current_key.extend_from_slice(&reader[..suffix_len]);
1798 reader = &reader[suffix_len..];
1799
1800 let value = V::deserialize(&mut reader)?;
1801
1802 if current_key.starts_with(prefix) {
1803 if results.len() >= max_results {
1804 return Ok((results, true));
1805 }
1806 results.push((current_key.clone(), value));
1807 } else if current_key.as_slice() > prefix {
1808 return Ok((results, false));
1810 }
1811 }
1812 }
1813
1814 Ok((results, false))
1815 }
1816
1817 #[cfg(feature = "sync")]
1819 pub fn prefix_scan_sync(&self, prefix: &[u8]) -> io::Result<Vec<(Vec<u8>, V)>> {
1820 let (results, _) = self.prefix_scan_limited_sync(prefix, usize::MAX)?;
1821 Ok(results)
1822 }
1823
1824 #[cfg(feature = "sync")]
1826 pub fn prefix_scan_limited_sync(
1827 &self,
1828 prefix: &[u8],
1829 max_results: usize,
1830 ) -> io::Result<PrefixScanResult<V>> {
1831 if self.block_index.is_empty() || prefix.is_empty() {
1832 return Ok((Vec::new(), false));
1833 }
1834
1835 let start_block = match self.block_index.locate(prefix) {
1836 Some(idx) => idx,
1837 None => return Ok((Vec::new(), false)),
1838 };
1839
1840 let mut results = Vec::new();
1841
1842 for block_idx in start_block..self.block_index.len() {
1843 let block_data = self.load_block_sync(block_idx)?;
1844 let mut reader = &block_data[..];
1845 let mut current_key = Vec::new();
1846
1847 while !reader.is_empty() {
1848 let common_prefix_len = read_vint(&mut reader)? as usize;
1849 let suffix_len = read_vint(&mut reader)? as usize;
1850
1851 if suffix_len > reader.len() {
1852 return Err(io::Error::new(
1853 io::ErrorKind::UnexpectedEof,
1854 "SSTable block suffix truncated",
1855 ));
1856 }
1857 current_key.truncate(common_prefix_len);
1858 current_key.extend_from_slice(&reader[..suffix_len]);
1859 reader = &reader[suffix_len..];
1860
1861 let value = V::deserialize(&mut reader)?;
1862
1863 if current_key.starts_with(prefix) {
1864 if results.len() >= max_results {
1865 return Ok((results, true));
1866 }
1867 results.push((current_key.clone(), value));
1868 } else if current_key.as_slice() > prefix {
1869 return Ok((results, false));
1870 }
1871 }
1872 }
1873
1874 Ok((results, false))
1875 }
1876}
1877
1878pub struct AsyncSSTableIterator<'a, V: SSTableValue> {
1880 reader: &'a AsyncSSTableReader<V>,
1881 current_block: usize,
1882 block_data: Option<Arc<[u8]>>,
1883 block_offset: usize,
1884 current_key: Vec<u8>,
1885 finished: bool,
1886}
1887
1888impl<'a, V: SSTableValue> AsyncSSTableIterator<'a, V> {
1889 fn new(reader: &'a AsyncSSTableReader<V>) -> Self {
1890 Self {
1891 reader,
1892 current_block: 0,
1893 block_data: None,
1894 block_offset: 0,
1895 current_key: Vec::new(),
1896 finished: reader.block_index.is_empty(),
1897 }
1898 }
1899
1900 async fn load_next_block(&mut self) -> io::Result<bool> {
1901 if self.current_block >= self.reader.block_index.len() {
1902 self.finished = true;
1903 return Ok(false);
1904 }
1905
1906 self.block_data = Some(self.reader.load_block(self.current_block).await?);
1907 self.block_offset = 0;
1908 self.current_key.clear();
1909 self.current_block += 1;
1910 Ok(true)
1911 }
1912
1913 pub async fn next(&mut self) -> io::Result<Option<(Vec<u8>, V)>> {
1915 if self.finished {
1916 return Ok(None);
1917 }
1918
1919 if self.block_data.is_none() && !self.load_next_block().await? {
1920 return Ok(None);
1921 }
1922
1923 loop {
1924 let block = self.block_data.as_ref().unwrap();
1925 if self.block_offset >= block.len() {
1926 if !self.load_next_block().await? {
1927 return Ok(None);
1928 }
1929 continue;
1930 }
1931
1932 let mut reader = &block[self.block_offset..];
1933 let start_len = reader.len();
1934
1935 let common_prefix_len = read_vint(&mut reader)? as usize;
1936 let suffix_len = read_vint(&mut reader)? as usize;
1937
1938 if suffix_len > reader.len() {
1939 return Err(io::Error::new(
1940 io::ErrorKind::UnexpectedEof,
1941 "SSTable block suffix truncated",
1942 ));
1943 }
1944 self.current_key.truncate(common_prefix_len);
1945 self.current_key.extend_from_slice(&reader[..suffix_len]);
1946 reader = &reader[suffix_len..];
1947
1948 let value = V::deserialize(&mut reader)?;
1949
1950 self.block_offset += start_len - reader.len();
1951
1952 return Ok(Some((self.current_key.clone(), value)));
1953 }
1954 }
1955}
1956
1957#[cfg(test)]
1958mod tests {
1959 use super::*;
1960
1961 #[test]
1962 fn test_bloom_filter_basic() {
1963 let mut bloom = BloomFilter::new(100, 10);
1964
1965 bloom.insert(b"hello");
1966 bloom.insert(b"world");
1967 bloom.insert(b"test");
1968
1969 assert!(bloom.may_contain(b"hello"));
1970 assert!(bloom.may_contain(b"world"));
1971 assert!(bloom.may_contain(b"test"));
1972
1973 assert!(!bloom.may_contain(b"notfound"));
1975 assert!(!bloom.may_contain(b"missing"));
1976 }
1977
1978 #[test]
1979 fn test_bloom_filter_serialization() {
1980 let mut bloom = BloomFilter::new(100, 10);
1981 bloom.insert(b"key1");
1982 bloom.insert(b"key2");
1983
1984 let bytes = bloom.to_bytes();
1985 let restored = BloomFilter::from_owned_bytes(OwnedBytes::new(bytes)).unwrap();
1986
1987 assert!(restored.may_contain(b"key1"));
1988 assert!(restored.may_contain(b"key2"));
1989 assert!(!restored.may_contain(b"key3"));
1990 }
1991
1992 #[test]
1993 fn test_bloom_filter_false_positive_rate() {
1994 let num_keys = 10000;
1995 let mut bloom = BloomFilter::new(num_keys, BLOOM_BITS_PER_KEY);
1996
1997 for i in 0..num_keys {
1999 let key = format!("key_{}", i);
2000 bloom.insert(key.as_bytes());
2001 }
2002
2003 for i in 0..num_keys {
2005 let key = format!("key_{}", i);
2006 assert!(bloom.may_contain(key.as_bytes()));
2007 }
2008
2009 let mut false_positives = 0;
2011 let test_count = 10000;
2012 for i in 0..test_count {
2013 let key = format!("nonexistent_{}", i);
2014 if bloom.may_contain(key.as_bytes()) {
2015 false_positives += 1;
2016 }
2017 }
2018
2019 let fp_rate = false_positives as f64 / test_count as f64;
2022 assert!(
2023 fp_rate < 0.03,
2024 "False positive rate {} is too high",
2025 fp_rate
2026 );
2027 }
2028
2029 #[test]
2030 fn test_sstable_writer_config() {
2031 use crate::structures::IndexOptimization;
2032
2033 let config = SSTableWriterConfig::default();
2035 assert_eq!(config.compression_level.0, 9); assert!(config.use_bloom_filter); assert!(!config.use_dictionary);
2038
2039 let adaptive = SSTableWriterConfig::from_optimization(IndexOptimization::Adaptive);
2041 assert_eq!(adaptive.compression_level.0, 9);
2042 assert!(adaptive.use_bloom_filter);
2043 assert!(!adaptive.use_dictionary);
2044
2045 let size = SSTableWriterConfig::from_optimization(IndexOptimization::SizeOptimized);
2047 assert_eq!(size.compression_level.0, 22); assert!(size.use_bloom_filter);
2049 assert!(size.use_dictionary);
2050
2051 let perf = SSTableWriterConfig::from_optimization(IndexOptimization::PerformanceOptimized);
2053 assert_eq!(perf.compression_level.0, 1); assert!(perf.use_bloom_filter); assert!(!perf.use_dictionary);
2056
2057 let fast = SSTableWriterConfig::fast();
2059 assert_eq!(fast.compression_level.0, 1);
2060
2061 let max = SSTableWriterConfig::max_compression();
2062 assert_eq!(max.compression_level.0, 22);
2063 }
2064
2065 #[test]
2066 fn test_vint_roundtrip() {
2067 let test_values = [0u64, 1, 127, 128, 255, 256, 16383, 16384, u64::MAX];
2068
2069 for &val in &test_values {
2070 let mut buf = Vec::new();
2071 write_vint(&mut buf, val).unwrap();
2072 let mut reader = buf.as_slice();
2073 let decoded = read_vint(&mut reader).unwrap();
2074 assert_eq!(val, decoded, "Failed for value {}", val);
2075 }
2076 }
2077
2078 #[test]
2079 fn test_common_prefix_len() {
2080 assert_eq!(common_prefix_len(b"hello", b"hello"), 5);
2081 assert_eq!(common_prefix_len(b"hello", b"help"), 3);
2082 assert_eq!(common_prefix_len(b"hello", b"world"), 0);
2083 assert_eq!(common_prefix_len(b"", b"hello"), 0);
2084 assert_eq!(common_prefix_len(b"hello", b""), 0);
2085 }
2086}