summaryrefslogtreecommitdiffstats
path: root/third_party/rust/scroll/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /third_party/rust/scroll/src
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--third_party/rust/scroll/src/ctx.rs107
-rw-r--r--third_party/rust/scroll/src/endian.rs5
-rw-r--r--third_party/rust/scroll/src/error.rs26
-rw-r--r--third_party/rust/scroll/src/leb128.rs18
-rw-r--r--third_party/rust/scroll/src/lesser.rs7
-rw-r--r--third_party/rust/scroll/src/lib.rs67
-rw-r--r--third_party/rust/scroll/src/pread.rs7
-rw-r--r--third_party/rust/scroll/src/pwrite.rs9
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..];