//! Per-handle offsets with transactional short-read and seek updates. use crate::os_vfs_open::Handle; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { NoSpace, UnknownHandle, InvalidCompletion, NegativeSeek, Overflow, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Whence { Start, Current, End } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Cursor { pub handle_id: u64, pub offset: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CursorTable { cursors: [Option; 2], } impl CursorTable { pub const fn new() -> Self { Self { cursors: [None; 2] } } pub fn attach(&mut self, handle: Handle) -> Result<(), Error> { if self.cursors.iter().any(|cursor| cursor.map(|value| value.handle_id) == Some(handle.id)) { return Err(Error::NoSpace); } let slot = self.cursors.iter().position(Option::is_none).ok_or(Error::NoSpace)?; self.cursors[slot] = Some(Cursor { handle_id: handle.id, offset: 0 }); Ok(()) } pub fn offset(&self, handle_id: u64) -> Result { self.cursors.iter().flatten() .find(|cursor| cursor.handle_id == handle_id) .map(|cursor| cursor.offset).ok_or(Error::UnknownHandle) } pub fn read_complete(&mut self, handle_id: u64, requested: u64, actual: u64) -> Result { if actual > requested { return Err(Error::InvalidCompletion); } let cursor = self.cursors.iter_mut().flatten() .find(|cursor| cursor.handle_id == handle_id) .ok_or(Error::UnknownHandle)?; let next = cursor.offset.checked_add(actual).ok_or(Error::Overflow)?; cursor.offset = next; Ok(next) } pub fn seek(&mut self, handle_id: u64, whence: Whence, delta: i64, file_len: u64) -> Result { let cursor = self.cursors.iter_mut().flatten() .find(|cursor| cursor.handle_id == handle_id) .ok_or(Error::UnknownHandle)?; let base = match whence { Whence::Start => 0, Whence::Current => cursor.offset, Whence::End => file_len, }; let next = i128::from(base) + i128::from(delta); if next < 0 { return Err(Error::NegativeSeek); } let next = next as u64; cursor.offset = next; Ok(next) } }