mirror of https://git.sr.ht/~stygianentity/bincode
Made the Decode of [T; N] properly drop all instances of T when an error has occured
This commit is contained in:
parent
151edf46d3
commit
07b3c8cd76
|
|
@ -400,31 +400,55 @@ where
|
||||||
// Safety: this is taken from the example of https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html
|
// Safety: this is taken from the example of https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html
|
||||||
// and is therefor assumed to be safe
|
// and is therefor assumed to be safe
|
||||||
|
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||||
|
|
||||||
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
||||||
// safe because the type we are claiming to have initialized here is a
|
// safe because the type we are claiming to have initialized here is a
|
||||||
// bunch of `MaybeUninit`s, which do not require initialization.
|
// bunch of `MaybeUninit`s, which do not require initialization.
|
||||||
let mut data: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut data: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
|
|
||||||
// Dropping a `MaybeUninit` does nothing. Thus using raw pointer
|
for (idx, elem) in data.iter_mut().enumerate() {
|
||||||
// assignment instead of `ptr::write` does not cause the old
|
match T::decode(&mut decoder) {
|
||||||
// uninitialized value to be dropped. Also if there is a panic during
|
Ok(t) => {
|
||||||
// this loop, we have a memory leak, but there is no memory safety
|
elem.write(t);
|
||||||
// issue.
|
}
|
||||||
for elem in &mut data[..] {
|
Err(e) => {
|
||||||
elem.write(T::decode(&mut decoder)?);
|
// We encountered an error, clean up all previous entries
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Safety: we know we've written up to `idx` items
|
||||||
|
for item in &mut data[..idx] {
|
||||||
|
// Safety: We know the `item` is written with a valid value of `T`.
|
||||||
|
// And we know that `&mut item` won't be used any more after this.
|
||||||
|
ManuallyDrop::drop(&mut *(item as *mut _ as *mut ManuallyDrop<T>));
|
||||||
|
}
|
||||||
|
// make sure `data` isn't dropped twice
|
||||||
|
core::mem::forget(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything is initialized. Transmute the array to the
|
// Everything is initialized. Transmute the array to the
|
||||||
// initialized type.
|
// initialized type.
|
||||||
|
|
||||||
// BlockedTODO: https://github.com/rust-lang/rust/issues/61956
|
// BlockedTODO: https://github.com/rust-lang/rust/issues/61956
|
||||||
|
// This does not work at the moment:
|
||||||
// let result = unsafe { transmute::<_, [T; N]>(data) };
|
// let result = unsafe { transmute::<_, [T; N]>(data) };
|
||||||
|
|
||||||
|
// Alternatively we can use this:
|
||||||
|
// BlockedTODO: https://github.com/rust-lang/rust/issues/80908
|
||||||
|
|
||||||
// Const generics don't work well with transmute at the moment
|
// Const generics don't work well with transmute at the moment
|
||||||
// The above issue recommends doing the following
|
// The first issue above recommends doing the following:
|
||||||
let ptr = &mut data as *mut _ as *mut [T; N];
|
let ptr = &mut data as *mut _ as *mut [T; N];
|
||||||
|
|
||||||
|
// Safety: we know this is an array with every value written,
|
||||||
|
// and that the array is valid.
|
||||||
|
// As well as the original `data` that will be forgotten so we won't get
|
||||||
|
// multiple (mutable) references to the same memory
|
||||||
let res = unsafe { ptr.read() };
|
let res = unsafe { ptr.read() };
|
||||||
core::mem::forget(data);
|
core::mem::forget(data);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,9 @@ pub fn decode_with_config<'__de, D: de::BorrowDecode<'__de>, C: Config>(
|
||||||
D::borrow_decode(&mut decoder)
|
D::borrow_decode(&mut decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Currently our doctests fail when trying to include the specs because the specs depend on `derive` and `alloc`.
|
||||||
|
// But we want to have the specs in the docs always
|
||||||
|
#[cfg(all(feature = "alloc", feature = "derive"))]
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
#![doc = include_str!("../docs/spec.md")]
|
#![doc = include_str!("../docs/spec.md")]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue