use std::sync::{Arc, Mutex};
const MAX_PER_TEMPFILE: u64 = 1024 * 1024 * 10;
struct Writer {
writer: std::fs::File,
read_clone: Arc<tempfile::NamedTempFile>,
}
type WriterPool = Arc<Mutex<std::collections::VecDeque<Writer>>>;
#[derive(Clone)]
pub struct StoreEntryRef {
read_clone: Arc<tempfile::NamedTempFile>,
offset: u64,
length: usize,
}
impl StoreEntryRef {
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.length
}
pub fn read(&self, buf: &mut Vec<u8>) -> std::io::Result<()> {
use std::io::{Read, Seek};
let mut reader = self.read_clone.reopen()?;
reader.seek(std::io::SeekFrom::Start(self.offset))?;
buf.reserve(self.length);
unsafe {
let offset = buf.len();
buf.set_len(offset + self.length);
if let Err(err) =
reader.read_exact(&mut buf[offset..offset + self.length])
{
buf.set_len(offset);
return Err(err);
}
}
Ok(())
}
pub fn parse(&self) -> std::io::Result<crate::ParsedEntry> {
let mut tmp = Vec::with_capacity(self.length);
self.read(&mut tmp)?;
crate::ParsedEntry::try_from_slice(&tmp)
}
}
pub struct Store {
writer_pool: WriterPool,
}
impl Default for Store {
fn default() -> Self {
Self {
writer_pool: Arc::new(
Mutex::new(std::collections::VecDeque::new()),
),
}
}
}
impl Store {
pub fn write(&self, content: &[u8]) -> std::io::Result<StoreEntryRef> {
use std::io::{Seek, Write};
let mut writer = {
match self.writer_pool.lock().unwrap().pop_front() {
Some(writer) => writer,
None => {
let read_clone = Arc::new(tempfile::NamedTempFile::new()?);
let writer = read_clone.reopen()?;
Writer { writer, read_clone }
}
}
};
let read_clone = writer.read_clone.clone();
let offset = writer.writer.stream_position()?;
let length = content.len();
writer.writer.write_all(content)?;
writer.writer.sync_data()?;
if offset + (length as u64) < MAX_PER_TEMPFILE {
self.writer_pool.lock().unwrap().push_back(writer);
}
Ok(StoreEntryRef {
read_clone,
offset,
length,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn happy_sanity() {
let s = Store::default();
let hello = s.write(b"Hello ").unwrap();
let world = s.write(b"world!").unwrap();
let mut buf = Vec::new();
hello.read(&mut buf).unwrap();
world.read(&mut buf).unwrap();
assert_eq!(b"Hello world!", buf.as_slice());
}
#[test]
fn happy_multi_thread_sanity() {
const COUNT: usize = 10;
let mut all = Vec::with_capacity(COUNT);
let s = Arc::new(Store::default());
let b = Arc::new(std::sync::Barrier::new(COUNT));
let (send, recv) = std::sync::mpsc::channel();
for i in 0..COUNT {
let send = send.clone();
let s = s.clone();
let b = b.clone();
all.push(std::thread::spawn(move || {
b.wait();
let wrote = format!("index:{i}");
let r = s.write(wrote.as_bytes()).unwrap();
send.send((wrote, r)).unwrap();
}));
}
for _ in 0..COUNT {
let (wrote, r) = recv.recv().unwrap();
let mut read = Vec::new();
r.read(&mut read).unwrap();
assert_eq!(wrote.as_bytes(), read);
}
}
}