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

use scale-bits for BitSequence decoding etc and enable WASM test #24

Merged
merged 3 commits into from
Sep 21, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ jobs:
command: check
args: --all-targets --no-default-features --workspace

wasm:
name: Check WASM compatibility
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
target: wasm32-unknown-unknown
override: true

- name: Rust Cache
uses: Swatinem/rust-cache@v1.3.0

- name: Check all features
uses: actions-rs/cargo@v1.0.3
with:
command: check
args: --all-targets --all-features --target wasm32-unknown-unknown
Copy link
Member

Choose a reason for hiding this comment

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

this looks funny but --all-targets actually imply that is compiles for test and bench I guess?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah that's my understanding of it! It's not really important to have here but I thought we may as well try and push for everything to compile to wasm when possible.. shrug :)


fmt:
name: Cargo fmt
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ The format is based on [Keep a Changelog].

[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/

## 0.6.0

Here we move to `scale_bits` from `bitvec` to handle our encode/decode logic and provide a simple type to decode bits into. We also now add a WASM CI test, and will expect this crate (potentially via features in the future) to be WASM compatible.

### Changed

- Use `scale-bits` for `BitSequence` decoding etc and enable WASM test. ([#24](https://github.com/paritytech/scale-value/pull/24)

## 0.5.0

The main user-facing change in this release is that the SCALE bytes for compact-encoded _structs_ previously decoded into `Composite` values with the same degree of nesting (ie if the compact encoded value was nested within 2 structs, it would be decoded into a value nested inside 2 `Composite`s). This nesting has now been removed, and the compact value is returned directly. This should have no impact on re-encoding the Value, since encoding into single-field composites will just delegrate to the inner field type as needed.
Expand Down
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "scale-value"
version = "0.5.0"
version = "0.6.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"

Expand All @@ -22,16 +22,17 @@ from_string = [
# Enable serde support for serializing/deserializing Values.
serde = [
"dep:serde",
"scale-bits/serde",
]

[dependencies]
bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] }
serde = { version = "1.0.124", features = ["derive"], optional = true }
frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] }
thiserror = "1.0.24"
scale-info = { version = "2.0.0", default-features = false, features = ["bit-vec", "std"] }
scale-decode = { version = "0.3.0" }
scale-info = { version = "2.0.0", default-features = false, features = ["std"] }
scale-decode = "0.4.0"
scale-bits = "0.3.0"
either = "1.6.1"
yap = { version = "0.7.2", optional = true }

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ pub mod serde {
/// assert_eq!(value, new_value.remove_context());
/// ```
pub mod scale {
pub use crate::scale_impls::{BitSequenceError, DecodeError, EncodeError, TypeId};
pub use crate::scale_impls::{DecodeError, EncodeError, TypeId};
pub use scale_info::PortableRegistry;

/// Attempt to decode some SCALE encoded bytes into a value, by providing a pointer
Expand Down
92 changes: 0 additions & 92 deletions src/scale_impls/bit_sequence.rs

This file was deleted.

45 changes: 5 additions & 40 deletions src/scale_impls/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,8 @@ impl scale_decode::visitor::Visitor for ValueVisitor {
value: &mut scale_decode::visitor::BitSequence,
type_id: scale_decode::visitor::TypeId,
) -> Result<Self::Value, Self::Error> {
Ok(Value {
value: ValueDef::BitSequence(value.decode_bitsequence()?.to_u8_lsb0()),
context: type_id.into(),
})
let bits: Result<_, _> = value.decode()?.collect();
Ok(Value { value: ValueDef::BitSequence(bits?), context: type_id.into() })
}
fn visit_str(
self,
Expand Down Expand Up @@ -440,42 +438,9 @@ mod test {

#[test]
fn decode_bit_sequence() {
use bitvec::{
bitvec,
order::{Lsb0, Msb0},
};
use scale_bits::bits;

encode_decode_check(
bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u8, Msb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u16, Lsb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u16, Msb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u32, Lsb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u32, Msb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u64, Lsb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
encode_decode_check(
bitvec![u64, Msb0; 0, 1, 1, 0, 1, 0],
Value::bit_sequence(bitvec![u8, Lsb0; 0, 1, 1, 0, 1, 0]),
);
// scale-decode already tests this more thoroughly:
encode_decode_check(bits![0, 1, 1, 0, 1, 0], Value::bit_sequence(bits![0, 1, 1, 0, 1, 0]));
}
}
97 changes: 35 additions & 62 deletions src/scale_impls/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{
bit_sequence::{get_bitsequence_details, BitOrderTy, BitSequenceError, BitStoreTy},
type_id::TypeId,
ScaleTypeDef as TypeDef,
};
use super::{type_id::TypeId, ScaleTypeDef as TypeDef};
use crate::value::{Composite, Primitive, Value, ValueDef, Variant};
use bitvec::{
order::{Lsb0, Msb0},
vec::BitVec,
};
use codec::{Compact, Encode};
use scale_info::{
form::PortableForm, Field, PortableRegistry, TypeDefArray, TypeDefBitSequence, TypeDefCompact,
TypeDefComposite, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, TypeDefVariant,
};

/// An error encoding a [`Value`] into SCALE bytes.
#[derive(Debug, Clone, thiserror::Error, PartialEq)]
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
pub enum EncodeError<T> {
/// The composite type we're trying to encode is the wrong length for the type we're trying to encode it into.
#[error("Composite type is the wrong length; expected length is {expected_len}, but got {}", actual.len())]
Expand Down Expand Up @@ -77,6 +69,9 @@ pub enum EncodeError<T> {
CannotCompactEncode(TypeId),
}

/// An error that can occur attempting to encode a bit sequence.
pub type BitSequenceError = scale_bits::scale::format::FromMetadataError;

/// Attempt to SCALE Encode a Value according to the [`TypeId`] and
/// [`PortableRegistry`] provided.
pub fn encode_value_as_type<T: Clone, Id: Into<TypeId>>(
Expand Down Expand Up @@ -552,10 +547,16 @@ fn encode_bitsequence_value<T: Clone>(
types: &PortableRegistry,
bytes: &mut Vec<u8>,
) -> Result<(), EncodeError<T>> {
// First, try to convert whatever we have into a vec of bools:
let bools: Vec<bool> = match &value.value {
ValueDef::BitSequence(bits) => bits.iter().by_vals().collect(),
let format =
scale_bits::Format::from_metadata(ty, types).map_err(EncodeError::BitSequenceError)?;

match &value.value {
ValueDef::BitSequence(bits) => {
// Bits can be encoded easily enough:
scale_bits::encode_using_format_to(bits.iter(), format, bytes)
}
ValueDef::Composite(Composite::Unnamed(vals)) => {
// For composite bools we need to find and store them first:
let mut bools = Vec::with_capacity(vals.len());
for val in vals {
match val.value {
Expand All @@ -568,39 +569,12 @@ fn encode_bitsequence_value<T: Clone>(
}
}
}
bools
// And then we can encode them:
scale_bits::encode_using_format_to(bools.into_iter(), format, bytes)
}
_ => return Err(EncodeError::WrongShape { actual: value.clone(), expected: type_id }),
};

// next, turn those bools into a bit sequence of the expected shape.
match get_bitsequence_details(ty, types).map_err(EncodeError::BitSequenceError)? {
(BitStoreTy::U8, BitOrderTy::Lsb0) => {
bools.into_iter().collect::<BitVec<u8, Lsb0>>().encode_to(bytes);
}
(BitStoreTy::U16, BitOrderTy::Lsb0) => {
bools.into_iter().collect::<BitVec<u16, Lsb0>>().encode_to(bytes);
}
(BitStoreTy::U32, BitOrderTy::Lsb0) => {
bools.into_iter().collect::<BitVec<u32, Lsb0>>().encode_to(bytes);
}
(BitStoreTy::U64, BitOrderTy::Lsb0) => {
bools.into_iter().collect::<BitVec<u64, Lsb0>>().encode_to(bytes);
}
(BitStoreTy::U8, BitOrderTy::Msb0) => {
bools.into_iter().collect::<BitVec<u8, Msb0>>().encode_to(bytes);
}
(BitStoreTy::U16, BitOrderTy::Msb0) => {
bools.into_iter().collect::<BitVec<u16, Msb0>>().encode_to(bytes);
}
(BitStoreTy::U32, BitOrderTy::Msb0) => {
bools.into_iter().collect::<BitVec<u32, Msb0>>().encode_to(bytes);
}
(BitStoreTy::U64, BitOrderTy::Msb0) => {
bools.into_iter().collect::<BitVec<u64, Msb0>>().encode_to(bytes);
}
}

Ok(())
}

Expand Down Expand Up @@ -766,27 +740,26 @@ mod test {

#[test]
fn can_encode_bitvecs() {
use bitvec::{
bitvec,
order::{Lsb0, Msb0},
};

let bits = bitvec![u8, Lsb0; 0, 1, 1, 0, 0, 1];
let value = Value::bit_sequence(bits);

// Support encoding our Value to the different underlying formats that bitvec can have:

assert_can_encode_to_type(value.clone(), bitvec![u8, Lsb0; 0, 1, 1, 0, 0, 1]);
assert_can_encode_to_type(value.clone(), bitvec![u8, Msb0; 0, 1, 1, 0, 0, 1]);
use scale_bits::bits;

assert_can_encode_to_type(value.clone(), bitvec![u16, Lsb0; 0, 1, 1, 0, 0, 1]);
assert_can_encode_to_type(value.clone(), bitvec![u16, Msb0; 0, 1, 1, 0, 0, 1]);

assert_can_encode_to_type(value.clone(), bitvec![u32, Lsb0; 0, 1, 1, 0, 0, 1]);
assert_can_encode_to_type(value.clone(), bitvec![u32, Msb0; 0, 1, 1, 0, 0, 1]);

assert_can_encode_to_type(value.clone(), bitvec![u64, Lsb0; 0, 1, 1, 0, 0, 1]);
assert_can_encode_to_type(value, bitvec![u64, Msb0; 0, 1, 1, 0, 0, 1]);
// We have more thorough tests of bitvec encoding in scale-bits.
// Here we just do a basic confirmation that bool composites or
// bitsequences encode to the bits we'd expect.
assert_can_encode_to_type(
Value::bit_sequence(bits![0, 1, 1, 0, 0, 1]),
bits![0, 1, 1, 0, 0, 1],
);
assert_can_encode_to_type(
Value::unnamed_composite(vec![
Value::bool(false),
Value::bool(true),
Value::bool(true),
Value::bool(false),
Value::bool(false),
Value::bool(true),
]),
bits![0, 1, 1, 0, 0, 1],
);
}

#[test]
Expand Down
2 changes: 0 additions & 2 deletions src/scale_impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

mod bit_sequence;
mod decode;
mod encode;
mod type_id;
Expand All @@ -24,7 +23,6 @@ type ScaleTypeId = scale_info::interner::UntrackedSymbol<std::any::TypeId>; // e
/// The portable version of [`scale_info::TypeDef`]
type ScaleTypeDef = scale_info::TypeDef<scale_info::form::PortableForm>;

pub use bit_sequence::BitSequenceError;
pub use decode::{decode_value_as_type, DecodeError};
pub use encode::{encode_value_as_type, EncodeError};

Expand Down