use crate::{Appender, Error, SharedMmap};
use std::{io::Write, path::PathBuf};
pub(crate) struct FlatFile {
inner: Appender,
}
impl FlatFile {
pub fn new(path: Option<PathBuf>) -> Result<Self, Error> {
Appender::new(path).map(|inner| FlatFile { inner })
}
pub fn append(&self, records: &[&[u8]]) -> Result<(), Error> {
if records.is_empty() {
return Ok(());
}
let size_inc: usize = records
.iter()
.map(|record| {
assert!(!record.is_empty(), "empty records are not supported");
record.len()
})
.sum();
self.inner.append(size_inc, move |mut mmap| {
for record in records {
mmap.write_all(record).unwrap();
}
})
}
pub fn get_record_at_offset(&self, offset: usize, length: usize) -> Option<SharedMmap> {
self.inner.get_data(offset, move |mmap| {
if mmap.len() < length {
return None;
}
Some(mmap.slice(..length))
})
}
pub fn len(&self) -> usize {
self.inner.size()
}
}
#[cfg(test)]
mod tests {
use super::FlatFile;
#[quickcheck]
fn test_read_write(records: Vec<Vec<u8>>) {
if records.is_empty() {
return;
}
let tmp = tempfile::NamedTempFile::new().unwrap();
let raw_records: Vec<_> = records
.iter()
.filter(|x| !x.is_empty())
.map(|x| x.as_ref())
.collect();
let flatfile = FlatFile::new(Some(tmp.path().to_path_buf())).unwrap();
flatfile.append(&raw_records).unwrap();
let mut offset = 0;
for record in raw_records.iter() {
let drive_record = flatfile.get_record_at_offset(offset, record.len()).unwrap();
assert_eq!(*record, drive_record.as_ref());
offset += drive_record.len();
}
}
}