From 67714510f5b86b1d106ecfc065cceecf6a7e36fb Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Mon, 28 Sep 2020 16:12:55 +0200 Subject: [PATCH] Add bitfield helpers for new store --- libraries/persistent_store/src/bitfield.rs | 370 +++++++++++++++++++++ libraries/persistent_store/src/lib.rs | 4 + libraries/persistent_store/src/store.rs | 63 ++++ 3 files changed, 437 insertions(+) create mode 100644 libraries/persistent_store/src/bitfield.rs create mode 100644 libraries/persistent_store/src/store.rs diff --git a/libraries/persistent_store/src/bitfield.rs b/libraries/persistent_store/src/bitfield.rs new file mode 100644 index 0000000..81af3fc --- /dev/null +++ b/libraries/persistent_store/src/bitfield.rs @@ -0,0 +1,370 @@ +// Copyright 2019-2020 Google LLC +// +// 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. + +//! Helps manipulate bit fields in 32-bits words. +// TODO(ia0): Remove when the module is used. +#![cfg_attr(not(test), allow(dead_code, unused_macros))] + +use crate::{StoreError, StoreResult}; + +/// Represents a bit field. +/// +/// A bit field is a contiguous sequence of bits in a 32-bits word. +/// +/// # Invariant +/// +/// - `pos + len < 32`. +pub struct Field { + /// The position of the bit field. + pub pos: usize, + + /// The length of the bit field. + pub len: usize, +} + +impl Field { + /// Reads the value of a bit field. + pub fn get(&self, word: u32) -> usize { + ((word >> self.pos) & self.mask()) as usize + } + + /// Sets the value of a bit field. + /// + /// # Preconditions + /// + /// - `num_bits(value) < self.len`. + /// - `self.get(*word) & value == value`. + pub fn set(&self, word: &mut u32, value: usize) { + let value = value as u32; + debug_assert_eq!(value & self.mask(), value); + let mask = !(self.mask() << self.pos); + *word &= mask | (value << self.pos); + debug_assert_eq!(self.get(*word), value as usize); + } + + /// Returns a bit mask the length of the bit field. + fn mask(&self) -> u32 { + (1 << self.len) - 1 + } +} + +/// Represents a constant bit field. +/// +/// # Invariant +/// +/// - `num_bits(value) <= field.len`. +pub struct Const { + /// The bit field. + pub field: Field, + + /// The constant value. + pub value: usize, +} + +impl Const { + /// Checks that the bit field has its value. + pub fn check(&self, word: u32) -> bool { + self.field.get(word) == self.value + } + + /// Sets the bit field to its value. + pub fn set(&self, word: &mut u32) { + self.field.set(word, self.value); + } +} + +/// Represents a single bit. +/// +/// # Invariant +/// +/// - `pos < 32`. +pub struct Bit { + /// The position of the bit. + pub pos: usize, +} + +impl Bit { + /// Returns whether the value of the bit is zero. + pub fn get(&self, word: u32) -> bool { + word & (1 << self.pos) == 0 + } + + /// Sets the value of the bit to zero. + pub fn set(&self, word: &mut u32) { + *word &= !(1 << self.pos); + } +} + +/// Represents a checksum. +/// +/// A checksum is a bit field counting how many bits are set to zero in the word (except in the +/// checksum itself) plus some external increment. It essentially behaves like a bit field storing +/// the external increment. +pub struct Checksum { + /// The bit field + pub field: Field, +} + +impl Checksum { + /// Reads the external increment from the checksum. + /// + /// # Errors + /// + /// Returns `InvalidStorage` if the external increment would be negative. + pub fn get(&self, word: u32) -> StoreResult { + let checksum = self.field.get(word); + let zeros = word.count_zeros() as usize - (self.field.len - checksum.count_ones() as usize); + checksum + .checked_sub(zeros) + .ok_or(StoreError::InvalidStorage) + } + + /// Sets the checksum to the external increment value. + /// + /// # Preconditions + /// + /// - The checksum bits should be set to one. + /// - The number of zeros in the word plus the value should fit in the checksum. + pub fn set(&self, word: &mut u32, value: usize) { + debug_assert_eq!(self.field.get(*word), self.field.mask() as usize); + self.field.set(word, word.count_zeros() as usize + value); + } +} + +/// Tracks the number of bits used so far. +/// +/// # Features +/// +/// Only available for tests. +#[cfg(any(doc, test))] +pub struct Length { + /// The position of the next available bit. + pub pos: usize, +} + +/// Helps defining contiguous bit fields. +/// +/// It takes a sequence of bit field descriptors as argument. A bit field descriptor is one of the +/// following: +/// - `$name: Bit,` to define a bit +/// - `$name: Field <= $max,` to define a bit field of minimum length to store `$max` +/// - `$name: Checksum <= $max,` to define a checksum of minimum length to store `$max` +/// - `$name: Length,` to define a length tracker +/// - `$name: Const = [$bits],` to define a constant bit field with value `$bits` (a sequence of +/// space-separated bits) +#[cfg_attr(doc, macro_export)] // For `cargo doc` to produce documentation. +macro_rules! bitfield { + ($($input: tt)*) => { + bitfield_impl! { []{ pos: 0 }[$($input)*] } + }; +} + +macro_rules! bitfield_impl { + // Main rules: + // - Input are bit field descriptors + // - Position is the number of bits used by prior bit fields + // - Output are the bit field definitions + ([$($output: tt)*]{ pos: $pos: expr }[$name: ident: Bit, $($input: tt)*]) => { + bitfield_impl! { + [$($output)* const $name: Bit = Bit { pos: $pos };] + { pos: $pos + 1 } + [$($input)*] + } + }; + ([$($output: tt)*]{ pos: $pos: expr }[$name: ident: Field <= $max: expr, $($input: tt)*]) => { + bitfield_impl! { + [$($output)* const $name: Field = Field { pos: $pos, len: num_bits($max) };] + { pos: $pos + $name.len } + [$($input)*] + } + }; + ([$($output: tt)*]{ pos: $pos: expr } + [$name: ident: Checksum <= $max: expr, $($input: tt)*]) => { + bitfield_impl! { + [$($output)* const $name: Checksum = Checksum { + field: Field { pos: $pos, len: num_bits($max) } + };] + { pos: $pos + $name.field.len } + [$($input)*] + } + }; + ([$($output: tt)*]{ pos: $pos: expr } + [$(#[$meta: meta])* $name: ident: Length, $($input: tt)*]) => { + bitfield_impl! { + [$($output)* $(#[$meta])* const $name: Length = Length { pos: $pos };] + { pos: $pos } + [$($input)*] + } + }; + ([$($output: tt)*]{ pos: $pos: expr }[$name: ident: Const = $bits: tt, $($input: tt)*]) => { + bitfield_impl! { + Reverse $name []$bits + [$($output)*]{ pos: $pos }[$($input)*] + } + }; + ([$($output: tt)*]{ pos: $pos: expr }[]) => { $($output)* }; + + // Auxiliary rules for constant bit fields: + // - Input is a sequence of bits + // - Output is the reversed sequence of bits + (Reverse $name: ident [$($output_bits: tt)*] [$bit: tt $($input_bits: tt)*] + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { + bitfield_impl! { + Reverse $name [$bit $($output_bits)*][$($input_bits)*] + [$($output)*]{ pos: $pos }[$($input)*] + } + }; + (Reverse $name: ident $bits: tt [] + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { + bitfield_impl! { + Const $name { len: 0, val: 0 }$bits + [$($output)*]{ pos: $pos }[$($input)*] + } + }; + + // Auxiliary rules for constant bit fields: + // - Input is a sequence of bits in reversed order + // - Output is the constant bit field definition with the sequence of bits as value + (Const $name: ident { len: $len: expr, val: $val: expr }[] + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { + bitfield_impl! { + [$($output)* const $name: Const = Const { + field: Field { pos: $pos, len: $len }, + value: $val, + };] + { pos: $pos + $name.field.len } + [$($input)*] + } + }; + (Const $name: ident { len: $len: expr, val: $val: expr }[$bit: tt $($bits: tt)*] + [$($output: tt)*]{ pos: $pos: expr }[$($input: tt)*]) => { + bitfield_impl! { + Const $name { len: $len + 1, val: $val * 2 + $bit }[$($bits)*] + [$($output)*]{ pos: $pos }[$($input)*] + } + }; +} + +/// Counts the number of bits equal to zero in a byte slice. +pub fn count_zeros(slice: &[u8]) -> usize { + slice.iter().map(|&x| x.count_zeros() as usize).sum() +} + +/// Returns the number of bits necessary to represent a number. +pub const fn num_bits(x: usize) -> usize { + 8 * core::mem::size_of::() - x.leading_zeros() as usize +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn field_ok() { + let field = Field { pos: 3, len: 5 }; + assert_eq!(field.get(0x00000000), 0); + assert_eq!(field.get(0x00000007), 0); + assert_eq!(field.get(0x00000008), 1); + assert_eq!(field.get(0x000000f8), 0x1f); + assert_eq!(field.get(0x0000ff37), 6); + let mut word = 0xffffffff; + field.set(&mut word, 3); + assert_eq!(word, 0xffffff1f); + } + + #[test] + fn const_ok() { + let field = Const { + field: Field { pos: 3, len: 5 }, + value: 9, + }; + assert!(!field.check(0x00000000)); + assert!(!field.check(0x0000ffff)); + assert!(field.check(0x00000048)); + assert!(field.check(0x0000ff4f)); + let mut word = 0xffffffff; + field.set(&mut word); + assert_eq!(word, 0xffffff4f); + } + + #[test] + fn bit_ok() { + let bit = Bit { pos: 3 }; + assert!(bit.get(0x00000000)); + assert!(bit.get(0xfffffff7)); + assert!(!bit.get(0x00000008)); + assert!(!bit.get(0xffffffff)); + let mut word = 0xffffffff; + bit.set(&mut word); + assert_eq!(word, 0xfffffff7); + } + + #[test] + fn checksum_ok() { + let field = Checksum { + field: Field { pos: 3, len: 5 }, + }; + assert_eq!(field.get(0x00000000), Err(StoreError::InvalidStorage)); + assert_eq!(field.get(0xffffffff), Ok(31)); + assert_eq!(field.get(0xffffff07), Ok(0)); + assert_eq!(field.get(0xffffff0f), Ok(1)); + assert_eq!(field.get(0x00ffff67), Ok(4)); + assert_eq!(field.get(0x7fffff07), Err(StoreError::InvalidStorage)); + let mut word = 0x0fffffff; + field.set(&mut word, 4); + assert_eq!(word, 0x0fffff47); + } + + #[test] + fn bitfield_ok() { + bitfield! { + FIELD: Field <= 127, + CONST: Const = [0 1 0 1], + BIT: Bit, + CHECKSUM: Checksum <= 58, + LENGTH: Length, + } + assert_eq!(FIELD.pos, 0); + assert_eq!(FIELD.len, 7); + assert_eq!(CONST.field.pos, 7); + assert_eq!(CONST.field.len, 4); + assert_eq!(CONST.value, 10); + assert_eq!(BIT.pos, 11); + assert_eq!(CHECKSUM.field.pos, 12); + assert_eq!(CHECKSUM.field.len, 6); + assert_eq!(LENGTH.pos, 18); + } + + #[test] + fn count_zeros_ok() { + assert_eq!(count_zeros(&[0xff, 0xff]), 0); + assert_eq!(count_zeros(&[0xff, 0xfe]), 1); + assert_eq!(count_zeros(&[0x7f, 0xff]), 1); + assert_eq!(count_zeros(&[0x12, 0x48]), 12); + assert_eq!(count_zeros(&[0x00, 0x00]), 16); + } + + #[test] + fn num_bits_ok() { + assert_eq!(num_bits(0), 0); + assert_eq!(num_bits(1), 1); + assert_eq!(num_bits(2), 2); + assert_eq!(num_bits(3), 2); + assert_eq!(num_bits(4), 3); + assert_eq!(num_bits(5), 3); + assert_eq!(num_bits(8), 4); + assert_eq!(num_bits(9), 4); + assert_eq!(num_bits(16), 5); + } +} diff --git a/libraries/persistent_store/src/lib.rs b/libraries/persistent_store/src/lib.rs index 846f956..954e66f 100644 --- a/libraries/persistent_store/src/lib.rs +++ b/libraries/persistent_store/src/lib.rs @@ -14,6 +14,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[macro_use] +mod bitfield; mod storage; +mod store; pub use self::storage::{Storage, StorageError, StorageIndex, StorageResult}; +pub use self::store::{StoreError, StoreResult}; diff --git a/libraries/persistent_store/src/store.rs b/libraries/persistent_store/src/store.rs new file mode 100644 index 0000000..0c94196 --- /dev/null +++ b/libraries/persistent_store/src/store.rs @@ -0,0 +1,63 @@ +// Copyright 2019-2020 Google LLC +// +// 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. + +use crate::StorageError; + +#[derive(Debug, PartialEq, Eq)] +pub enum StoreError { + /// Invalid argument. + /// + /// The store is left unchanged. The operation will repeatedly fail until the argument is fixed. + InvalidArgument, + + /// Not enough capacity. + /// + /// The store is left unchanged. The operation will repeatedly fail until capacity is freed. + NoCapacity, + + /// Reached end of lifetime. + /// + /// The store is left unchanged. The operation will repeatedly fail until emergency lifetime is + /// added. + NoLifetime, + + /// A storage operation failed. + /// + /// The consequences depend on the storage failure. In particular, the operation may or may not + /// have succeeded, and the storage may have become invalid. Before doing any other operation, + /// the store should be [recovered]. The operation may then be retried if idempotent. + /// + /// [recovered]: struct.Store.html#method.recover + StorageError, + + /// Storage is invalid. + /// + /// The storage should be erased and the store [recovered]. The store would be empty and have + /// lost track of lifetime. + /// + /// [recovered]: struct.Store.html#method.recover + InvalidStorage, +} + +impl From for StoreError { + fn from(error: StorageError) -> StoreError { + match error { + StorageError::CustomError => StoreError::StorageError, + // The store always calls the storage correctly. + StorageError::NotAligned | StorageError::OutOfBounds => unreachable!(), + } + } +} + +pub type StoreResult = Result;