diff --git a/docs/spec.md b/docs/spec.md index 58ab56c..6cd8471 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -2,7 +2,7 @@ *NOTE*: Serialization is done by `bincode_derive` by default. If you enable the `serde` flag, serialization with `serde-derive` is supported as well. `serde-derive` has the same guarantees as `bincode_derive` for now. -Related issue: https://github.com/serde-rs/serde/issues/1756#issuecomment-689682123 +Related issue: ## Endian @@ -17,12 +17,14 @@ All basic numeric types will be encoded based on the configured [IntEncoding](#I All floating point types will take up exactly 4 (for `f32`) or 8 (for `f64`) bytes. All tuples have no additional bytes, and are encoded in their specified order, e.g. -```rs +```rust +use bincode::config::Configuration; + let tuple = (u32::min_value(), i32::max_value()); // 8 bytes -let encoded = bincode::encode_to_vec_with_options(&tuple, Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(tuple, Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ 0, 0, 0, 0, // 4 bytes for first type: u32 - 255, 255, 255, 255 // 4 bytes for second type: i32 + 255, 255, 255, 127 // 4 bytes for second type: i32 ]); ``` @@ -56,7 +58,9 @@ Enums are encoded with their variant first, followed by optionally the variant f Both named and unnamed fields are serialized with their values only, and therefor encode to the same value. -```rs +```rust +use bincode::config::Configuration; + #[derive(bincode::Encode)] pub enum SomeEnum { A, @@ -65,23 +69,23 @@ pub enum SomeEnum { } // SomeEnum::A -let encoded = bincode::encode_to_vec_with_options(&SomeEnum::A, Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(SomeEnum::A, Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ 0, 0, 0, 0, // first variant, A // no extra bytes because A has no fields ]); // SomeEnum::B(0) -let encoded = bincode::encode_to_vec_with_options(&SomeEnum::B(0), Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(SomeEnum::B(0), Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ - 0, 0, 0, 1, // first variant, B + 1, 0, 0, 0, // first variant, B 0, 0, 0, 0 // B has 1 unnamed field, which is an u32, so 4 bytes ]); // SomeEnum::C { value: 0u32 } -let encoded = bincode::encode_to_vec_with_options(&SomeEnum::C { value: 0u32 }, Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(SomeEnum::C { value: 0u32 }, Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ - 0, 0, 0, 2, // first variant, C + 2, 0, 0, 0, // first variant, C 0, 0, 0, 0 // C has 1 named field which is a u32, so 4 bytes ]); ``` @@ -92,16 +96,17 @@ Collections are encoded with their length value first, following by each entry o **note**: fixed array length do not have their `len` encoded. See [Arrays](#arrays) -```rs +```rust +use bincode::config::Configuration; let list = vec![ 0u8, 1u8, 2u8 ]; -let encoded = bincode::encode_to_vec_with_options(&list, Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(list, Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ - 0, 0, 0, 0, 0, 0, 0, 3, // length of 3u64 + 3, 0, 0, 0, 0, 0, 0, 0, // length of 3u64 0, // entry 0 1, // entry 1 2, // entry 2 @@ -114,30 +119,39 @@ This also applies to e.g. `HashMap`, where each entry is a [tuple](#basic-types) Both `String` and `&str` are treated as a `Vec`. See [Collections](#collections) for more information. -```rs +```rust +use bincode::config::Configuration; + let str = "Hello"; // Could also be `String::new(...)` -let encoded = bincode::encode_to_vec_with_options(&list, Options::default().with_fixint_encoding()).unwrap(); +let encoded = bincode::encode_to_vec_with_config(str, Configuration::legacy()).unwrap(); assert_eq!(encoded.as_slice(), &[ - 0, 0, 0, 0, 0, 0, 0, 5, // length of the string, 5 bytes + 5, 0, 0, 0, 0, 0, 0, 0, // length of the string, 5 bytes b'H', b'e', b'l', b'l', b'o' ]); ``` # Arrays -Arrays are encoded *without* a length. +Arrays are encoded *with* a length by default. + +```rust +use bincode::config::Configuration; -```rs let arr: [u8; 5] = [10, 20, 30, 40, 50]; -let encoded = bincode::encode_to_vec(&list).unwrap(); -assert_eq!(encoded.as_slice(), &[10, 20, 30, 40 50]); +let encoded = bincode::encode_to_vec_with_config(arr, Configuration::legacy()).unwrap(); +assert_eq!(encoded.as_slice(), &[ + 5, 0, 0, 0, 0, 0, 0, 0, // The length, as a u64 + 10, 20, 30, 40, 50, // the bytes +]); ``` This applies to any type `T` that implements `Encodabl`/`Decodabl` -```rs -#[derive(bincode::Encodabl)] +```rust +use bincode::config::Configuration; + +#[derive(bincode::Encode)] struct Foo { first: u8, second: u8 @@ -154,7 +168,11 @@ let arr: [Foo; 2] = [ }, ]; -let encoded = bincode::encode_to_vec(&list).unwrap(); -assert_eq!(encoded.as_slice(), &[10, 20, 30, 40]); +let encoded = bincode::encode_to_vec_with_config(arr, Configuration::legacy()).unwrap(); +assert_eq!(encoded.as_slice(), &[ + 2, 0, 0, 0, 0, 0, 0, 0, // Length of the array + 10, 20, // First Foo + 30, 40, // Second Foo +]); ``` diff --git a/src/de/impls.rs b/src/de/impls.rs index 3dccb96..1ef50c5 100644 --- a/src/de/impls.rs +++ b/src/de/impls.rs @@ -382,9 +382,11 @@ impl<'a, 'de: 'a> BorrowDecode<'de> for &'a str { } } -impl Decode for [u8; N] { +impl Decode for [T; N] +where + T: Decode + Sized, +{ fn decode(mut decoder: D) -> Result { - let mut array = [0u8; N]; if !D::C::SKIP_FIXED_ARRAY_LENGTH { let length = usize::decode(&mut decoder)?; if length != N { @@ -394,8 +396,38 @@ impl Decode for [u8; N] { }); } } - decoder.reader().read(&mut array)?; - Ok(array) + + // 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 + + use core::mem::MaybeUninit; + + // Create an uninitialized array of `MaybeUninit`. The `assume_init` is + // safe because the type we are claiming to have initialized here is a + // bunch of `MaybeUninit`s, which do not require initialization. + let mut data: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + + // Dropping a `MaybeUninit` does nothing. Thus using raw pointer + // assignment instead of `ptr::write` does not cause the old + // uninitialized value to be dropped. Also if there is a panic during + // this loop, we have a memory leak, but there is no memory safety + // issue. + for elem in &mut data[..] { + elem.write(T::decode(&mut decoder)?); + } + + // Everything is initialized. Transmute the array to the + // initialized type. + + // BlockedTODO: https://github.com/rust-lang/rust/issues/61956 + // let result = unsafe { transmute::<_, [T; N]>(data) }; + + // Const generics don't work well with transmute at the moment + // The above issue recommends doing the following + let ptr = &mut data as *mut _ as *mut [T; N]; + let res = unsafe { ptr.read() }; + core::mem::forget(data); + Ok(res) } } diff --git a/src/enc/impls.rs b/src/enc/impls.rs index 82645df..6685839 100644 --- a/src/enc/impls.rs +++ b/src/enc/impls.rs @@ -338,12 +338,18 @@ impl Encode for &'_ str { } } -impl Encode for [u8; N] { +impl Encode for [T; N] +where + T: Encode, +{ fn encode(&self, mut encoder: E) -> Result<(), EncodeError> { if !E::C::SKIP_FIXED_ARRAY_LENGTH { N.encode(&mut encoder)?; } - encoder.writer().write(self) + for item in self.iter() { + item.encode(&mut encoder)?; + } + Ok(()) } } diff --git a/src/lib.rs b/src/lib.rs index 4c6f837..c158754 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,3 +91,7 @@ pub fn decode_with_config<'__de, D: de::BorrowDecode<'__de>, C: Config>( let mut decoder = de::DecoderImpl::<_, C>::new(reader, _config); D::borrow_decode(&mut decoder) } + +pub mod spec { + #![doc = include_str!("../docs/spec.md")] +} diff --git a/tests/utils.rs b/tests/utils.rs index 5f4f280..f0c4316 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -7,7 +7,7 @@ where C: Config, CMP: Fn(&V, &V) -> bool, { - let mut buffer = [0u8; 1024]; + let mut buffer = [0u8; 2048]; let len = bincode::encode_into_slice_with_config(&element, &mut buffer, config).unwrap(); println!( "{:?}: {:?} ({:?})",