io: Allow setting position bigger than inner buffer

Currently if one calls `set_position` on a cursor setting the position
greater than the total length of the inner buffer future calls to `Read`
will panic. Bad `Cursor` - no biscuit.

Mirror the stdlib `Cursor` and allow setting the position to any value.
Make reads past the end of the buffer return `Ok(0)`.
This commit is contained in:
Tobin C. Harding 2025-01-08 14:42:50 +11:00
parent 4ade08c755
commit e315aaf1c6
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
1 changed files with 31 additions and 1 deletions

View File

@ -212,7 +212,7 @@ impl<T: AsRef<[u8]>> Cursor<T> {
/// This method allows seeking within the wrapped memory by setting the position.
///
/// Note that setting a position that is larger than the buffer length will cause reads to
/// return no bytes (EOF).
/// succeed by reading zero bytes.
#[inline]
pub fn set_position(&mut self, position: u64) { self.pos = position; }
@ -247,6 +247,10 @@ impl<T: AsRef<[u8]>> Read for Cursor<T> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let inner: &[u8] = self.inner.as_ref();
let start_pos = self.pos.try_into().unwrap_or(inner.len());
if start_pos >= self.inner.as_ref().len() {
return Ok(0);
}
let read = core::cmp::min(inner.len().saturating_sub(start_pos), buf.len());
buf[..read].copy_from_slice(&inner[start_pos..start_pos + read]);
self.pos = self.pos.saturating_add(read.try_into().unwrap_or(u64::MAX /* unreachable */));
@ -415,4 +419,30 @@ mod tests {
assert_eq!(read, 2);
assert_eq!(&buf, "16".as_bytes())
}
#[test]
#[cfg(feature = "std")]
fn set_position_past_end_read_returns_eof() {
const BUF_LEN: usize = 64; // Just a small buffer.
let mut buf = [0_u8; BUF_LEN]; // We never actually write to this buffer.
let v = [1_u8; BUF_LEN];
// Sanity check the stdlib Cursor's behaviour.
let mut c = std::io::Cursor::new(v);
for pos in [BUF_LEN, BUF_LEN + 1, BUF_LEN * 2] {
c.set_position(pos as u64);
let read = c.read(&mut buf).unwrap();
assert_eq!(read, 0);
assert_eq!(buf[0], 0x00); // Double check that buffer state is sane.
}
let mut c = Cursor::new(v);
for pos in [BUF_LEN, BUF_LEN + 1, BUF_LEN * 2] {
c.set_position(pos as u64);
let read = c.read(&mut buf).unwrap();
assert_eq!(read, 0);
assert_eq!(buf[0], 0x00); // Double check that buffer state is sane.
}
}
}