diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /third_party/rust/scroll/src | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/scroll/src')
-rw-r--r-- | third_party/rust/scroll/src/ctx.rs | 107 | ||||
-rw-r--r-- | third_party/rust/scroll/src/endian.rs | 5 | ||||
-rw-r--r-- | third_party/rust/scroll/src/error.rs | 26 | ||||
-rw-r--r-- | third_party/rust/scroll/src/leb128.rs | 18 | ||||
-rw-r--r-- | third_party/rust/scroll/src/lesser.rs | 7 | ||||
-rw-r--r-- | third_party/rust/scroll/src/lib.rs | 67 | ||||
-rw-r--r-- | third_party/rust/scroll/src/pread.rs | 7 | ||||
-rw-r--r-- | third_party/rust/scroll/src/pwrite.rs | 9 |
8 files changed, 156 insertions, 90 deletions
diff --git a/third_party/rust/scroll/src/ctx.rs b/third_party/rust/scroll/src/ctx.rs index 1f982b82fa..e24d2dc506 100644 --- a/third_party/rust/scroll/src/ctx.rs +++ b/third_party/rust/scroll/src/ctx.rs @@ -180,17 +180,14 @@ //! } //! ``` -use core::mem::size_of; -use core::mem::transmute; +use core::mem::{size_of, MaybeUninit}; use core::ptr::copy_nonoverlapping; -use core::result; -use core::str; - +use core::{result, str}; #[cfg(feature = "std")] use std::ffi::{CStr, CString}; use crate::endian::Endian; -use crate::error; +use crate::{error, Pread, Pwrite}; /// A trait for measuring how large something is; for a byte sequence, it will be its length. pub trait MeasureWith<Ctx> { @@ -240,18 +237,14 @@ impl Default for StrCtx { impl StrCtx { pub fn len(&self) -> usize { - match *self { + match self { StrCtx::Delimiter(_) | StrCtx::DelimiterUntil(_, _) => 1, StrCtx::Length(_) => 0, } } pub fn is_empty(&self) -> bool { - if let StrCtx::Length(_) = *self { - true - } else { - false - } + matches!(self, StrCtx::Length(_)) } } @@ -267,6 +260,7 @@ pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> { /// `[u8]`), then you need to implement this trait /// /// ```rust +/// ##[cfg(feature = "std")] { /// use scroll::{self, ctx, Pread}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); @@ -286,6 +280,7 @@ pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> { /// /// let foo2 = bytes.pread_with::<Foo>(0, scroll::BE).unwrap(); /// assert_eq!(Foo(0xdeadu16), foo2); +/// # } /// ``` /// /// # Advanced: Using Your Own Error in `TryFromCtx` @@ -350,6 +345,7 @@ pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { /// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` /// # Example /// ```rust +/// ##[cfg(feature = "std")] { /// use scroll::{self, ctx, LE, Endian, Pwrite}; /// #[derive(Debug, PartialEq, Eq)] /// pub struct Foo(u16); @@ -369,6 +365,7 @@ pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { /// /// let mut bytes: [u8; 4] = [0, 0, 0, 0]; /// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); +/// # } /// ``` pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { type Error; @@ -403,13 +400,14 @@ macro_rules! signed_to_unsigned { macro_rules! write_into { ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => {{ + assert!($dst.len() >= $size); + let bytes = if $endian.is_little() { + $n.to_le() + } else { + $n.to_be() + } + .to_ne_bytes(); unsafe { - assert!($dst.len() >= $size); - let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { - $n.to_le() - } else { - $n.to_be() - }); copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); } }}; @@ -570,12 +568,12 @@ macro_rules! from_ctx_float_impl { &mut data as *mut signed_to_unsigned!($typ) as *mut u8, $size, ); - transmute(if le.is_little() { - data.to_le() - } else { - data.to_be() - }) } + $typ::from_bits(if le.is_little() { + data.to_le() + } else { + data.to_be() + }) } } impl<'a> TryFromCtx<'a, Endian> for $typ @@ -621,13 +619,7 @@ macro_rules! into_ctx_float_impl { #[inline] fn into_ctx(self, dst: &mut [u8], le: Endian) { assert!(dst.len() >= $size); - write_into!( - signed_to_unsigned!($typ), - $size, - transmute::<$typ, signed_to_unsigned!($typ)>(self), - dst, - le - ); + write_into!(signed_to_unsigned!($typ), $size, self.to_bits(), dst, le); } } impl<'a> IntoCtx<Endian> for &'a $typ { @@ -725,7 +717,7 @@ impl<'a> TryIntoCtx for &'a [u8] { let src_len = self.len() as isize; let dst_len = dst.len() as isize; // if src_len < 0 || dst_len < 0 || offset < 0 { - // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into()) + // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {src_len} dst len: {dst_len} offset: {offset}")).into()) // } if src_len > dst_len { Err(error::Error::TooBig { @@ -789,6 +781,56 @@ impl<'a> TryFromCtx<'a, usize> for &'a [u8] { } } +impl<'a, Ctx: Copy, T: TryFromCtx<'a, Ctx, Error = error::Error>, const N: usize> + TryFromCtx<'a, Ctx> for [T; N] +{ + type Error = error::Error; + fn try_from_ctx(src: &'a [u8], ctx: Ctx) -> Result<(Self, usize), Self::Error> { + let mut offset = 0; + + let mut buf: [MaybeUninit<T>; N] = core::array::from_fn(|_| MaybeUninit::uninit()); + + let mut error_ctx = None; + for (idx, element) in buf.iter_mut().enumerate() { + match src.gread_with::<T>(&mut offset, ctx) { + Ok(val) => { + *element = MaybeUninit::new(val); + } + Err(e) => { + error_ctx = Some((e, idx)); + break; + } + } + } + if let Some((e, idx)) = error_ctx { + for element in &mut buf[0..idx].iter_mut() { + // SAFETY: Any element upto idx must have already been initialized, since + // we iterate until we encounter an error. + unsafe { + element.assume_init_drop(); + } + } + Err(e) + } else { + // SAFETY: we initialized each element above by preading them out, correctness + // of the initialized element is guaranted by pread itself + Ok((buf.map(|element| unsafe { element.assume_init() }), offset)) + } + } +} +impl<Ctx: Copy, T: TryIntoCtx<Ctx, Error = error::Error>, const N: usize> TryIntoCtx<Ctx> + for [T; N] +{ + type Error = error::Error; + fn try_into_ctx(self, buf: &mut [u8], ctx: Ctx) -> Result<usize, Self::Error> { + let mut offset = 0; + for element in self { + buf.gwrite_with(element, &mut offset, ctx)?; + } + Ok(offset) + } +} + #[cfg(feature = "std")] impl<'a> TryFromCtx<'a> for &'a CStr { type Error = error::Error; @@ -863,11 +905,11 @@ impl TryIntoCtx for CString { // } #[cfg(test)] +#[cfg(feature = "std")] mod tests { use super::*; #[test] - #[cfg(feature = "std")] fn parse_a_cstr() { let src = CString::new("Hello World").unwrap(); let as_bytes = src.as_bytes_with_nul(); @@ -879,7 +921,6 @@ mod tests { } #[test] - #[cfg(feature = "std")] fn round_trip_a_c_str() { let src = CString::new("Hello World").unwrap(); let src = src.as_c_str(); diff --git a/third_party/rust/scroll/src/endian.rs b/third_party/rust/scroll/src/endian.rs index 06d7a1dc1c..7b83c348d5 100644 --- a/third_party/rust/scroll/src/endian.rs +++ b/third_party/rust/scroll/src/endian.rs @@ -43,9 +43,6 @@ impl Endian { } #[inline] pub fn is_little(&self) -> bool { - match *self { - LE => true, - _ => false, - } + *self == LE } } diff --git a/third_party/rust/scroll/src/error.rs b/third_party/rust/scroll/src/error.rs index 7740254774..1b68c2e4c7 100644 --- a/third_party/rust/scroll/src/error.rs +++ b/third_party/rust/scroll/src/error.rs @@ -1,10 +1,7 @@ use core::fmt::{self, Display}; use core::result; - -#[cfg(feature = "std")] -use std::error; #[cfg(feature = "std")] -use std::io; +use std::{error, io}; #[derive(Debug)] /// A custom Scroll error @@ -20,18 +17,19 @@ pub enum Error { size: usize, msg: &'static str, }, + /// A custom Scroll error for reporting messages to clients. + /// For no-std, use [`Error::BadInput`] with a static string. #[cfg(feature = "std")] - /// A custom Scroll error for reporting messages to clients Custom(String), - #[cfg(feature = "std")] /// Returned when IO based errors are encountered + #[cfg(feature = "std")] IO(io::Error), } #[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { - match *self { + match self { Error::TooBig { .. } => "TooBig", Error::BadOffset(_) => "BadOffset", Error::BadInput { .. } => "BadInput", @@ -40,7 +38,7 @@ impl error::Error for Error { } } fn cause(&self) -> Option<&dyn error::Error> { - match *self { + match self { Error::TooBig { .. } => None, Error::BadOffset(_) => None, Error::BadInput { .. } => None, @@ -59,23 +57,23 @@ impl From<io::Error> for Error { impl Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { Error::TooBig { ref size, ref len } => { - write!(fmt, "type is too big ({}) for {}", size, len) + write!(fmt, "type is too big ({size}) for {len}") } Error::BadOffset(ref offset) => { - write!(fmt, "bad offset {}", offset) + write!(fmt, "bad offset {offset}") } Error::BadInput { ref msg, ref size } => { - write!(fmt, "bad input {} ({})", msg, size) + write!(fmt, "bad input {msg} ({size})") } #[cfg(feature = "std")] Error::Custom(ref msg) => { - write!(fmt, "{}", msg) + write!(fmt, "{msg}") } #[cfg(feature = "std")] Error::IO(ref err) => { - write!(fmt, "{}", err) + write!(fmt, "{err}") } } } diff --git a/third_party/rust/scroll/src/leb128.rs b/third_party/rust/scroll/src/leb128.rs index 43f50b95f1..eceec6d166 100644 --- a/third_party/rust/scroll/src/leb128.rs +++ b/third_party/rust/scroll/src/leb128.rs @@ -1,9 +1,8 @@ -use crate::ctx::TryFromCtx; -use crate::error; -use crate::Pread; use core::convert::{AsRef, From}; -use core::result; -use core::u8; +use core::{result, u8}; + +use crate::ctx::TryFromCtx; +use crate::{error, Pread}; #[derive(Debug, PartialEq, Copy, Clone)] /// An unsigned leb128 integer @@ -184,21 +183,24 @@ mod tests { let buf = [2u8 | CONTINUATION_BIT, 1]; let bytes = &buf[..]; let num = bytes.pread::<Uleb128>(0).unwrap(); - println!("num: {:?}", &num); + #[cfg(feature = "std")] + println!("num: {num:?}"); assert_eq!(130u64, num.into()); assert_eq!(num.size(), 2); let buf = [0x00, 0x01]; let bytes = &buf[..]; let num = bytes.pread::<Uleb128>(0).unwrap(); - println!("num: {:?}", &num); + #[cfg(feature = "std")] + println!("num: {num:?}"); assert_eq!(0u64, num.into()); assert_eq!(num.size(), 1); let buf = [0x21]; let bytes = &buf[..]; let num = bytes.pread::<Uleb128>(0).unwrap(); - println!("num: {:?}", &num); + #[cfg(feature = "std")] + println!("num: {num:?}"); assert_eq!(0x21u64, num.into()); assert_eq!(num.size(), 1); } diff --git a/third_party/rust/scroll/src/lesser.rs b/third_party/rust/scroll/src/lesser.rs index 46ef4c5b11..636bf2553e 100644 --- a/third_party/rust/scroll/src/lesser.rs +++ b/third_party/rust/scroll/src/lesser.rs @@ -1,6 +1,7 @@ -use crate::ctx::{FromCtx, IntoCtx, SizeWith}; use std::io::{Read, Result, Write}; +use crate::ctx::{FromCtx, IntoCtx, SizeWith}; + /// An extension trait to `std::io::Read` streams; mainly targeted at reading primitive types with /// a known size. /// @@ -104,8 +105,8 @@ pub trait IOread<Ctx: Copy>: Read { fn ioread_with<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self, ctx: Ctx) -> Result<N> { let mut scratch = [0u8; 256]; let size = N::size_with(&ctx); - let mut buf = &mut scratch[0..size]; - self.read_exact(&mut buf)?; + let buf = &mut scratch[0..size]; + self.read_exact(buf)?; Ok(N::from_ctx(buf, ctx)) } } diff --git a/third_party/rust/scroll/src/lib.rs b/third_party/rust/scroll/src/lib.rs index dcb58e7564..2740648517 100644 --- a/third_party/rust/scroll/src/lib.rs +++ b/third_party/rust/scroll/src/lib.rs @@ -119,6 +119,7 @@ //! [FromCtx](ctx/trait.FromCtx.html) and [SizeWith](ctx/trait.SizeWith.html). //! //! ```rust +//! ##[cfg(feature = "std")] { //! use std::io::Cursor; //! use scroll::{IOread, ctx, Endian}; //! let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; @@ -139,12 +140,14 @@ //! // read/written, e.g. switching between ELF32 or ELF64 at runtime. //! let size = <u64 as ctx::SizeWith<Endian>>::size_with(&Endian::Little) as u64; //! assert_eq!(prev + size, after); +//! # } //! ``` //! //! In the same vein as IOread we can use IOwrite to write a type to anything implementing //! `std::io::Write`: //! //! ```rust +//! ##[cfg(feature = "std")] { //! use std::io::Cursor; //! use scroll::{IOwrite}; //! @@ -155,6 +158,7 @@ //! cursor.iowrite_with(0xdeadbeef as u32, scroll::BE).unwrap(); //! //! assert_eq!(cursor.into_inner(), [0xde, 0xad, 0xbe, 0xef, 0x0]); +//! # } //! ``` //! //! ## Complex use cases @@ -249,8 +253,7 @@ pub use crate::pwrite::*; #[doc(hidden)] pub mod export { - pub use ::core::mem; - pub use ::core::result; + pub use ::core::{mem, result}; } #[allow(unused)] @@ -267,7 +270,6 @@ doc_comment!(include_str!("../README.md")); #[cfg(test)] mod tests { - #[allow(overflowing_literals)] use super::LE; #[test] @@ -355,36 +357,48 @@ mod tests { let bytes: [u8; 2] = [0x2e, 0x0]; let b = &bytes[..]; let s: &str = b.pread(0).unwrap(); - println!("str: {}", s); + #[cfg(feature = "std")] + println!("str: {s}"); assert_eq!(s.len(), bytes[..].len() - 1); let bytes: &[u8] = b"hello, world!\0some_other_things"; let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap(); - println!("{:?}", &hello_world); + #[cfg(feature = "std")] + println!("{hello_world:?}"); assert_eq!(hello_world.len(), 13); let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap(); - println!("{:?}", &hello); + #[cfg(feature = "std")] + println!("{hello:?}"); assert_eq!(hello.len(), 6); // this could result in underflow so we just try it let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE)); let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); - println!("{:?}", &error); + #[cfg(feature = "std")] + println!("{error:?}"); 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::*; use super::Pread; let bytes: &[u8] = b""; let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)); - println!("1 {:?}", &hello_world); - assert_eq!(hello_world.is_err(), true); + #[cfg(feature = "std")] + println!("1 {hello_world:?}"); + assert!(hello_world.unwrap().is_empty()); let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); - println!("2 {:?}", &error); + #[cfg(feature = "std")] + println!("2 {error:?}"); assert!(error.is_err()); let bytes: &[u8] = b"\0"; let null = bytes.pread::<&str>(0).unwrap(); - println!("3 {:?}", &null); + #[cfg(feature = "std")] + println!("3 {null:?}"); assert_eq!(null.len(), 0); } @@ -413,8 +427,7 @@ mod tests { assert_eq!(bytes, "bytes"); } - use std::error; - use std::fmt::{self, Display}; + use core::fmt::{self, Display}; #[derive(Debug)] pub struct ExternalError {} @@ -425,11 +438,12 @@ mod tests { } } - impl error::Error for ExternalError { + #[cfg(feature = "std")] + impl std::error::Error for ExternalError { fn description(&self) -> &str { "ExternalError" } - fn cause(&self) -> Option<&dyn error::Error> { + fn cause(&self) -> Option<&dyn std::error::Error> { None } } @@ -451,7 +465,7 @@ mod tests { fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<usize, Self::Error> { use super::Pwrite; if this.len() < 2 { - return Err((ExternalError {}).into()); + return Err(ExternalError {}); } this.pwrite_with(self.0, 0, le)?; Ok(2) @@ -463,7 +477,7 @@ mod tests { fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, usize), Self::Error> { use super::Pread; if this.len() > 2 { - return Err((ExternalError {}).into()); + return Err(ExternalError {}); } let n = this.pread_with(0, le)?; Ok((Foo(n), 2)) @@ -499,7 +513,7 @@ mod tests { let mut offset = 0; let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); assert_eq!(deadbeef, $deadbeef as $typ); - assert_eq!(offset, ::std::mem::size_of::<$typ>()); + assert_eq!(offset, ::core::mem::size_of::<$typ>()); } }; } @@ -518,7 +532,7 @@ mod tests { let mut offset = 0; let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); assert_eq!(deadbeef, $deadbeef as $typ); - assert_eq!(offset, ::std::mem::size_of::<$typ>()); + assert_eq!(offset, ::core::mem::size_of::<$typ>()); } }; } @@ -537,8 +551,8 @@ mod tests { let o2 = &mut 0; let val: $typ = buffer.gread_with(o2, LE).unwrap(); assert_eq!(val, $val); - assert_eq!(*offset, ::std::mem::size_of::<$typ>()); - assert_eq!(*o2, ::std::mem::size_of::<$typ>()); + assert_eq!(*offset, ::core::mem::size_of::<$typ>()); + assert_eq!(*o2, ::core::mem::size_of::<$typ>()); assert_eq!(*o2, *offset); buffer.gwrite_with($val.clone(), offset, BE).unwrap(); let val: $typ = buffer.gread_with(o2, BE).unwrap(); @@ -612,16 +626,17 @@ mod tests { let res = b.gread_with::<&str>(offset, StrCtx::Length(3)); assert!(res.is_err()); *offset = 0; - let astring: [u8; 3] = [0x45, 042, 0x44]; + let astring: [u8; 3] = [0x45, 0x42, 0x44]; let string = astring.gread_with::<&str>(offset, StrCtx::Length(2)); match &string { - &Ok(_) => {} - &Err(ref err) => { - println!("{}", &err); + Ok(_) => {} + Err(_err) => { + #[cfg(feature = "std")] + println!("{_err}"); panic!(); } } - assert_eq!(string.unwrap(), "E*"); + assert_eq!(string.unwrap(), "EB"); *offset = 0; let bytes2: &[u8] = b.gread_with(offset, 2).unwrap(); assert_eq!(*offset, 2); diff --git a/third_party/rust/scroll/src/pread.rs b/third_party/rust/scroll/src/pread.rs index 72ba877054..15bf1426be 100644 --- a/third_party/rust/scroll/src/pread.rs +++ b/third_party/rust/scroll/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<Ctx: Copy, E: From<error::Error>> Pread<Ctx, E> for [u8] { ctx: Ctx, ) -> result::Result<N, E> { 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/third_party/rust/scroll/src/pwrite.rs b/third_party/rust/scroll/src/pwrite.rs index ab6d96157d..7a07f2daba 100644 --- a/third_party/rust/scroll/src/pwrite.rs +++ b/third_party/rust/scroll/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<Ctx: Copy, E: From<error::Error>> Pwrite<Ctx, E> for [u8] { offset: usize, ctx: Ctx, ) -> result::Result<usize, E> { - if offset >= self.len() { + if offset > self.len() { return Err(error::Error::BadOffset(offset).into()); } let dst = &mut self[offset..]; |