Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BitVec: Improve the encoding and consolidate the implementations #327

Merged
merged 6 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "parity-scale-codec"
description = "SCALE - Simple Concatenating Aggregated Little Endians"
version = "3.1.0"
version = "3.1.1"
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
repository = "https://github.com/paritytech/parity-scale-codec"
Expand Down
73 changes: 39 additions & 34 deletions src/bit_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,13 @@
//! `BitVec` specific serialization.

use bitvec::{
vec::BitVec, store::BitStore, order::BitOrder, slice::BitSlice, boxed::BitBox,
vec::BitVec, store::BitStore, order::BitOrder, slice::BitSlice, boxed::BitBox, view::BitView,
};
use crate::{
EncodeLike, Encode, Decode, Input, Output, Error, Compact,
codec::{decode_vec_with_len, encode_slice_no_len},
EncodeLike, Encode, Decode, Input, Output, Error, Compact, codec::decode_vec_with_len,
};

impl<O: BitOrder, T: BitStore> Encode for BitSlice<T, O>
where
T::Mem: Encode
{
Comment on lines -25 to -28
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This technically is semver breaking, but I would go this route. BitVec already required the same bound.

impl<O: BitOrder, T: BitStore + Encode> Encode for BitSlice<T, O> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
let bits = self.len();
assert!(
Expand All @@ -34,23 +30,18 @@ impl<O: BitOrder, T: BitStore> Encode for BitSlice<T, O>
);
Compact(bits as u32).encode_to(dest);

for element in self.domain() {
// Iterate over chunks
for chunk in self.chunks(core::mem::size_of::<T>() * 8) {
let mut element = T::ZERO;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be moved outside the loop perhaps and re-used? Or will the compiler re-use element anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be wrong. As the last chunk could have less bits than the ones before, we could end up with an element having bits set that should not be set. I mean that should be fixed on the decoding side. However, I also don't see any real performance issue with having the primitive type inside the loop.

element.view_bits_mut::<O>()[..chunk.len()].copy_from_bitslice(chunk);
ordian marked this conversation as resolved.
Show resolved Hide resolved
element.encode_to(dest);
}
}
}

impl<O: BitOrder, T: BitStore + Encode> Encode for BitVec<T, O> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
let bits = self.len();
assert!(
bits <= ARCH32BIT_BITSLICE_MAX_BITS,
"Attempted to encode a BitVec with too many bits.",
);
Compact(bits as u32).encode_to(dest);

let slice = self.as_raw_slice();
encode_slice_no_len(slice, dest)
self.as_bitslice().encode_to(dest)
}
}

Expand Down Expand Up @@ -84,15 +75,7 @@ impl<O: BitOrder, T: BitStore + Decode> Decode for BitVec<T, O> {

impl<O: BitOrder, T: BitStore + Encode> Encode for BitBox<T, O> {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
let bits = self.len();
assert!(
bits <= ARCH32BIT_BITSLICE_MAX_BITS,
"Attempted to encode a BitBox with too many bits.",
);
Compact(bits as u32).encode_to(dest);

let slice = self.as_raw_slice();
encode_slice_no_len(slice, dest)
self.as_bitslice().encode_to(dest)
}
}

