Included spec.md into cargo's documentation, fixed the issues, changed the [u8; N] implementations to [T; N]

This commit is contained in:
Victor Koenders 2021-10-19 11:04:56 +02:00
parent 435e030182
commit 151edf46d3
5 changed files with 91 additions and 31 deletions

View File

@ -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. *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: <https://github.com/serde-rs/serde/issues/1756#issuecomment-689682123>
## Endian ## 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 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. 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 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(), &[ assert_eq!(encoded.as_slice(), &[
0, 0, 0, 0, // 4 bytes for first type: u32 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. 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)] #[derive(bincode::Encode)]
pub enum SomeEnum { pub enum SomeEnum {
A, A,
@ -65,23 +69,23 @@ pub enum SomeEnum {
} }
// SomeEnum::A // 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(), &[ assert_eq!(encoded.as_slice(), &[
0, 0, 0, 0, // first variant, A 0, 0, 0, 0, // first variant, A
// no extra bytes because A has no fields // no extra bytes because A has no fields
]); ]);
// SomeEnum::B(0) // 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(), &[ 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 0, 0, 0, 0 // B has 1 unnamed field, which is an u32, so 4 bytes
]); ]);
// SomeEnum::C { value: 0u32 } // 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(), &[ 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 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) **note**: fixed array length do not have their `len` encoded. See [Arrays](#arrays)
```rs ```rust
use bincode::config::Configuration;
let list = vec![ let list = vec![
0u8, 0u8,
1u8, 1u8,
2u8 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(), &[ 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 0, // entry 0
1, // entry 1 1, // entry 1
2, // entry 2 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<u8>`. See [Collections](#collections) for more information. Both `String` and `&str` are treated as a `Vec<u8>`. See [Collections](#collections) for more information.
```rs ```rust
use bincode::config::Configuration;
let str = "Hello"; // Could also be `String::new(...)` 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(), &[ 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' b'H', b'e', b'l', b'l', b'o'
]); ]);
``` ```
# Arrays # 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 arr: [u8; 5] = [10, 20, 30, 40, 50];
let encoded = bincode::encode_to_vec(&list).unwrap(); let encoded = bincode::encode_to_vec_with_config(arr, Configuration::legacy()).unwrap();
assert_eq!(encoded.as_slice(), &[10, 20, 30, 40 50]); 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` This applies to any type `T` that implements `Encodabl`/`Decodabl`
```rs ```rust
#[derive(bincode::Encodabl)] use bincode::config::Configuration;
#[derive(bincode::Encode)]
struct Foo { struct Foo {
first: u8, first: u8,
second: u8 second: u8
@ -154,7 +168,11 @@ let arr: [Foo; 2] = [
}, },
]; ];
let encoded = bincode::encode_to_vec(&list).unwrap(); let encoded = bincode::encode_to_vec_with_config(arr, Configuration::legacy()).unwrap();
assert_eq!(encoded.as_slice(), &[10, 20, 30, 40]); 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
]);
``` ```

View File

@ -382,9 +382,11 @@ impl<'a, 'de: 'a> BorrowDecode<'de> for &'a str {
} }
} }
impl<const N: usize> Decode for [u8; N] { impl<T, const N: usize> Decode for [T; N]
where
T: Decode + Sized,
{
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> { fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
let mut array = [0u8; N];
if !D::C::SKIP_FIXED_ARRAY_LENGTH { if !D::C::SKIP_FIXED_ARRAY_LENGTH {
let length = usize::decode(&mut decoder)?; let length = usize::decode(&mut decoder)?;
if length != N { if length != N {
@ -394,8 +396,38 @@ impl<const N: usize> 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<T>; 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)
} }
} }

View File

@ -338,12 +338,18 @@ impl Encode for &'_ str {
} }
} }
impl<const N: usize> Encode for [u8; N] { impl<T, const N: usize> Encode for [T; N]
where
T: Encode,
{
fn encode<E: Encoder>(&self, mut encoder: E) -> Result<(), EncodeError> { fn encode<E: Encoder>(&self, mut encoder: E) -> Result<(), EncodeError> {
if !E::C::SKIP_FIXED_ARRAY_LENGTH { if !E::C::SKIP_FIXED_ARRAY_LENGTH {
N.encode(&mut encoder)?; N.encode(&mut encoder)?;
} }
encoder.writer().write(self) for item in self.iter() {
item.encode(&mut encoder)?;
}
Ok(())
} }
} }

View File

@ -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); let mut decoder = de::DecoderImpl::<_, C>::new(reader, _config);
D::borrow_decode(&mut decoder) D::borrow_decode(&mut decoder)
} }
pub mod spec {
#![doc = include_str!("../docs/spec.md")]
}

View File

@ -7,7 +7,7 @@ where
C: Config, C: Config,
CMP: Fn(&V, &V) -> bool, 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(); let len = bincode::encode_into_slice_with_config(&element, &mut buffer, config).unwrap();
println!( println!(
"{:?}: {:?} ({:?})", "{:?}: {:?} ({:?})",