Skip to content

Commit d16421a

Browse files
committed
FromZeros boxed slice method supports slice DSTs
Change methods that allocate to return a new error, `AllocError`, on allocation failure rather than panicking or aborting. Makes progress on #29
1 parent 02dc876 commit d16421a

File tree

3 files changed

+88
-56
lines changed

3 files changed

+88
-56
lines changed

src/error.rs

+14
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,20 @@ impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> {
570570
}
571571
}
572572

573+
/// The error type of a failed allocation.
574+
///
575+
/// This type is intended to be deprecated in favor of the standard library's
576+
/// [`AllocError`] type once it is stabilized. When that happens, this type will
577+
/// be replaced by a type alias to the standard library type. We do not intend
578+
/// to treat this as a breaking change; users who wish to avoid breakage should
579+
/// avoid writing code which assumes that this is *not* such an alias. For
580+
/// example, implementing the same trait for both types will result in an impl
581+
/// conflict once this type is an alias.
582+
///
583+
/// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html
584+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
585+
pub struct AllocError;
586+
573587
#[cfg(test)]
574588
mod tests {
575589
use super::*;

src/impls.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,7 @@ mod tests {
10201020
impl<T: FromBytes> TryFromBytesTestable for T {
10211021
fn with_passing_test_cases<F: Fn(Box<Self>)>(f: F) {
10221022
// Test with a zeroed value.
1023-
f(Self::new_box_zeroed());
1023+
f(Self::new_box_zeroed().unwrap());
10241024

10251025
let ffs = {
10261026
let mut t = Self::new_zeroed();

src/lib.rs

+73-55
Original file line numberDiff line numberDiff line change
@@ -2113,14 +2113,15 @@ pub unsafe trait FromZeros: TryFromBytes {
21132113
/// Note that `Box<Self>` can be converted to `Arc<Self>` and other
21142114
/// container types without reallocation.
21152115
///
2116-
/// # Panics
2116+
/// # Errors
21172117
///
2118-
/// Panics if allocation of `size_of::<Self>()` bytes fails.
2118+
/// Returns an error on allocation failure. Allocation failure is guaranteed
2119+
/// never to cause a panic or an abort.
21192120
#[must_use = "has no side effects (other than allocation)"]
21202121
#[cfg(any(feature = "alloc", test))]
21212122
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
21222123
#[inline]
2123-
fn new_box_zeroed() -> Box<Self>
2124+
fn new_box_zeroed() -> Result<Box<Self>, AllocError>
21242125
where
21252126
Self: Sized,
21262127
{
@@ -2146,22 +2147,18 @@ pub unsafe trait FromZeros: TryFromBytes {
21462147
// [2] Per https://doc.rust-lang.org/std/ptr/struct.NonNull.html#method.dangling:
21472148
//
21482149
// Creates a new `NonNull` that is dangling, but well-aligned.
2149-
unsafe {
2150-
return Box::from_raw(NonNull::dangling().as_ptr());
2151-
}
2150+
return Ok(unsafe { Box::from_raw(NonNull::dangling().as_ptr()) });
21522151
}
21532152

21542153
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
21552154
#[allow(clippy::undocumented_unsafe_blocks)]
21562155
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout).cast::<Self>() };
21572156
if ptr.is_null() {
2158-
alloc::alloc::handle_alloc_error(layout);
2157+
return Err(AllocError);
21592158
}
21602159
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
21612160
#[allow(clippy::undocumented_unsafe_blocks)]
2162-
unsafe {
2163-
Box::from_raw(ptr)
2164-
}
2161+
Ok(unsafe { Box::from_raw(ptr) })
21652162
}
21662163

21672164
/// Creates a `Box<[Self]>` (a boxed slice) from zeroed bytes.
@@ -2181,22 +2178,24 @@ pub unsafe trait FromZeros: TryFromBytes {
21812178
/// actual information, but its `len()` property will report the correct
21822179
/// value.
21832180
///
2184-
/// # Panics
2181+
/// # Errors
21852182
///
2186-
/// * Panics if `size_of::<Self>() * len` overflows.
2187-
/// * Panics if allocation of `size_of::<Self>() * len` bytes fails.
2183+
/// Returns an error on allocation failure. Allocation failure is
2184+
/// guaranteed never to cause a panic or an abort.
21882185
#[must_use = "has no side effects (other than allocation)"]
21892186
#[cfg(feature = "alloc")]
21902187
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
21912188
#[inline]
2192-
fn new_box_slice_zeroed(len: usize) -> Box<[Self]>
2189+
fn new_box_zeroed_with_elems(count: usize) -> Result<Box<Self>, AllocError>
21932190
where
2194-
Self: Sized,
2191+
Self: KnownLayout<PointerMetadata = usize>,
21952192
{
2196-
let size = mem::size_of::<Self>()
2197-
.checked_mul(len)
2198-
.expect("mem::size_of::<Self>() * len overflows `usize`");
2199-
let align = mem::align_of::<Self>();
2193+
let size = match count.size_for_metadata(Self::LAYOUT) {
2194+
Some(size) => size,
2195+
None => return Err(AllocError),
2196+
};
2197+
2198+
let align = Self::LAYOUT.align.get();
22002199
// On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a
22012200
// bug in which sufficiently-large allocations (those which, when
22022201
// rounded up to the alignment, overflow `isize`) are not rejected,
@@ -2205,32 +2204,47 @@ pub unsafe trait FromZeros: TryFromBytes {
22052204
// TODO(#67): Once our MSRV is > 1.64.0, remove this assertion.
22062205
#[allow(clippy::as_conversions)]
22072206
let max_alloc = (isize::MAX as usize).saturating_sub(align);
2208-
assert!(size <= max_alloc);
2207+
if size > max_alloc {
2208+
return Err(AllocError);
2209+
}
2210+
22092211
// TODO(https://github.com/rust-lang/rust/issues/55724): Use
22102212
// `Layout::repeat` once it's stabilized.
2211-
let layout =
2212-
Layout::from_size_align(size, align).expect("total allocation size overflows `isize`");
2213+
let layout = Layout::from_size_align(size, align).or(Err(AllocError))?;
22132214

22142215
let ptr = if layout.size() != 0 {
22152216
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
22162217
#[allow(clippy::undocumented_unsafe_blocks)]
2217-
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout).cast::<Self>() };
2218-
if ptr.is_null() {
2219-
alloc::alloc::handle_alloc_error(layout);
2218+
let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) };
2219+
match NonNull::new(ptr) {
2220+
Some(ptr) => ptr,
2221+
None => return Err(AllocError),
22202222
}
2221-
ptr
22222223
} else {
22232224
// `Box<[T]>` does not allocate when `T` is zero-sized or when `len`
22242225
// is zero, but it does require a non-null dangling pointer for its
22252226
// allocation.
2226-
NonNull::<Self>::dangling().as_ptr()
2227+
NonNull::<u8>::dangling()
22272228
};
22282229

2230+
let ptr = Self::raw_from_ptr_len(ptr, count);
2231+
22292232
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
22302233
#[allow(clippy::undocumented_unsafe_blocks)]
2231-
unsafe {
2232-
Box::from_raw(slice::from_raw_parts_mut(ptr, len))
2233-
}
2234+
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
2235+
}
2236+
2237+
#[deprecated(since = "0.8.0", note = "renamed to `FromZeros::new_box_zeroed_with_elems`")]
2238+
#[doc(hidden)]
2239+
#[cfg(feature = "alloc")]
2240+
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
2241+
#[must_use = "has no side effects (other than allocation)"]
2242+
#[inline(always)]
2243+
fn new_box_slice_zeroed(len: usize) -> Result<Box<[Self]>, AllocError>
2244+
where
2245+
Self: Sized,
2246+
{
2247+
<[Self]>::new_box_zeroed_with_elems(len)
22342248
}
22352249

22362250
/// Creates a `Vec<Self>` from zeroed bytes.
@@ -2249,19 +2263,19 @@ pub unsafe trait FromZeros: TryFromBytes {
22492263
/// actual information, but its `len()` property will report the correct
22502264
/// value.
22512265
///
2252-
/// # Panics
2266+
/// # Errors
22532267
///
2254-
/// * Panics if `size_of::<Self>() * len` overflows.
2255-
/// * Panics if allocation of `size_of::<Self>() * len` bytes fails.
2268+
/// Returns an error on allocation failure. Allocation failure is
2269+
/// guaranteed never to cause a panic or an abort.
22562270
#[must_use = "has no side effects (other than allocation)"]
22572271
#[cfg(feature = "alloc")]
22582272
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
22592273
#[inline(always)]
2260-
fn new_vec_zeroed(len: usize) -> Vec<Self>
2274+
fn new_vec_zeroed(len: usize) -> Result<Vec<Self>, AllocError>
22612275
where
22622276
Self: Sized,
22632277
{
2264-
Self::new_box_slice_zeroed(len).into()
2278+
<[Self]>::new_box_zeroed_with_elems(len).map(Into::into)
22652279
}
22662280
}
22672281

@@ -4972,7 +4986,7 @@ mod alloc_support {
49724986

49734987
#[test]
49744988
fn test_new_box_zeroed() {
4975-
assert_eq!(*u64::new_box_zeroed(), 0);
4989+
assert_eq!(u64::new_box_zeroed(), Ok(Box::new(0)));
49764990
}
49774991

49784992
#[test]
@@ -4986,13 +5000,13 @@ mod alloc_support {
49865000
// when running under Miri.
49875001
#[allow(clippy::unit_cmp)]
49885002
{
4989-
assert_eq!(*<()>::new_box_zeroed(), ());
5003+
assert_eq!(<()>::new_box_zeroed(), Ok(Box::new(())));
49905004
}
49915005
}
49925006

49935007
#[test]
49945008
fn test_new_box_slice_zeroed() {
4995-
let mut s: Box<[u64]> = u64::new_box_slice_zeroed(3);
5009+
let mut s: Box<[u64]> = <[u64]>::new_box_zeroed_with_elems(3).unwrap();
49965010
assert_eq!(s.len(), 3);
49975011
assert_eq!(&*s, &[0, 0, 0]);
49985012
s[1] = 3;
@@ -5001,13 +5015,13 @@ mod alloc_support {
50015015

50025016
#[test]
50035017
fn test_new_box_slice_zeroed_empty() {
5004-
let s: Box<[u64]> = u64::new_box_slice_zeroed(0);
5018+
let s: Box<[u64]> = <[u64]>::new_box_zeroed_with_elems(0).unwrap();
50055019
assert_eq!(s.len(), 0);
50065020
}
50075021

50085022
#[test]
50095023
fn test_new_box_slice_zeroed_zst() {
5010-
let mut s: Box<[()]> = <()>::new_box_slice_zeroed(3);
5024+
let mut s: Box<[()]> = <[()]>::new_box_zeroed_with_elems(3).unwrap();
50115025
assert_eq!(s.len(), 3);
50125026
assert!(s.get(10).is_none());
50135027
// This test exists in order to exercise unsafe code, especially
@@ -5021,21 +5035,19 @@ mod alloc_support {
50215035

50225036
#[test]
50235037
fn test_new_box_slice_zeroed_zst_empty() {
5024-
let s: Box<[()]> = <()>::new_box_slice_zeroed(0);
5038+
let s: Box<[()]> = <[()]>::new_box_zeroed_with_elems(0).unwrap();
50255039
assert_eq!(s.len(), 0);
50265040
}
50275041

50285042
#[test]
5029-
#[should_panic(expected = "mem::size_of::<Self>() * len overflows `usize`")]
5030-
fn test_new_box_slice_zeroed_panics_mul_overflow() {
5031-
let _ = u16::new_box_slice_zeroed(usize::MAX);
5032-
}
5043+
fn test_new_box_slice_zeroed_errors() {
5044+
assert_eq!(<[u16]>::new_box_zeroed_with_elems(usize::MAX), Err(AllocError));
50335045

5034-
#[test]
5035-
#[should_panic(expected = "assertion failed: size <= max_alloc")]
5036-
fn test_new_box_slice_zeroed_panics_isize_overflow() {
50375046
let max = usize::try_from(isize::MAX).unwrap();
5038-
let _ = u16::new_box_slice_zeroed((max / mem::size_of::<u16>()) + 1);
5047+
assert_eq!(
5048+
<[u16]>::new_box_zeroed_with_elems((max / mem::size_of::<u16>()) + 1),
5049+
Err(AllocError)
5050+
);
50395051
}
50405052
}
50415053
}
@@ -5525,14 +5537,20 @@ mod tests {
55255537

55265538
#[cfg(feature = "alloc")]
55275539
{
5528-
assert_eq!(bool::new_box_zeroed(), Box::new(false));
5529-
assert_eq!(char::new_box_zeroed(), Box::new('\0'));
5540+
assert_eq!(bool::new_box_zeroed(), Ok(Box::new(false)));
5541+
assert_eq!(char::new_box_zeroed(), Ok(Box::new('\0')));
55305542

5531-
assert_eq!(bool::new_box_slice_zeroed(3).as_ref(), [false, false, false]);
5532-
assert_eq!(char::new_box_slice_zeroed(3).as_ref(), ['\0', '\0', '\0']);
5543+
assert_eq!(
5544+
<[bool]>::new_box_zeroed_with_elems(3).unwrap().as_ref(),
5545+
[false, false, false]
5546+
);
5547+
assert_eq!(
5548+
<[char]>::new_box_zeroed_with_elems(3).unwrap().as_ref(),
5549+
['\0', '\0', '\0']
5550+
);
55335551

5534-
assert_eq!(bool::new_vec_zeroed(3).as_ref(), [false, false, false]);
5535-
assert_eq!(char::new_vec_zeroed(3).as_ref(), ['\0', '\0', '\0']);
5552+
assert_eq!(bool::new_vec_zeroed(3).unwrap().as_ref(), [false, false, false]);
5553+
assert_eq!(char::new_vec_zeroed(3).unwrap().as_ref(), ['\0', '\0', '\0']);
55365554
}
55375555

55385556
let mut string = "hello".to_string();

0 commit comments

Comments
 (0)