Expand All @@ -107,8 +90,8 @@ impl<O: BitOrder, T: BitStore + Decode> Decode for BitBox<T, O> {
#[cfg(test)]
mod tests {
use super::*;
use bitvec::{bitvec, order::Msb0};
use crate::codec::MAX_PREALLOCATION;
use bitvec::{bitvec, order::{Msb0, Lsb0}};
use crate::{codec::MAX_PREALLOCATION, CompactLen};

macro_rules! test_data {
($inner_type:ident) => (
Expand Down Expand Up @@ -150,8 +133,9 @@ mod tests {
let encoded = v.encode();
assert_eq!(*v, BitVec::<u8, Msb0>::decode(&mut &encoded[..]).unwrap());

let encoded = v.as_bitslice().encode();
assert_eq!(*v, BitVec::<u8, Msb0>::decode(&mut &encoded[..]).unwrap());
let elements = bitvec::mem::elts::<u8>(v.len());
let compact_len = Compact::compact_len(&(v.len() as u32));
assert_eq!(compact_len + elements, encoded.len(), "{}", v);
}
}

Expand All @@ -161,8 +145,9 @@ mod tests {
let encoded = v.encode();
assert_eq!(*v, BitVec::<u16, Msb0>::decode(&mut &encoded[..]).unwrap());

let encoded = v.as_bitslice().encode();
assert_eq!(*v, BitVec::<u16, Msb0>::decode(&mut &encoded[..]).unwrap());
let elements = bitvec::mem::elts::<u16>(v.len());
let compact_len = Compact::compact_len(&(v.len() as u32));
assert_eq!(compact_len + elements * 2, encoded.len(), "{}", v);
}
}

Expand All @@ -172,8 +157,9 @@ mod tests {
let encoded = v.encode();
assert_eq!(*v, BitVec::<u32, Msb0>::decode(&mut &encoded[..]).unwrap());

let encoded = v.as_bitslice().encode();
assert_eq!(*v, BitVec::<u32, Msb0>::decode(&mut &encoded[..]).unwrap());
let elements = bitvec::mem::elts::<u32>(v.len());
let compact_len = Compact::compact_len(&(v.len() as u32));
assert_eq!(compact_len + elements * 4, encoded.len(), "{}", v);
}
}

Expand All @@ -182,6 +168,10 @@ mod tests {
for v in &test_data!(u64) {
let encoded = v.encode();
assert_eq!(*v, BitVec::<u64, Msb0>::decode(&mut &encoded[..]).unwrap());

let elements = bitvec::mem::elts::<u64>(v.len());
let compact_len = Compact::compact_len(&(v.len() as u32));
assert_eq!(compact_len + elements * 8, encoded.len(), "{}", v);
}
}

Expand All @@ -203,4 +193,19 @@ mod tests {
let decoded = BitBox::<u8, Msb0>::decode(&mut &encoded[..]).unwrap();
assert_eq!(bb, decoded);
}

#[test]
fn bitvec_u8_encodes_as_expected() {
let cases = vec![
(bitvec![u8, Lsb0; 0, 0, 1, 1].encode(), (Compact(4u32), 0b00001100u8).encode()),
(bitvec![u8, Lsb0; 0, 1, 1, 1].encode(), (Compact(4u32), 0b00001110u8).encode()),
(bitvec![u8, Lsb0; 1, 1, 1, 1].encode(), (Compact(4u32), 0b00001111u8).encode()),
(bitvec![u8, Lsb0; 1, 1, 1, 1, 1].encode(), (Compact(5u32), 0b00011111u8).encode()),
(bitvec![u8, Lsb0; 1, 1, 1, 1, 1, 0].encode(), (Compact(6u32), 0b00011111u8).encode()),
];

for (idx, (actual, expected)) in cases.into_iter().enumerate() {
assert_eq!(actual, expected, "case at index {} failed; encodings differ", idx);
}
}
}
34 changes: 33 additions & 1 deletion tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#[cfg(not(feature="derive"))]
use parity_scale_codec_derive::{Encode, Decode};
use parity_scale_codec::{
Encode, Decode, HasCompact, Compact, EncodeAsRef, CompactAs, Error, Output,
Encode, Decode, HasCompact, Compact, EncodeAsRef, CompactAs, Error, Output, DecodeAll
};
use serde_derive::{Serialize, Deserialize};

Expand Down Expand Up @@ -586,3 +586,35 @@ fn custom_trait_bound() {

Something::<NotEncode, u32>::decode(&mut &encoded[..]).unwrap();
}

#[test]
dvdplm marked this conversation as resolved.
Show resolved Hide resolved
#[cfg(feature = "bit-vec")]
fn bit_vec_works() {
use bitvec::prelude::*;

// Try some fancy stuff
let original_vec = bitvec![u8, Msb0; 1; 8];
let mut original_vec_clone = original_vec.clone();
original_vec_clone = original_vec_clone.split_off(5);
original_vec_clone.push(true);
original_vec_clone.push(true);
original_vec_clone.push(true);
original_vec_clone.push(true);
original_vec_clone.push(true);

assert_eq!(original_vec, original_vec_clone);

#[derive(Decode, Encode, PartialEq, Debug)]
struct MyStruct {
v: BitVec<u8, Msb0>,
x: u8,
}

let v1 = MyStruct { v: original_vec, x: 42 }.encode();
let v2 = MyStruct { v: original_vec_clone, x: 42 }.encode();
assert_eq!(v1, v2);

let v1 = MyStruct::decode(&mut &v1[..]).unwrap();
let v2 = MyStruct::decode_all(&mut &v2[..]).unwrap();
assert_eq!(v1.x, v2.x);
}