From ada503769c39248c9af7c9c1c84a03509ee21961 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Nov 2021 14:45:04 +0200 Subject: [PATCH 1/9] Simplified Decode for [T; N] --- src/codec.rs | 16 ++++++++-------- tests/chain-error.rs | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 4d3aebaa..6b3e8ceb 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -35,7 +35,6 @@ use core::num::{ NonZeroU64, NonZeroU128, }; -use arrayvec::ArrayVec; use byte_slice_cast::{AsByteSlice, AsMutByteSlice, ToMutByteSlice}; @@ -707,15 +706,16 @@ impl Encode for [T; N] { impl Decode for [T; N] { fn decode(input: &mut I) -> Result { - let mut array = ArrayVec::new(); - for _ in 0..N { - array.push(T::decode(input)?); - } + let mut data: ::core::mem::MaybeUninit<[T; N]> = unsafe { + ::core::mem::MaybeUninit::uninit().assume_init() + }; - match array.into_inner() { - Ok(a) => Ok(a), - Err(_) => panic!("We decode `N` elements; qed"), + let ptr = data.as_mut_ptr(); + for i in 0..N { + unsafe { ::core::ptr::addr_of_mut!((*ptr)[i]).write(T::decode(input)?); } } + + Ok(unsafe { data.assume_init() }) } } diff --git a/tests/chain-error.rs b/tests/chain-error.rs index d06f3582..6a106ae2 100644 --- a/tests/chain-error.rs +++ b/tests/chain-error.rs @@ -21,7 +21,7 @@ struct Wrapper(T); #[derive(Decode, Debug)] struct StructNamed { - foo: u16 + _foo: u16 } #[derive(Decode, Debug)] @@ -29,7 +29,7 @@ struct StructUnnamed(u16); #[derive(Decode, Debug)] enum E { - VariantNamed { foo: u16, }, + VariantNamed { _foo: u16, }, VariantUnnamed(u16), } @@ -37,7 +37,7 @@ enum E { fn full_error_struct_named() { let encoded = vec![0]; let err = r#"Could not decode `Wrapper.0`: - Could not decode `StructNamed::foo`: + Could not decode `StructNamed::_foo`: Not enough data to fill buffer "#; @@ -75,7 +75,7 @@ fn full_error_enum_unknown_variant() { #[test] fn full_error_enum_named_field() { let encoded = vec![0, 0]; - let err = r#"Could not decode `E::VariantNamed::foo`: + let err = r#"Could not decode `E::VariantNamed::_foo`: Not enough data to fill buffer "#; From b418b6eeed20b2f1d3a55b0473bf045b9f8538ba Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 18 Nov 2021 20:06:32 +0200 Subject: [PATCH 2/9] Robepop version --- src/codec.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 6b3e8ceb..9d83e02a 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -704,18 +704,26 @@ impl Encode for [T; N] { } } -impl Decode for [T; N] { +impl Decode for [T; N] { fn decode(input: &mut I) -> Result { - let mut data: ::core::mem::MaybeUninit<[T; N]> = unsafe { - ::core::mem::MaybeUninit::uninit().assume_init() - }; - - let ptr = data.as_mut_ptr(); - for i in 0..N { - unsafe { ::core::ptr::addr_of_mut!((*ptr)[i]).write(T::decode(input)?); } + let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); + // The following line coerces the pointer to the array to a pointer + // to the first array element which is equivalent. + let mut ptr = uninit.as_mut_ptr() as *mut T; + for _ in 0..N { + let decoded = T::decode(input)?; + // SAFETY: We do not read uninitialized array contents + // while initializing them. + unsafe { + ::core::ptr::write(ptr, decoded); + } + // SAFETY: Point to the next element after every iteration. + // We do this N times therefore this is safe. + ptr = unsafe { ptr.add(1) }; } - - Ok(unsafe { data.assume_init() }) + // SAFETY: All array elements have been initialized above. + let init = unsafe { uninit.assume_init() }; + Ok(init) } } From a5764a5af7910b5eb17d09ba31733f617de21ad2 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 18 Nov 2021 22:49:49 +0200 Subject: [PATCH 3/9] Added specific implementation for u8, i8, and other integers. Added tests. --- Cargo.lock | 7 ++++ Cargo.toml | 1 + src/codec.rs | 110 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 98 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f2c2f84..463f9977 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,6 +427,7 @@ dependencies = [ "generic-array", "impl-trait-for-tuples", "parity-scale-codec-derive", + "paste", "quickcheck", "serde", "serde_derive", @@ -444,6 +445,12 @@ dependencies = [ "syn", ] +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + [[package]] name = "plotters" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index 264f3fd1..58d2c7fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde_derive = { version = "1.0" } parity-scale-codec-derive = { path = "derive", default-features = false } quickcheck = "1.0" trybuild = "1.0.42" +paste = "1" [[bench]] name = "benches" diff --git a/src/codec.rs b/src/codec.rs index 9d83e02a..68feb00b 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -636,7 +636,75 @@ pub(crate) fn encode_slice_no_len(slice: &[T], de } } -/// Decode the slice (without prepended the len). +/// Decode the array. +/// +/// This is equivalent to decoding all the element one by one, but it is optimized for some types. +pub(crate) fn decode_array(input: &mut I) -> Result<[T; N], Error> { + fn general_array_decode(input: &mut I) -> Result<[T; N], Error> { + let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); + // The following line coerces the pointer to the array to a pointer + // to the first array element which is equivalent. + let mut ptr = uninit.as_mut_ptr() as *mut T; + for _ in 0..N { + let decoded = T::decode(input)?; + // SAFETY: We do not read uninitialized array contents + // while initializing them. + unsafe { + ::core::ptr::write(ptr, decoded); + } + // SAFETY: Point to the next element after every iteration. + // We do this N times therefore this is safe. + ptr = unsafe { ptr.add(1) }; + } + // SAFETY: All array elements have been initialized above. + let init = unsafe { uninit.assume_init() }; + Ok(init) + } + + macro_rules! decode { + ( u8 ) => {{ + let mut array: [u8; N] = [0; N]; + input.read(&mut array[..])?; + let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; + let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; + ::core::mem::forget(array); + Ok(typed) + }}; + ( i8 ) => {{ + let mut array: [i8; N] = [0; N]; + let bytes = unsafe { mem::transmute::<&mut [i8], &mut [u8]>(&mut array[..]) }; + input.read(bytes)?; + + let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; + let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; + ::core::mem::forget(array); + Ok(typed) + }}; + ( $ty:ty ) => {{ + if cfg!(target_endian = "little") { + let mut array: [$ty; N] = [0; N]; + let bytes = <[$ty] as AsMutByteSlice<$ty>>::as_mut_byte_slice(&mut array[..]); + input.read(bytes)?; + let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; + let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; + ::core::mem::forget(array); + Ok(typed) + } else { + general_array_decode(input) + } + }}; + } + + with_type_info! { + ::TYPE_INFO, + decode, + { + general_array_decode(input) + }, + } +} + +/// Decode the vec (without prepended the len). /// /// This is equivalent to decode all elements one by one, but it is optimized in some /// situation. @@ -704,26 +772,9 @@ impl Encode for [T; N] { } } -impl Decode for [T; N] { +impl Decode for [T; N] { fn decode(input: &mut I) -> Result { - let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); - // The following line coerces the pointer to the array to a pointer - // to the first array element which is equivalent. - let mut ptr = uninit.as_mut_ptr() as *mut T; - for _ in 0..N { - let decoded = T::decode(input)?; - // SAFETY: We do not read uninitialized array contents - // while initializing them. - unsafe { - ::core::ptr::write(ptr, decoded); - } - // SAFETY: Point to the next element after every iteration. - // We do this N times therefore this is safe. - ptr = unsafe { ptr.add(1) }; - } - // SAFETY: All array elements have been initialized above. - let init = unsafe { uninit.assume_init() }; - Ok(init) + decode_array(input) } } @@ -1655,6 +1706,25 @@ mod tests { <[u32; 0]>::decode(&mut &encoded[..]).unwrap(); } + + macro_rules! array_encode_and_decode { + ( $( $name:ty ),* $(,)? ) => { + $( + paste::item! { + #[test] + fn []() { + let data: [$name; 32] = [123; 32]; + let encoded = data.encode(); + let decoded: [$name; 32] = Decode::decode(&mut &encoded[..]).unwrap(); + assert_eq!(decoded, data); + } + } + )* + } + } + + array_encode_and_decode!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128); + fn test_encoded_size(val: impl Encode) { let length = val.using_encoded(|v| v.len()); From c401cec7d80b65f55211f6e6ae66a617ecf99365 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 18 Nov 2021 22:51:56 +0200 Subject: [PATCH 4/9] Removed Debug(added for testing) --- src/codec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 68feb00b..917c12b4 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -639,8 +639,8 @@ pub(crate) fn encode_slice_no_len(slice: &[T], de /// Decode the array. /// /// This is equivalent to decoding all the element one by one, but it is optimized for some types. -pub(crate) fn decode_array(input: &mut I) -> Result<[T; N], Error> { - fn general_array_decode(input: &mut I) -> Result<[T; N], Error> { +pub(crate) fn decode_array(input: &mut I) -> Result<[T; N], Error> { + fn general_array_decode(input: &mut I) -> Result<[T; N], Error> { let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); // The following line coerces the pointer to the array to a pointer // to the first array element which is equivalent. @@ -772,7 +772,7 @@ impl Encode for [T; N] { } } -impl Decode for [T; N] { +impl Decode for [T; N] { fn decode(input: &mut I) -> Result { decode_array(input) } From eb7ba47fed0d11ae18e50dbb2668681567af7be2 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 18 Nov 2021 22:59:51 +0200 Subject: [PATCH 5/9] Add #[inline] --- src/codec.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/codec.rs b/src/codec.rs index 917c12b4..a78326d8 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -639,7 +639,9 @@ pub(crate) fn encode_slice_no_len(slice: &[T], de /// Decode the array. /// /// This is equivalent to decoding all the element one by one, but it is optimized for some types. +#[inline] pub(crate) fn decode_array(input: &mut I) -> Result<[T; N], Error> { + #[inline] fn general_array_decode(input: &mut I) -> Result<[T; N], Error> { let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); // The following line coerces the pointer to the array to a pointer @@ -773,6 +775,7 @@ impl Encode for [T; N] { } impl Decode for [T; N] { + #[inline] fn decode(input: &mut I) -> Result { decode_array(input) } From 5c094c08e6e38065f2eb881d936e68cad46207b0 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 19 Nov 2021 00:49:38 +0200 Subject: [PATCH 6/9] Fixed tests with a new rust. Fixed comments in PR --- src/codec.rs | 32 ++++++++++--------- tests/max_encoded_len_ui/crate_str.stderr | 10 +++--- .../max_encoded_len_ui/incomplete_attr.stderr | 10 +++--- .../missing_crate_specifier.stderr | 10 +++--- tests/max_encoded_len_ui/not_encode.stderr | 10 +++--- 5 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index a78326d8..34ba94a9 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -20,8 +20,13 @@ use core::{ iter::FromIterator, marker::PhantomData, mem, + mem::{ + MaybeUninit, + ManuallyDrop, + }, ops::{Deref, Range, RangeInclusive}, time::Duration, + ptr, }; use core::num::{ NonZeroI8, @@ -643,7 +648,7 @@ pub(crate) fn encode_slice_no_len(slice: &[T], de pub(crate) fn decode_array(input: &mut I) -> Result<[T; N], Error> { #[inline] fn general_array_decode(input: &mut I) -> Result<[T; N], Error> { - let mut uninit = <::core::mem::MaybeUninit<[T; N]>>::uninit(); + let mut uninit = >::uninit(); // The following line coerces the pointer to the array to a pointer // to the first array element which is equivalent. let mut ptr = uninit.as_mut_ptr() as *mut T; @@ -652,7 +657,7 @@ pub(crate) fn decode_array(input: &mut I) - // SAFETY: We do not read uninitialized array contents // while initializing them. unsafe { - ::core::ptr::write(ptr, decoded); + ptr::write(ptr, decoded); } // SAFETY: Point to the next element after every iteration. // We do this N times therefore this is safe. @@ -665,31 +670,28 @@ pub(crate) fn decode_array(input: &mut I) - macro_rules! decode { ( u8 ) => {{ - let mut array: [u8; N] = [0; N]; + let mut array: ManuallyDrop<[u8; N]> = ManuallyDrop::new([0; N]); input.read(&mut array[..])?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; - let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; - ::core::mem::forget(array); + let typed: [T; N] = unsafe { ptr::read(ref_typed) }; Ok(typed) }}; ( i8 ) => {{ - let mut array: [i8; N] = [0; N]; + let mut array: ManuallyDrop<[i8; N]> = ManuallyDrop::new([0; N]); let bytes = unsafe { mem::transmute::<&mut [i8], &mut [u8]>(&mut array[..]) }; input.read(bytes)?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; - let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; - ::core::mem::forget(array); + let typed: [T; N] = unsafe { ptr::read(ref_typed) }; Ok(typed) }}; ( $ty:ty ) => {{ if cfg!(target_endian = "little") { - let mut array: [$ty; N] = [0; N]; + let mut array: ManuallyDrop<[$ty; N]> = ManuallyDrop::new([0; N]); let bytes = <[$ty] as AsMutByteSlice<$ty>>::as_mut_byte_slice(&mut array[..]); input.read(bytes)?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; - let typed: [T; N] = unsafe { ::core::ptr::read(ref_typed) }; - ::core::mem::forget(array); + let typed: [T; N] = unsafe { ptr::read(ref_typed) }; Ok(typed) } else { general_array_decode(input) @@ -1710,10 +1712,10 @@ mod tests { } - macro_rules! array_encode_and_decode { + macro_rules! test_array_encode_and_decode { ( $( $name:ty ),* $(,)? ) => { $( - paste::item! { + paste::item! { #[test] fn []() { let data: [$name; 32] = [123; 32]; @@ -1721,12 +1723,12 @@ mod tests { let decoded: [$name; 32] = Decode::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, data); } - } + } )* } } - array_encode_and_decode!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128); + test_array_encode_and_decode!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128); fn test_encoded_size(val: impl Encode) { let length = val.using_encoded(|v| v.len()); diff --git a/tests/max_encoded_len_ui/crate_str.stderr b/tests/max_encoded_len_ui/crate_str.stderr index 07048141..9dd9ca01 100644 --- a/tests/max_encoded_len_ui/crate_str.stderr +++ b/tests/max_encoded_len_ui/crate_str.stderr @@ -10,10 +10,10 @@ error[E0277]: the trait bound `Example: WrapperTypeEncode` is not satisfied 3 | #[derive(Encode, MaxEncodedLen)] | ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example` | - ::: $WORKSPACE/src/max_encoded_len.rs - | - | pub trait MaxEncodedLen: Encode { - | ------ required by this bound in `MaxEncodedLen` - | = note: required because of the requirements on the impl of `Encode` for `Example` +note: required by a bound in `MaxEncodedLen` + --> $DIR/max_encoded_len.rs:28:26 + | +28 | pub trait MaxEncodedLen: Encode { + | ^^^^^^ required by this bound in `MaxEncodedLen` = note: this error originates in the derive macro `MaxEncodedLen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/max_encoded_len_ui/incomplete_attr.stderr b/tests/max_encoded_len_ui/incomplete_attr.stderr index 560fdfe1..ddf6c0eb 100644 --- a/tests/max_encoded_len_ui/incomplete_attr.stderr +++ b/tests/max_encoded_len_ui/incomplete_attr.stderr @@ -10,10 +10,10 @@ error[E0277]: the trait bound `Example: WrapperTypeEncode` is not satisfied 3 | #[derive(Encode, MaxEncodedLen)] | ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example` | - ::: $WORKSPACE/src/max_encoded_len.rs - | - | pub trait MaxEncodedLen: Encode { - | ------ required by this bound in `MaxEncodedLen` - | = note: required because of the requirements on the impl of `Encode` for `Example` +note: required by a bound in `MaxEncodedLen` + --> $DIR/max_encoded_len.rs:28:26 + | +28 | pub trait MaxEncodedLen: Encode { + | ^^^^^^ required by this bound in `MaxEncodedLen` = note: this error originates in the derive macro `MaxEncodedLen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/max_encoded_len_ui/missing_crate_specifier.stderr b/tests/max_encoded_len_ui/missing_crate_specifier.stderr index 22ae84bd..42409804 100644 --- a/tests/max_encoded_len_ui/missing_crate_specifier.stderr +++ b/tests/max_encoded_len_ui/missing_crate_specifier.stderr @@ -10,10 +10,10 @@ error[E0277]: the trait bound `Example: WrapperTypeEncode` is not satisfied 3 | #[derive(Encode, MaxEncodedLen)] | ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example` | - ::: $WORKSPACE/src/max_encoded_len.rs - | - | pub trait MaxEncodedLen: Encode { - | ------ required by this bound in `MaxEncodedLen` - | = note: required because of the requirements on the impl of `Encode` for `Example` +note: required by a bound in `MaxEncodedLen` + --> $DIR/max_encoded_len.rs:28:26 + | +28 | pub trait MaxEncodedLen: Encode { + | ^^^^^^ required by this bound in `MaxEncodedLen` = note: this error originates in the derive macro `MaxEncodedLen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/max_encoded_len_ui/not_encode.stderr b/tests/max_encoded_len_ui/not_encode.stderr index 29a82d7f..442fc969 100644 --- a/tests/max_encoded_len_ui/not_encode.stderr +++ b/tests/max_encoded_len_ui/not_encode.stderr @@ -4,10 +4,10 @@ error[E0277]: the trait bound `NotEncode: WrapperTypeEncode` is not satisfied 3 | #[derive(MaxEncodedLen)] | ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NotEncode` | - ::: $WORKSPACE/src/max_encoded_len.rs - | - | pub trait MaxEncodedLen: Encode { - | ------ required by this bound in `MaxEncodedLen` - | = note: required because of the requirements on the impl of `Encode` for `NotEncode` +note: required by a bound in `MaxEncodedLen` + --> $DIR/max_encoded_len.rs:28:26 + | +28 | pub trait MaxEncodedLen: Encode { + | ^^^^^^ required by this bound in `MaxEncodedLen` = note: this error originates in the derive macro `MaxEncodedLen` (in Nightly builds, run with -Z macro-backtrace for more info) From 226be2240d57327554c43ba3375ea1525d561a25 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 19 Nov 2021 01:15:26 +0200 Subject: [PATCH 7/9] Fixed formatting --- src/codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 34ba94a9..15eb9259 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -674,7 +674,7 @@ pub(crate) fn decode_array(input: &mut I) - input.read(&mut array[..])?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; let typed: [T; N] = unsafe { ptr::read(ref_typed) }; - Ok(typed) + Ok(typed) }}; ( i8 ) => {{ let mut array: ManuallyDrop<[i8; N]> = ManuallyDrop::new([0; N]); @@ -683,7 +683,7 @@ pub(crate) fn decode_array(input: &mut I) - let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; let typed: [T; N] = unsafe { ptr::read(ref_typed) }; - Ok(typed) + Ok(typed) }}; ( $ty:ty ) => {{ if cfg!(target_endian = "little") { From 4137711d99c44fe1b7d2090816c0073569b09b84 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Sun, 21 Nov 2021 01:16:56 +0200 Subject: [PATCH 8/9] Added comment to describe complexity of transmute=) --- src/codec.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 15eb9259..6345e190 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -655,12 +655,12 @@ pub(crate) fn decode_array(input: &mut I) - for _ in 0..N { let decoded = T::decode(input)?; // SAFETY: We do not read uninitialized array contents - // while initializing them. + // while initializing them. unsafe { ptr::write(ptr, decoded); } // SAFETY: Point to the next element after every iteration. - // We do this N times therefore this is safe. + // We do this N times therefore this is safe. ptr = unsafe { ptr.add(1) }; } // SAFETY: All array elements have been initialized above. @@ -668,6 +668,19 @@ pub(crate) fn decode_array(input: &mut I) - Ok(init) } + // Description for the code below. + // It is not possible to transmute `[u8; N]` into `[T; N]` due to this issue: + // https://github.com/rust-lang/rust/issues/61956 + // + // Workaround: Transmute `&[u8; N]` into `&[T; N]` and interpret that reference as value. + // ``` + // let mut array: ManuallyDrop<[u8; N]> = ManuallyDrop::new([0; N]); + // let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; + // let typed: [T; N] = unsafe { ptr::read(ref_typed) }; + // Here `array` and `typed` points on the same memory. + // Function returns `typed` -> it is not dropped, but `array` will be dropped. + // To avoid that `array` under the `ManuallyDrop`. + // ``` macro_rules! decode { ( u8 ) => {{ let mut array: ManuallyDrop<[u8; N]> = ManuallyDrop::new([0; N]); From 6a3ebfc189d05d341d25dbb8874e83080146e684 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Sun, 21 Nov 2021 12:02:54 +0200 Subject: [PATCH 9/9] Use `forget` instead of `ManualDrop` to avoid memory leak in case of error --- src/codec.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/codec.rs b/src/codec.rs index 6345e190..4e59cb8e 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -22,7 +22,7 @@ use core::{ mem, mem::{ MaybeUninit, - ManuallyDrop, + forget, }, ops::{Deref, Range, RangeInclusive}, time::Duration, @@ -674,37 +674,41 @@ pub(crate) fn decode_array(input: &mut I) - // // Workaround: Transmute `&[u8; N]` into `&[T; N]` and interpret that reference as value. // ``` - // let mut array: ManuallyDrop<[u8; N]> = ManuallyDrop::new([0; N]); + // let mut array: [u8; N] = [0; N]; // let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; // let typed: [T; N] = unsafe { ptr::read(ref_typed) }; + // forget(array); // Here `array` and `typed` points on the same memory. // Function returns `typed` -> it is not dropped, but `array` will be dropped. - // To avoid that `array` under the `ManuallyDrop`. + // To avoid that `array` should be forgotten. // ``` macro_rules! decode { ( u8 ) => {{ - let mut array: ManuallyDrop<[u8; N]> = ManuallyDrop::new([0; N]); + let mut array: [u8; N] = [0; N]; input.read(&mut array[..])?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; let typed: [T; N] = unsafe { ptr::read(ref_typed) }; + forget(array); Ok(typed) }}; ( i8 ) => {{ - let mut array: ManuallyDrop<[i8; N]> = ManuallyDrop::new([0; N]); + let mut array: [i8; N] = [0; N]; let bytes = unsafe { mem::transmute::<&mut [i8], &mut [u8]>(&mut array[..]) }; input.read(bytes)?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; let typed: [T; N] = unsafe { ptr::read(ref_typed) }; + forget(array); Ok(typed) }}; ( $ty:ty ) => {{ if cfg!(target_endian = "little") { - let mut array: ManuallyDrop<[$ty; N]> = ManuallyDrop::new([0; N]); + let mut array: [$ty; N] = [0; N]; let bytes = <[$ty] as AsMutByteSlice<$ty>>::as_mut_byte_slice(&mut array[..]); input.read(bytes)?; let ref_typed: &[T; N] = unsafe { mem::transmute(&array) }; let typed: [T; N] = unsafe { ptr::read(ref_typed) }; + forget(array); Ok(typed) } else { general_array_decode(input)