mod allocation;
mod file_mapping;
mod frames;
mod header;
use memmap2::MmapMut;
use std::error::Error;
use std::fs::File;
pub struct Backend {
size: usize,
mapped_file: MmapMut,
file: File,
header: header::Header,
}
impl Backend {
pub fn new(file: File) -> Result<Self, Box<dyn Error>> {
let (size, mut mapped_file) = Self::open_file(&file)?;
let header = Self::initialize_header(&mut mapped_file)?;
let backend = Self {
header,
file,
mapped_file,
size,
};
Ok(backend)
}
pub fn create(&mut self, bytes: &[u8]) -> Result<usize, Box<dyn Error>> {
let start = self.next_free_frame()?;
self.write_bytes_starting_at(start, bytes)?;
self.flush()?;
Ok(start)
}
fn write_bytes_starting_at(
&mut self,
start: usize,
bytes: &[u8],
) -> Result<(), Box<dyn Error>> {
let chunk_size = frames::Frame::capacity();
let mut last_frame_position: Option<usize> = None;
for (index, byte_chunk) in bytes.chunks(chunk_size).enumerate() {
let position = if index == 0 {
self.unlink_free_frame(start)?
} else {
self.next_free_frame()?
};
self.create_frame(position)?;
self.write_frame_body(position, byte_chunk)?;
if let Some(last_position) = last_frame_position {
let mut last_frame = self.read_frame(last_position)?;
last_frame.next = position;
self.update_frame(last_frame)?;
}
last_frame_position = Some(position);
}
Ok(())
}
pub fn read(&self, position: usize) -> Result<Vec<u8>, Box<dyn Error>> {
let mut bytes: Vec<u8> = vec![];
let mut cursor: usize = position;
while cursor != 0 {
let frame = self.read_frame(cursor)?;
if frame.deleted == false {
let body = self.read_frame_body(cursor)?;
bytes.extend_from_slice(body);
}
cursor = frame.next;
}
Ok(bytes)
}
pub fn update(&mut self, position: usize, bytes: &[u8]) -> Result<(), Box<dyn Error>> {
self.delete(position)?;
self.write_bytes_starting_at(position, bytes)?;
self.flush()?;
Ok(())
}
pub fn delete(&mut self, position: usize) -> Result<(), Box<dyn Error>> {
let mut cursor: usize = position;
while cursor != 0 {
let mut frame = self.read_frame(cursor)?;
let current = cursor;
cursor = frame.next;
frame.deleted = true;
frame.next = self.header.first_free_frame;
self.header.first_free_frame = current;
self.update_frame(frame)?;
}
self.header.update(&mut self.mapped_file)?;
self.flush()?;
Ok(())
}
pub fn offset() -> usize {
header::Header::size()
}
pub fn block_size() -> usize {
frames::Frame::total_size()
}
pub fn is_empty(&self) -> bool {
self.header.frame_count == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create() {
let file = tempfile::tempfile().expect("could not create tempfile");
let mut backend = Backend::new(file).expect("could not create mmap");
let position = backend.create(b"hello").expect("could not create");
assert_eq!(position, 24);
let data = backend.read(position).expect("could not read");
assert_eq!(data, b"hello");
let long_data = (0..1025).map(|_| 1 as u8).collect::<Vec<u8>>();
let position = backend.create(&long_data).expect("could not create");
assert_eq!(position, 24 + 1024);
let long_data = backend.read(position).expect("could not read");
assert_eq!(long_data.len(), 1025);
}
#[test]
fn update() {
let file = tempfile::tempfile().expect("could not create tempfile");
let mut backend = Backend::new(file).expect("could not create mmap");
let long_data = (0..1025).map(|_| 1 as u8).collect::<Vec<u8>>();
let position = backend.create(&long_data).expect("could not create");
assert_eq!(position, 24);
let long_data = backend.read(position).expect("could not read");
assert_eq!(long_data.len(), 1025);
let data = (0..10).map(|_| 1 as u8).collect::<Vec<u8>>();
backend.update(position, &data).expect("could not create");
assert_eq!(position, 24);
let data = backend.read(position).expect("could not read");
assert_eq!(data.len(), 10);
let data = backend.read(position + 1024).expect("could not read");
assert_eq!(data.len(), 0);
}
}