//! Single-name in-memory file lifetime; immutable reader guards own backing pins. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidName, InvalidBlocks, NotFound, ReaderOverflow, BufferTooSmall } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Ownership { pub linked: bool, pub readers: u16, pub blocks: u64, } pub struct File { name: [u8; 16], name_length: usize, linked: core::cell::Cell, readers: core::cell::Cell, blocks: core::cell::Cell, bytes: core::cell::RefCell<[u8; 1300]>, } pub struct Reader<'a> { file: &'a File } impl File { pub fn new(name: &[u8], blocks: [u8; 3], bytes: [u8; 1300]) -> Result { if name.is_empty() || name.len() > 16 || name == b"." || name == b".." || name.contains(&b'/') || name.contains(&0) || core::str::from_utf8(name).is_err() { return Err(Error::InvalidName); } let mut bits = 0u64; for block in blocks { if !(2..64).contains(&block) || bits & (1u64 << block) != 0 { return Err(Error::InvalidBlocks); } bits |= 1u64 << block; } let mut stored = [0; 16]; stored[..name.len()].copy_from_slice(name); Ok(Self {name: stored, name_length: name.len(), linked: core::cell::Cell::new(true), readers: core::cell::Cell::new(0), blocks: core::cell::Cell::new(bits), bytes: core::cell::RefCell::new(bytes)}) } pub fn lookup(&self, name: &[u8]) -> bool { self.linked.get() && name == &self.name[..self.name_length] } pub fn ownership(&self) -> Ownership { Ownership {linked: self.linked.get(), readers: self.readers.get(), blocks: self.blocks.get()} } pub fn open(&self, name: &[u8]) -> Result, Error> { if !self.lookup(name) { return Err(Error::NotFound); } let next = self.readers.get().checked_add(1).ok_or(Error::ReaderOverflow)?; self.readers.set(next); Ok(Reader {file: self}) } pub fn unlink(&self, name: &[u8]) -> Result<(), Error> { if !self.lookup(name) { return Err(Error::NotFound); } self.linked.set(false); if self.readers.get() == 0 { self.retire(); } Ok(()) } fn retire(&self) { self.bytes.borrow_mut().fill(0); self.blocks.set(0); } } impl Reader<'_> { pub fn read_all(&self, output: &mut [u8]) -> Result { if output.len() < 1300 { return Err(Error::BufferTooSmall); } output[..1300].copy_from_slice(&*self.file.bytes.borrow()); Ok(1300) } } impl Drop for Reader<'_> { fn drop(&mut self) { let next = self.file.readers.get() - 1; self.file.readers.set(next); if next == 0 && !self.file.linked.get() { self.file.retire(); } } }