mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 00:42:42 +00:00
Use SmallVec in Bitfield
This commit is contained in:
@@ -18,6 +18,7 @@ eth2_ssz = "0.4.1"
|
|||||||
typenum = "1.12.0"
|
typenum = "1.12.0"
|
||||||
arbitrary = { version = "1.0", features = ["derive"], optional = true }
|
arbitrary = { version = "1.0", features = ["derive"], optional = true }
|
||||||
derivative = "2.1.1"
|
derivative = "2.1.1"
|
||||||
|
smallvec = "1.8.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1.0.58"
|
serde_json = "1.0.58"
|
||||||
|
|||||||
@@ -5,10 +5,17 @@ use derivative::Derivative;
|
|||||||
use eth2_serde_utils::hex::{encode as hex_encode, PrefixedHexVisitor};
|
use eth2_serde_utils::hex::{encode as hex_encode, PrefixedHexVisitor};
|
||||||
use serde::de::{Deserialize, Deserializer};
|
use serde::de::{Deserialize, Deserializer};
|
||||||
use serde::ser::{Serialize, Serializer};
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
use smallvec::{smallvec, SmallVec, ToSmallVec};
|
||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use tree_hash::Hash256;
|
use tree_hash::Hash256;
|
||||||
use typenum::Unsigned;
|
use typenum::Unsigned;
|
||||||
|
|
||||||
|
/// Maximum number of bytes to store on the stack in a bitfield's `SmallVec`.
|
||||||
|
///
|
||||||
|
/// The default of 32 bytes is enough to take us through to ~500K validators, as the byte length of
|
||||||
|
/// attestation bitfields is roughly `N // 32 slots // 64 committes // 8 bits`.
|
||||||
|
pub const SMALLVEC_LEN: usize = 32;
|
||||||
|
|
||||||
/// A marker trait applied to `Variable` and `Fixed` that defines the behaviour of a `Bitfield`.
|
/// A marker trait applied to `Variable` and `Fixed` that defines the behaviour of a `Bitfield`.
|
||||||
pub trait BitfieldBehaviour: Clone {}
|
pub trait BitfieldBehaviour: Clone {}
|
||||||
|
|
||||||
@@ -91,7 +98,7 @@ pub type BitVector<N> = Bitfield<Fixed<N>>;
|
|||||||
#[derive(Clone, Debug, Derivative)]
|
#[derive(Clone, Debug, Derivative)]
|
||||||
#[derivative(PartialEq, Hash(bound = ""))]
|
#[derivative(PartialEq, Hash(bound = ""))]
|
||||||
pub struct Bitfield<T> {
|
pub struct Bitfield<T> {
|
||||||
bytes: Vec<u8>,
|
bytes: SmallVec<[u8; SMALLVEC_LEN]>,
|
||||||
len: usize,
|
len: usize,
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
@@ -106,7 +113,7 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
|
|||||||
pub fn with_capacity(num_bits: usize) -> Result<Self, Error> {
|
pub fn with_capacity(num_bits: usize) -> Result<Self, Error> {
|
||||||
if num_bits <= N::to_usize() {
|
if num_bits <= N::to_usize() {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
bytes: vec![0; bytes_for_bit_len(num_bits)],
|
bytes: smallvec![0; bytes_for_bit_len(num_bits)],
|
||||||
len: num_bits,
|
len: num_bits,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
})
|
})
|
||||||
@@ -138,7 +145,7 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(b.into_bytes(), vec![0b0001_0000]);
|
/// assert_eq!(b.into_bytes(), vec![0b0001_0000]);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
pub fn into_bytes(self) -> SmallVec<[u8; SMALLVEC_LEN]> {
|
||||||
let len = self.len();
|
let len = self.len();
|
||||||
let mut bytes = self.bytes;
|
let mut bytes = self.bytes;
|
||||||
|
|
||||||
@@ -163,7 +170,7 @@ impl<N: Unsigned + Clone> Bitfield<Variable<N>> {
|
|||||||
/// produces (SSZ).
|
/// produces (SSZ).
|
||||||
///
|
///
|
||||||
/// Returns `None` if `bytes` are not a valid encoding.
|
/// Returns `None` if `bytes` are not a valid encoding.
|
||||||
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
|
pub fn from_bytes(bytes: SmallVec<[u8; SMALLVEC_LEN]>) -> Result<Self, Error> {
|
||||||
let bytes_len = bytes.len();
|
let bytes_len = bytes.len();
|
||||||
let mut initial_bitfield: Bitfield<Variable<N>> = {
|
let mut initial_bitfield: Bitfield<Variable<N>> = {
|
||||||
let num_bits = bytes.len() * 8;
|
let num_bits = bytes.len() * 8;
|
||||||
@@ -235,7 +242,7 @@ impl<N: Unsigned + Clone> Bitfield<Fixed<N>> {
|
|||||||
/// All bits are initialized to `false`.
|
/// All bits are initialized to `false`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
bytes: vec![0; bytes_for_bit_len(Self::capacity())],
|
bytes: smallvec![0; bytes_for_bit_len(Self::capacity())],
|
||||||
len: Self::capacity(),
|
len: Self::capacity(),
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
@@ -258,7 +265,7 @@ impl<N: Unsigned + Clone> Bitfield<Fixed<N>> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(BitVector4::new().into_bytes(), vec![0b0000_0000]);
|
/// assert_eq!(BitVector4::new().into_bytes(), vec![0b0000_0000]);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn into_bytes(self) -> Vec<u8> {
|
pub fn into_bytes(self) -> SmallVec<[u8; SMALLVEC_LEN]> {
|
||||||
self.into_raw_bytes()
|
self.into_raw_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +273,7 @@ impl<N: Unsigned + Clone> Bitfield<Fixed<N>> {
|
|||||||
/// produces (SSZ).
|
/// produces (SSZ).
|
||||||
///
|
///
|
||||||
/// Returns `None` if `bytes` are not a valid encoding.
|
/// Returns `None` if `bytes` are not a valid encoding.
|
||||||
pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, Error> {
|
pub fn from_bytes(bytes: SmallVec<[u8; SMALLVEC_LEN]>) -> Result<Self, Error> {
|
||||||
Self::from_raw_bytes(bytes, Self::capacity())
|
Self::from_raw_bytes(bytes, Self::capacity())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +362,7 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the underlying bytes representation of the bitfield.
|
/// Returns the underlying bytes representation of the bitfield.
|
||||||
pub fn into_raw_bytes(self) -> Vec<u8> {
|
pub fn into_raw_bytes(self) -> SmallVec<[u8; SMALLVEC_LEN]> {
|
||||||
self.bytes
|
self.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,9 +379,9 @@ impl<T: BitfieldBehaviour> Bitfield<T> {
|
|||||||
/// - `bytes` is not the minimal required bytes to represent a bitfield of `bit_len` bits.
|
/// - `bytes` is not the minimal required bytes to represent a bitfield of `bit_len` bits.
|
||||||
/// - `bit_len` is not a multiple of 8 and `bytes` contains set bits that are higher than, or
|
/// - `bit_len` is not a multiple of 8 and `bytes` contains set bits that are higher than, or
|
||||||
/// equal to `bit_len`.
|
/// equal to `bit_len`.
|
||||||
fn from_raw_bytes(bytes: Vec<u8>, bit_len: usize) -> Result<Self, Error> {
|
fn from_raw_bytes(bytes: SmallVec<[u8; SMALLVEC_LEN]>, bit_len: usize) -> Result<Self, Error> {
|
||||||
if bit_len == 0 {
|
if bit_len == 0 {
|
||||||
if bytes.len() == 1 && bytes == [0] {
|
if bytes.len() == 1 && bytes[0] == 0 {
|
||||||
// A bitfield with `bit_len` 0 can only be represented by a single zero byte.
|
// A bitfield with `bit_len` 0 can only be represented by a single zero byte.
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
bytes,
|
bytes,
|
||||||
@@ -512,7 +519,7 @@ impl<N: Unsigned + Clone> Encode for Bitfield<Variable<N>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||||
buf.append(&mut self.clone().into_bytes())
|
buf.extend_from_slice(&self.clone().into_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +529,7 @@ impl<N: Unsigned + Clone> Decode for Bitfield<Variable<N>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
||||||
Self::from_bytes(bytes.to_vec()).map_err(|e| {
|
Self::from_bytes(bytes.to_smallvec()).map_err(|e| {
|
||||||
ssz::DecodeError::BytesInvalid(format!("BitList failed to decode: {:?}", e))
|
ssz::DecodeError::BytesInvalid(format!("BitList failed to decode: {:?}", e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -542,7 +549,7 @@ impl<N: Unsigned + Clone> Encode for Bitfield<Fixed<N>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
fn ssz_append(&self, buf: &mut Vec<u8>) {
|
||||||
buf.append(&mut self.clone().into_bytes())
|
buf.extend_from_slice(&self.clone().into_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,7 +563,7 @@ impl<N: Unsigned + Clone> Decode for Bitfield<Fixed<N>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
||||||
Self::from_bytes(bytes.to_vec()).map_err(|e| {
|
Self::from_bytes(bytes.to_smallvec()).map_err(|e| {
|
||||||
ssz::DecodeError::BytesInvalid(format!("BitVector failed to decode: {:?}", e))
|
ssz::DecodeError::BytesInvalid(format!("BitVector failed to decode: {:?}", e))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ superstruct = "0.4.0"
|
|||||||
serde_json = "1.0.74"
|
serde_json = "1.0.74"
|
||||||
milhouse = { path = "../../../milhouse", optional = true }
|
milhouse = { path = "../../../milhouse", optional = true }
|
||||||
rpds = "0.11.0"
|
rpds = "0.11.0"
|
||||||
|
smallvec = "1.8.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3.3"
|
criterion = "0.3.3"
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{BitList, BitVector, Unsigned};
|
use crate::{BitList, BitVector, Unsigned};
|
||||||
|
use smallvec::smallvec;
|
||||||
|
|
||||||
impl<N: Unsigned + Clone> TestRandom for BitList<N> {
|
impl<N: Unsigned + Clone> TestRandom for BitList<N> {
|
||||||
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
||||||
let mut raw_bytes = vec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||||
rng.fill_bytes(&mut raw_bytes);
|
rng.fill_bytes(&mut raw_bytes);
|
||||||
Self::from_bytes(raw_bytes).expect("we generate a valid BitList")
|
Self::from_bytes(raw_bytes).expect("we generate a valid BitList")
|
||||||
}
|
}
|
||||||
@@ -11,7 +12,7 @@ impl<N: Unsigned + Clone> TestRandom for BitList<N> {
|
|||||||
|
|
||||||
impl<N: Unsigned + Clone> TestRandom for BitVector<N> {
|
impl<N: Unsigned + Clone> TestRandom for BitVector<N> {
|
||||||
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
fn random_for_test(rng: &mut impl RngCore) -> Self {
|
||||||
let mut raw_bytes = vec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
|
||||||
rng.fill_bytes(&mut raw_bytes);
|
rng.fill_bytes(&mut raw_bytes);
|
||||||
Self::from_bytes(raw_bytes).expect("we generate a valid BitVector")
|
Self::from_bytes(raw_bytes).expect("we generate a valid BitVector")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user