From aa8df8e4003e5dcf70cea7992683502cf377ea49 Mon Sep 17 00:00:00 2001 From: Ryan Lahfa Date: Tue, 21 Nov 2023 05:34:10 +0100 Subject: [PATCH] pread, pwrite: do not error out if {p,g}{read,write}_with if the offset is equal to slice len (#94) We now allow reading zero-sized types (like possibly an empty array) from empty slice, or the exact end of a byte array, and similarly, one can write a empty string or byte array into the tail end of a byte array (i.e., write nothing to nothing). Co-authored-by: Nickie S --- src/lib.rs | 7 ++++++- src/pread.rs | 7 ++++++- src/pwrite.rs | 9 ++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 07c91ff..2740648 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -377,6 +377,11 @@ mod tests { assert!(error.is_ok()); } + /// In this test, we are testing preading + /// at length boundaries. + /// In the past, this test was supposed to test failures for `hello_world`. + /// Since PR#94, this test is unwrapping as we exploit + /// the fact that if you do &x[x.len()..] you get an empty slice. #[test] fn pread_str_weird() { use super::ctx::*; @@ -385,7 +390,7 @@ mod tests { let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)); #[cfg(feature = "std")] println!("1 {hello_world:?}"); - assert!(hello_world.is_err()); + assert!(hello_world.unwrap().is_empty()); let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); #[cfg(feature = "std")] println!("2 {error:?}"); diff --git a/src/pread.rs b/src/pread.rs index 72ba877..15bf142 100644 --- a/src/pread.rs +++ b/src/pread.rs @@ -20,6 +20,11 @@ use crate::error; /// over chunks of memory or any other indexable type — but scroll does come with a set of powerful /// blanket implementations for data being a continous block of byte-addressable memory. /// +/// Note that in the particular case of the implementation of `Pread` for `[u8]`, +/// reading it at the length boundary of that slice will cause to read from an empty slice. +/// i.e. we make use of the fact that `&bytes[bytes.len()..]` will return an empty slice, rather +/// than returning an error. In the past, scroll returned an offset error. +/// /// Pread provides two main groups of functions: pread and gread. /// /// `pread` is the basic function that simply extracts a given type from a given data store - either @@ -167,7 +172,7 @@ impl> Pread for [u8] { ctx: Ctx, ) -> result::Result { let start = *offset; - if start >= self.len() { + if start > self.len() { return Err(error::Error::BadOffset(start).into()); } N::try_from_ctx(&self[start..], ctx).map(|(n, size)| { diff --git a/src/pwrite.rs b/src/pwrite.rs index ab6d961..7a07f2d 100644 --- a/src/pwrite.rs +++ b/src/pwrite.rs @@ -19,6 +19,13 @@ use crate::error; /// with 'read' switched for 'write' and 'From' switched with 'Into' so if you haven't yet you /// should read the documentation of `Pread` first. /// +/// As with `Pread`, note that in the particular case of the implementation of `Pwrite` for `[u8]`, +/// writing it at the length boundary of that slice will cause to write in an empty slice. +/// i.e. we make use of the fact that `&bytes[bytes.len()..]` will return an empty slice, rather +/// than returning an error. In the past, scroll returned an offset error. +/// In this case, this is relevant if you are writing an empty slice inside an empty slice and +/// expected this to work. +/// /// Unless you need to implement your own data store — that is either can't convert to `&[u8]` or /// have a data that does not expose a `&mut [u8]` — you will probably want to implement /// [TryIntoCtx](ctx/trait.TryIntoCtx.html) on your Rust types to be written. @@ -87,7 +94,7 @@ impl> Pwrite for [u8] { offset: usize, ctx: Ctx, ) -> result::Result { - if offset >= self.len() { + if offset > self.len() { return Err(error::Error::BadOffset(offset).into()); } let dst = &mut self[offset..];