43 lines
1.4 KiB
Rust
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)
|
|
}
|
|
}
|