Skip to content

Commit 8d64040

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 3093640 commit 8d64040

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

src/codec.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,77 @@ impl<T> WrapperTypeDecode for Box<T> {
595595

596596
impl<T: DecodeWithMemTracking> DecodeWithMemTracking for Box<T> {}
597597

598+
impl<T: Decode> Decode for Box<[T]> {
599+
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
600+
let len = <Compact<u32>>::decode(input).map(|Compact(len)| len as usize)?;
601+
602+
input.descend_ref()?;
603+
604+
// Placement new is not yet stable, but we can just manually allocate a chunk of memory
605+
// and convert it to a `Box` ourselves.
606+
//
607+
// The explicit types here are written out for clarity.
608+
//
609+
// TODO: Use `Box::new_uninit_slice` once that's stable.
610+
let layout = core::alloc::Layout::array::<MaybeUninit<T>>(len)
611+
.map_err(|_| Error::from("Item is too big and cannot be allocated"))?;
612+
613+
input.on_before_alloc_mem(layout.size())?;
614+
let ptr: *mut MaybeUninit<T> = if layout.size() == 0 {
615+
core::ptr::NonNull::dangling().as_ptr()
616+
} else {
617+
// SAFETY: Layout has a non-zero size so calling this is safe.
618+
let ptr = unsafe { crate::alloc::alloc::alloc(layout) };
619+
620+
if ptr.is_null() {
621+
crate::alloc::alloc::handle_alloc_error(layout);
622+
}
623+
624+
ptr.cast()
625+
};
626+
627+
// SAFETY: Constructing a `Box` from a piece of memory allocated with `std::alloc::alloc`
628+
// is explicitly allowed as long as it was allocated with the global allocator
629+
// and the memory layout matches.
630+
//
631+
// Constructing a `Box` from `NonNull::dangling` is also always safe as long
632+
// as the underlying type is zero-sized.
633+
let mut boxed_slice: Box<[MaybeUninit<T>]> = unsafe {
634+
Box::from_raw(core::slice::from_raw_parts_mut(ptr, len))
635+
};
636+
637+
for elem in &mut *boxed_slice {
638+
T::decode_into(input, elem)?;
639+
}
640+
641+
// Decoding succeeded, so let's get rid of `MaybeUninit`.
642+
// TODO: Use `Box::assume_init` once that's stable.
643+
let boxed_slice = Vec::from(boxed_slice)
644+
.into_iter()
645+
.map(|elem| unsafe { MaybeUninit::assume_init(elem) })
646+
.collect();
647+
648+
input.ascend_ref();
649+
Ok(boxed_slice)
650+
}
651+
}
652+
653+
impl<T: DecodeWithMemTracking> DecodeWithMemTracking for Box<[T]> {}
654+
655+
impl Decode for Box<str> {
656+
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
657+
// Guaranteed to create a Vec with capacity == len
658+
let vec = Vec::from(Box::<[u8]>::decode(input)?);
659+
// Guaranteed not to reallocate the vec, only transmute to String
660+
let str = String::from_utf8(vec).map_err(|_| "Invalid utf8 sequence")?;
661+
662+
assert_eq!(str.capacity(), str.len());
663+
Ok(str.into_boxed_str())
664+
}
665+
}
666+
667+
impl DecodeWithMemTracking for Box<str> {}
668+
598669
impl<T> WrapperTypeDecode for Rc<T> {
599670
type Wrapped = T;
600671

@@ -1713,6 +1784,39 @@ mod tests {
17131784
assert_eq!((x, y), Decode::decode(&mut &encoded[..]).unwrap());
17141785
}
17151786

1787+
#[test]
1788+
fn boxed_str_works() {
1789+
let s = "Hello world".to_owned();
1790+
let b = s.clone().into_boxed_str();
1791+
1792+
let encoded = b.encode();
1793+
assert_eq!(s.encode(), encoded);
1794+
1795+
assert_eq!(*b, *Box::<str>::decode(&mut &encoded[..]).unwrap());
1796+
}
1797+
1798+
#[test]
1799+
fn boxed_slice_of_primitives_works() {
1800+
let v = vec![1u32, 2, 3, 4, 5, 6];
1801+
let b = v.clone().into_boxed_slice();
1802+
1803+
let encoded = b.encode();
1804+
assert_eq!(v.encode(), encoded);
1805+
1806+
assert_eq!(*b, *Box::<[u32]>::decode(&mut &b.encode()[..]).unwrap());
1807+
}
1808+
1809+
#[test]
1810+
fn boxed_slice_of_strings_works() {
1811+
let v = vec!["mine".to_owned(), "yours".to_owned(), "his".to_owned()];
1812+
let b = v.clone().into_boxed_slice();
1813+
1814+
let encoded = b.encode();
1815+
assert_eq!(v.encode(), encoded);
1816+
1817+
assert_eq!(*b, *Box::<[String]>::decode(&mut &b.encode()[..]).unwrap());
1818+
}
1819+
17161820
#[test]
17171821
fn cow_works() {
17181822
let x = &[1u32, 2, 3, 4, 5, 6][..];

0 commit comments

Comments
 (0)