Skip to content

Commit 23abd83

Browse files
committed
Add implementation of Decode for Box<str> and Box<[T]>
Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
1 parent 805816a commit 23abd83

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

src/codec.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,72 @@ impl<T> WrapperTypeDecode for Box<T> {
573573
}
574574
}
575575

576+
impl<T: Decode> Decode for Box<[T]> {
577+
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
578+
let len = <Compact<u32>>::decode(input).map(|Compact(len)| len as usize)?;
579+
580+
input.descend_ref()?;
581+
582+
// Placement new is not yet stable, but we can just manually allocate a chunk of memory
583+
// and convert it to a `Box` ourselves.
584+
//
585+
// The explicit types here are written out for clarity.
586+
//
587+
// TODO: Use `Box::new_uninit_slice` once that's stable.
588+
let layout = core::alloc::Layout::array::<MaybeUninit<T>>(len)
589+
.map_err(|_| Error::from("Item is too big and cannot be allocated"))?;
590+
591+
let ptr: *mut MaybeUninit<T> = if layout.size() == 0 {
592+
core::ptr::NonNull::dangling().as_ptr()
593+
} else {
594+
// SAFETY: Layout has a non-zero size so calling this is safe.
595+
let ptr = unsafe { crate::alloc::alloc::alloc(layout) };
596+
597+
if ptr.is_null() {
598+
crate::alloc::alloc::handle_alloc_error(layout);
599+
}
600+
601+
ptr.cast()
602+
};
603+
604+
// SAFETY: Constructing a `Box` from a piece of memory allocated with `std::alloc::alloc`
605+
// is explicitly allowed as long as it was allocated with the global allocator
606+
// and the memory layout matches.
607+
//
608+
// Constructing a `Box` from `NonNull::dangling` is also always safe as long
609+
// as the underlying type is zero-sized.
610+
let mut boxed_slice: Box<[MaybeUninit<T>]> = unsafe {
611+
Box::from_raw(core::slice::from_raw_parts_mut(ptr, len))
612+
};
613+
614+
for elem in &mut *boxed_slice {
615+
T::decode_into(input, elem)?;
616+
}
617+
618+
// Decoding succeeded, so let's get rid of `MaybeUninit`.
619+
// TODO: Use `Box::assume_init` once that's stable.
620+
let boxed_slice = Vec::from(boxed_slice)
621+
.into_iter()
622+
.map(|elem| unsafe { MaybeUninit::assume_init(elem) })
623+
.collect();
624+
625+
input.ascend_ref();
626+
Ok(boxed_slice)
627+
}
628+
}
629+
630+
impl Decode for Box<str> {
631+
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
632+
// Guaranteed to create a Vec with capacity == len
633+
let vec = Vec::from(Box::<[u8]>::decode(input)?);
634+
// Guaranteed not to reallocate the vec, only transmute to String
635+
let str = String::from_utf8(vec).map_err(|_| "Invalid utf8 sequence")?;
636+
637+
assert_eq!(str.capacity(), str.len());
638+
Ok(str.into_boxed_str())
639+
}
640+
}
641+
576642
impl<T> WrapperTypeDecode for Rc<T> {
577643
type Wrapped = T;
578644

@@ -1612,6 +1678,28 @@ mod tests {
16121678
assert_eq!((x, y), Decode::decode(&mut &encoded[..]).unwrap());
16131679
}
16141680

1681+
#[test]
1682+
fn boxed_str_works() {
1683+
let s = "Hello world".to_owned();
1684+
let b = s.clone().into_boxed_str();
1685+
1686+
let encoded = b.encode();
1687+
assert_eq!(s.encode(), encoded);
1688+
1689+
assert_eq!(*b, *Box::<str>::decode(&mut &encoded[..]).unwrap());
1690+
}
1691+
1692+
#[test]
1693+
fn boxed_slice_works() {
1694+
let v = vec![1u32, 2, 3, 4, 5, 6];
1695+
let b = v.clone().into_boxed_slice();
1696+
1697+
let encoded = b.encode();
1698+
assert_eq!(v.encode(), encoded);
1699+
1700+
assert_eq!(*b, *Box::<[u32]>::decode(&mut &b.encode()[..]).unwrap());
1701+
}
1702+
16151703
#[test]
16161704
fn cow_works() {
16171705
let x = &[1u32, 2, 3, 4, 5, 6][..];

0 commit comments

Comments
 (0)