From e315aaf1c65225b89435d0a60907a3233caa5db1 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 8 Jan 2025 14:42:50 +1100 Subject: [PATCH] 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)`. --- io/src/lib.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/io/src/lib.rs b/io/src/lib.rs index c8d846dd5..8dd775b19 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -212,7 +212,7 @@ impl> Cursor { /// 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> Read for Cursor { fn read(&mut self, buf: &mut [u8]) -> Result { 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. + } + } }