Commit b980954c authored by Stanislav Tkach's avatar Stanislav Tkach Committed by asynchronous rob
Browse files

Support BitVec, BitBox and BitSlice (#81)

* Support BitVec

* Move bitvec related parts into a separate file

* Generic Encode implementation

* Make decode implementation generic and add more tests

* Add BitBox and BitSlice implementations

* More detailed byte slice casting error description

* Fix formatting
parent 06a6bdd7
......@@ -23,7 +23,7 @@ cache:
test:rust:stable:
stage: test
script:
- time cargo test --verbose --all
- time cargo test --verbose --all --features bit-vec
only:
- triggers
- tags
......@@ -37,7 +37,7 @@ test:rust:stable:
bench:rust:nightly:
stage: test
script:
- time cargo +nightly bench
- time cargo +nightly bench --features bit-vec
only:
- triggers
- tags
......@@ -53,7 +53,7 @@ bench:rust:nightly:
build:linux:ubuntu:amd64:
stage: build
script:
- cargo build --verbose --release
- cargo build --verbose --release --features bit-vec
only:
- master
- tags
......
......@@ -8,6 +8,16 @@ dependencies = [
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitvec"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byte-slice-cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nodrop"
version = "0.1.13"
......@@ -18,6 +28,8 @@ name = "parity-codec"
version = "3.3.0"
dependencies = [
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"bitvec 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byte-slice-cast 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.3.0",
"serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -97,6 +109,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
"checksum bitvec 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c28d4291b516ccfbb897d45de3c468c135e6af7c4f1f1aacfaae0a5bc2e6ea2c"
"checksum byte-slice-cast 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d6eec63fe0fb4f9f0f8011d32ab1586c123f1248c930a35237ff4ef08beaf1"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum proc-macro-crate 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4c6cf4e5b00300d151dfffae39f529dfa5188f42eeb14201229aa420d6aad10c"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
......
......@@ -12,6 +12,8 @@ edition = "2018"
arrayvec = { version = "0.4", default-features = false, features = ["array-sizes-33-128", "array-sizes-129-255"] }
serde = { version = "1.0", optional = true }
parity-codec-derive = { path = "derive", version = "3.3", default-features = false, optional = true }
bitvec = { version = "0.11", default-features = false, features = ["alloc"], optional = true }
byte-slice-cast = { version = "0.3", optional = true }
[dev-dependencies]
serde_derive = { version = "1.0" }
......@@ -20,7 +22,8 @@ parity-codec-derive = { path = "derive", version = "3.3", default-features = fal
[features]
default = ["std"]
derive = ["parity-codec-derive"]
std = ["serde"]
std = ["serde", "bitvec/std"]
bit-vec = ["bitvec", "byte-slice-cast"]
# WARNING: DO _NOT_ USE THIS FEATURE IF YOU ARE WORKING ON CONSENSUS CODE!*
#
......
// Copyright 2019 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! `BitVec` specific serialization.
use std::mem;
use bitvec::{vec::BitVec, bits::Bits, cursor::Cursor, slice::BitSlice, boxed::BitBox};
use byte_slice_cast::{AsByteSlice, ToByteSlice, FromByteSlice, Error as FromByteSliceError};
use crate::codec::{Encode, Decode, Input, Output, Compact, Error};
impl From<FromByteSliceError> for Error {
fn from(e: FromByteSliceError) -> Error {
match e {
FromByteSliceError::AlignmentMismatch {..} => "failed to cast from byte slice: alignment mismatch".into(),
FromByteSliceError::LengthMismatch {..} => "failed to cast from byte slice: length mismatch".into(),
}
}
}
impl<C: Cursor, T: Bits + ToByteSlice> Encode for BitSlice<C, T> {
fn encode_to<W: Output>(&self, dest: &mut W) {
let len = self.len();
assert!(len <= u32::max_value() as usize, "Attempted to serialize a collection with too many elements.");
Compact(len as u32).encode_to(dest);
dest.write(self.as_slice().as_byte_slice());
}
}
impl<C: Cursor, T: Bits + ToByteSlice> Encode for BitVec<C, T> {
fn encode_to<W: Output>(&self, dest: &mut W) {
self.as_bitslice().encode_to(dest)
}
}
impl<C: Cursor, T: Bits + FromByteSlice> Decode for BitVec<C, T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
<Compact<u32>>::decode(input).and_then(move |Compact(bits)| {
let bits = bits as usize;
let mut vec = vec![0; required_bytes::<T>(bits)];
input.read(&mut vec)?;
Ok(if vec.is_empty() {
Self::new()
} else {
let mut result = Self::from_slice(T::from_byte_slice(&vec)?);
assert!(bits <= result.len());
unsafe { result.set_len(bits); }
result
})
})
}
}
impl<C: Cursor, T: Bits + ToByteSlice> Encode for BitBox<C, T> {
fn encode_to<W: Output>(&self, dest: &mut W) {
self.as_bitslice().encode_to(dest)
}
}
impl<C: Cursor, T: Bits + FromByteSlice> Decode for BitBox<C, T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
Ok(Self::from_bitslice(BitVec::<C, T>::decode(input)?.as_bitslice()))
}
}
// Calculates bytes required to store given amount of `bits` as if they were stored in the array of `T`.
fn required_bytes<T>(bits: usize) -> usize {
let element_bits = mem::size_of::<T>() * 8;
(bits + element_bits - 1) / element_bits * mem::size_of::<T>()
}
#[cfg(test)]
mod tests {
use super::*;
use bitvec::{bitvec, cursor::BigEndian};
macro_rules! test_data {
($inner_type: ty) => (
[
BitVec::<BigEndian, $inner_type>::new(),
bitvec![BigEndian, $inner_type; 0],
bitvec![BigEndian, $inner_type; 1],
bitvec![BigEndian, $inner_type; 0, 0],
bitvec![BigEndian, $inner_type; 1, 0],
bitvec![BigEndian, $inner_type; 0, 1],
bitvec![BigEndian, $inner_type; 1, 1],
bitvec![BigEndian, $inner_type; 1, 0, 1],
bitvec![BigEndian, $inner_type; 0, 1, 0, 1, 0, 1, 1],
bitvec![BigEndian, $inner_type; 0, 1, 0, 1, 0, 1, 1, 0],
bitvec![BigEndian, $inner_type; 1, 1, 0, 1, 0, 1, 1, 0, 1],
bitvec![BigEndian, $inner_type; 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0],
bitvec![BigEndian, $inner_type; 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0],
bitvec![BigEndian, $inner_type; 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0],
bitvec![BigEndian, $inner_type; 0; 15],
bitvec![BigEndian, $inner_type; 1; 16],
bitvec![BigEndian, $inner_type; 0; 17],
bitvec![BigEndian, $inner_type; 1; 31],
bitvec![BigEndian, $inner_type; 0; 32],
bitvec![BigEndian, $inner_type; 1; 33],
bitvec![BigEndian, $inner_type; 0; 63],
bitvec![BigEndian, $inner_type; 1; 64],
bitvec![BigEndian, $inner_type; 0; 65],
]
)
}
#[test]
fn required_bytes_test() {
assert_eq!(0, required_bytes::<u8>(0));
assert_eq!(1, required_bytes::<u8>(1));
assert_eq!(1, required_bytes::<u8>(7));
assert_eq!(1, required_bytes::<u8>(8));
assert_eq!(2, required_bytes::<u8>(9));
assert_eq!(0, required_bytes::<u16>(0));
assert_eq!(2, required_bytes::<u16>(1));
assert_eq!(2, required_bytes::<u16>(15));
assert_eq!(2, required_bytes::<u16>(16));
assert_eq!(4, required_bytes::<u16>(17));
assert_eq!(0, required_bytes::<u32>(0));
assert_eq!(4, required_bytes::<u32>(1));
assert_eq!(4, required_bytes::<u32>(31));
assert_eq!(4, required_bytes::<u32>(32));
assert_eq!(8, required_bytes::<u32>(33));
assert_eq!(0, required_bytes::<u64>(0));
assert_eq!(8, required_bytes::<u64>(1));
assert_eq!(8, required_bytes::<u64>(63));
assert_eq!(8, required_bytes::<u64>(64));
assert_eq!(16, required_bytes::<u64>(65));
}
#[test]
fn bitvec_u8() {
for v in &test_data!(u8) {
let encoded = v.encode();
assert_eq!(*v, BitVec::<BigEndian, u8>::decode(&mut &encoded[..]).unwrap());
}
}
#[test]
fn bitvec_u16() {
for v in &test_data!(u16) {
let encoded = v.encode();
assert_eq!(*v, BitVec::<BigEndian, u16>::decode(&mut &encoded[..]).unwrap());
}
}
#[test]
fn bitvec_u32() {
for v in &test_data!(u32) {
let encoded = v.encode();
assert_eq!(*v, BitVec::<BigEndian, u32>::decode(&mut &encoded[..]).unwrap());
}
}
#[test]
fn bitvec_u64() {
for v in &test_data!(u64) {
let encoded = v.encode();
assert_eq!(*v, BitVec::<BigEndian, u64>::decode(&mut &encoded[..]).unwrap());
}
}
#[test]
fn bitslice() {
let data: &[u8] = &[0x69];
let slice: &BitSlice = data.into();
let encoded = slice.encode();
let decoded = BitVec::<BigEndian, u8>::decode(&mut &encoded[..]).unwrap();
assert_eq!(slice, decoded.as_bitslice());
}
#[test]
fn bitbox() {
let data: &[u8] = &[5, 10];
let bb: BitBox = data.into();
let encoded = bb.encode();
let decoded = BitBox::<BigEndian, u8>::decode(&mut &encoded[..]).unwrap();
assert_eq!(bb, decoded);
}
}
......@@ -1879,8 +1879,6 @@ mod tests {
}
}
#[test]
fn should_avoid_overlapping_definition() {
check_bound!(
......
......@@ -58,6 +58,8 @@ pub mod alloc {
mod codec;
mod joiner;
mod keyedvec;
#[cfg(feature = "bit-vec")]
mod bit_vec;
pub use self::codec::{Input, Output, Error, Encode, Decode, Codec, Compact, HasCompact, EncodeAsRef, CompactAs, EncodeAppend};
pub use self::joiner::Joiner;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment