Merge rust-bitcoin/rust-bitcoin#3885: io: Allow setting position bigger than inner buffer

e315aaf1c6 io: Allow setting position bigger than inner buffer (Tobin C. Harding)

Pull request description:

  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)`.

ACKs for top commit:
  apoelstra:
    ACK e315aaf1c65225b89435d0a60907a3233caa5db1; successfully ran local tests

Tree-SHA512: 71b151218e96343a86c8b2c21c6e8212e2d1c2aea6517b4662da3ad01b3b598cec497028b863a8e36a6b1fae1d4f7f975c8610c3b6f39f1c256adc5751d06ea0
This commit is contained in:
merge-script 2025-01-10 00:56:43 +00:00
commit 67db64cd97
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
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. /// 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 /// 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] #[inline]
pub fn set_position(&mut self, position: u64) { self.pos = position; } 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> { fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let inner: &[u8] = self.inner.as_ref(); let inner: &[u8] = self.inner.as_ref();
let start_pos = self.pos.try_into().unwrap_or(inner.len()); 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()); let read = core::cmp::min(inner.len().saturating_sub(start_pos), buf.len());
buf[..read].copy_from_slice(&inner[start_pos..start_pos + read]); 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 */)); 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!(read, 2);
assert_eq!(&buf, "16".as_bytes()) 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.
}
}
} }