synvael/crates/shared/src/save/cursor.rs

43 lines
1.4 KiB
Rust

// SPDX-License-Identifier: AGPL-3.0-only
//! A bounds-checked, forward-only cursor over untrusted save-format bytes.
use super::error::SaveError;
/// A forward-only reader over a byte slice that bounds-checks every read.
pub(crate) struct Reader<'a> {
/// The full buffer being read.
bytes: &'a [u8],
/// The offset of the next unread byte.
offset: usize,
}
impl<'a> Reader<'a> {
pub(crate) fn new(bytes: &'a [u8]) -> Self {
Self { bytes, offset: 0 }
}
/// Returns the next `n` bytes and advances the cursor, or [`SaveError::Truncated`] if fewer remain.
pub(crate) fn take(&mut self, n: usize) -> Result<&'a [u8], SaveError> {
let end = self.offset.checked_add(n).ok_or(SaveError::Truncated {
offset: self.offset,
needed: n,
available: self.bytes.len().saturating_sub(self.offset),
})?;
let slice = self.bytes.get(self.offset..end).ok_or(SaveError::Truncated {
offset: self.offset,
needed: n,
available: self.bytes.len().saturating_sub(self.offset),
})?;
self.offset = end;
Ok(slice)
}
/// Returns the next `N` bytes as a fixed-size array and advances the cursor.
pub(crate) fn take_array<const N: usize>(&mut self) -> Result<[u8; N], SaveError> {
let mut array = [0u8; N];
array.copy_from_slice(self.take(N)?);
Ok(array)
}
}