use super::BlockStream;
use crate::fs::{Fs, FsOpenOptions, StdFs};
use crate::table::util::TimedFile;
use alloc::sync::Arc;
use std::io::Read;
fn pattern(len: usize) -> Vec<u8> {
(0..=250u8).cycle().take(len).collect()
}
fn stream_over(bytes: &[u8], readahead: usize) -> crate::Result<(tempfile::TempDir, BlockStream)> {
let dir = tempfile::tempdir()?;
let path = dir.path().join("table");
std::fs::write(&path, bytes)?;
let fs: Arc<dyn Fs> = Arc::new(StdFs);
let file = fs.open(&path, &FsOpenOptions::new().read(true))?;
let stream = BlockStream::new(TimedFile::new(file), 0, bytes.len() as u64, readahead);
Ok((dir, stream))
}
#[test]
fn views_share_their_chunk_and_a_straddling_block_starts_the_next() -> crate::Result<()> {
let bytes = pattern(1_000);
let (_dir, mut stream) = stream_over(&bytes, 300)?;
let first = stream.view(100)?;
let second = stream.view(100)?;
assert_eq!(&*first, &bytes[..100]);
assert_eq!(&*second, &bytes[100..200]);
assert_eq!(
second.as_ptr(),
first.as_ptr().wrapping_add(100),
"two blocks of one chunk are views of it, not copies"
);
let straddling = stream.view(150)?;
assert_eq!(&*straddling, &bytes[200..350]);
assert_eq!(
stream.chunk.len(),
300,
"the next chunk, read at the readahead"
);
let long = stream.view(500)?;
assert_eq!(&*long, &bytes[350..850]);
let mut rest = vec![0; 150];
stream.read_exact(&mut rest)?;
assert_eq!(rest, &bytes[850..]);
assert!(stream.view(1).is_err(), "nothing is left to read");
Ok(())
}
#[test]
fn a_chunk_is_no_longer_than_the_file() -> crate::Result<()> {
let bytes = pattern(50);
let (_dir, mut stream) = stream_over(&bytes, 1_000)?;
assert_eq!(&*stream.view(10)?, &bytes[..10]);
assert_eq!(stream.chunk.len(), 50);
assert_eq!(&*stream.view(40)?, &bytes[10..]);
Ok(())
}
#[test]
fn small_reads_cross_chunks() -> crate::Result<()> {
let bytes = pattern(1_000);
let (_dir, mut stream) = stream_over(&bytes, 64)?;
let mut read = Vec::new();
let mut piece = [0u8; 7];
while read.len() + piece.len() <= bytes.len() {
stream.read_exact(&mut piece)?;
read.extend_from_slice(&piece);
}
assert_eq!(read, &bytes[..read.len()]);
Ok(())
}
fn noise(seed: u64, len: usize) -> Vec<u8> {
let mut state = seed;
core::iter::repeat_with(|| {
state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
(z ^ (z >> 31)).to_le_bytes()
})
.flatten()
.take(len)
.collect()
}
struct EveryRead;
impl crate::table::util::ReadPacer for EveryRead {
fn active(&self) -> bool {
true
}
fn pace(&self, _offset: u64, _len: u64) {}
}
#[test]
fn a_scan_returns_what_an_indexed_read_does() -> crate::Result<()> {
use crate::config::{BlockSizePolicy, CompressionPolicy};
use crate::{AbstractTree, AnyTree, Config, SequenceNumberCounter};
const ENTRIES: u32 = 16_000;
let codecs = [
crate::CompressionType::None,
#[cfg(feature = "lz4")]
crate::CompressionType::Lz4,
];
for codec in codecs {
let folder = tempfile::tempdir()?;
let AnyTree::Standard(tree) = Config::new(
folder.path(),
SequenceNumberCounter::default(),
SequenceNumberCounter::default(),
)
.data_block_compression_policy(CompressionPolicy::all(codec))
.data_block_size_policy(BlockSizePolicy::all(3_001))
.table_target_size(64 * 1_024 * 1_024)
.open()?
else {
panic!("expected a standard tree");
};
for i in 0..ENTRIES {
let value = noise(u64::from(i), 300);
tree.insert(format!("key{i:06}"), value, u64::from(i));
}
tree.flush_active_memtable(0)?;
let version = tree.current_version();
let tables: Vec<_> = version.iter_tables().cloned().collect();
assert_eq!(tables.len(), 1, "one table");
let table = &tables[0];
assert!(
table.file_size() > 2 * super::SCANNER_READAHEAD_BYTES as u64,
"the table spans several chunks: {} bytes",
table.file_size()
);
let entries = |iter: &mut dyn Iterator<Item = crate::Result<crate::InternalValue>>| {
iter.map(|entry| entry.map(|entry| (entry.key, entry.value)))
.collect::<crate::Result<Vec<_>>>()
};
let indexed = entries(&mut table.iter())?;
assert_eq!(indexed.len(), ENTRIES as usize);
assert_eq!(entries(&mut table.scan()?)?, indexed, "{codec:?}");
let pacer: crate::table::util::Pacer = Arc::new(EveryRead);
assert_eq!(
entries(&mut table.scan_paced(Some(&pacer))?)?,
indexed,
"{codec:?}, paced"
);
}
Ok(())
}