Merge pull request #192 from ia0/v2_lib
Clarify different integer types
This commit is contained in:
@@ -15,68 +15,104 @@
|
|||||||
// TODO(ia0): Remove when the module is used.
|
// TODO(ia0): Remove when the module is used.
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use crate::bitfield::*;
|
#[macro_use]
|
||||||
use crate::{Storage, StorageIndex, StoreError, StoreResult};
|
mod bitfield;
|
||||||
|
|
||||||
|
use self::bitfield::*;
|
||||||
|
use crate::{usize_to_nat, Nat, Storage, StorageIndex, StoreError, StoreResult};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
/// Internal representation of a word in flash.
|
||||||
|
///
|
||||||
|
/// Currently, the store only supports storages where a word is 32 bits.
|
||||||
type WORD = u32;
|
type WORD = u32;
|
||||||
|
|
||||||
|
/// Abstract representation of a word in flash.
|
||||||
|
///
|
||||||
|
/// This type is kept abstract to avoid possible confusion with `Nat` if they happen to have the
|
||||||
|
/// same representation. This is because they have different semantics, `Nat` represents natural
|
||||||
|
/// numbers while `Word` represents sequences of bits (and thus has no arithmetic).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Word(WORD);
|
||||||
|
|
||||||
|
/// Byte slice representation of a word in flash.
|
||||||
|
///
|
||||||
|
/// The slice is in little-endian representation.
|
||||||
|
pub type WordSlice = [u8; core::mem::size_of::<WORD>()];
|
||||||
|
|
||||||
|
impl Word {
|
||||||
|
/// Converts a byte slice into a word.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `slice.len() != WORD_SIZE`.
|
||||||
|
pub fn from_slice(slice: &[u8]) -> Word {
|
||||||
|
Word(WORD::from_le_bytes(<WordSlice>::try_from(slice).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a word into a byte slice.
|
||||||
|
pub fn as_slice(self) -> WordSlice {
|
||||||
|
self.0.to_le_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Size of a word in bytes.
|
/// Size of a word in bytes.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports storages where a word is 4 bytes.
|
/// Currently, the store only supports storages where a word is 4 bytes.
|
||||||
const WORD_SIZE: usize = core::mem::size_of::<WORD>();
|
const WORD_SIZE: Nat = core::mem::size_of::<WORD>() as Nat;
|
||||||
|
|
||||||
/// Minimum number of words per page.
|
/// Minimum number of words per page.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports storages where pages have at least 8 words.
|
/// Currently, the store only supports storages where pages have at least 8 words.
|
||||||
const MIN_NUM_WORDS_PER_PAGE: usize = 8;
|
const MIN_NUM_WORDS_PER_PAGE: Nat = 8;
|
||||||
|
|
||||||
/// Maximum size of a page in bytes.
|
/// Maximum size of a page in bytes.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports storages where pages are between 8 and 1024 [words].
|
/// Currently, the store only supports storages where pages are between 8 and 1024 [words].
|
||||||
///
|
///
|
||||||
/// [words]: constant.WORD_SIZE.html
|
/// [words]: constant.WORD_SIZE.html
|
||||||
const MAX_PAGE_SIZE: usize = 4096;
|
const MAX_PAGE_SIZE: Nat = 4096;
|
||||||
|
|
||||||
/// Maximum number of erase cycles.
|
/// Maximum number of erase cycles.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports storages where the maximum number of erase cycles fits on 16
|
/// Currently, the store only supports storages where the maximum number of erase cycles fits on 16
|
||||||
/// bits.
|
/// bits.
|
||||||
const MAX_ERASE_CYCLE: usize = 65535;
|
const MAX_ERASE_CYCLE: Nat = 65535;
|
||||||
|
|
||||||
/// Minimum number of pages.
|
/// Minimum number of pages.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports storages with at least 3 pages.
|
/// Currently, the store only supports storages with at least 3 pages.
|
||||||
const MIN_NUM_PAGES: usize = 3;
|
const MIN_NUM_PAGES: Nat = 3;
|
||||||
|
|
||||||
/// Maximum page index.
|
/// Maximum page index.
|
||||||
///
|
///
|
||||||
/// Thus the maximum number of pages is one more than this number. Currently, the store only
|
/// Thus the maximum number of pages is one more than this number. Currently, the store only
|
||||||
/// supports storages where the number of pages is between 3 and 64.
|
/// supports storages where the number of pages is between 3 and 64.
|
||||||
const MAX_PAGE_INDEX: usize = 63;
|
const MAX_PAGE_INDEX: Nat = 63;
|
||||||
|
|
||||||
/// Maximum key index.
|
/// Maximum key index.
|
||||||
///
|
///
|
||||||
/// Thus the number of keys is one more than this number. Currently, the store only supports 4096
|
/// Thus the number of keys is one more than this number. Currently, the store only supports 4096
|
||||||
/// keys.
|
/// keys.
|
||||||
const MAX_KEY_INDEX: usize = 4095;
|
const MAX_KEY_INDEX: Nat = 4095;
|
||||||
|
|
||||||
/// Maximum length in bytes of a user payload.
|
/// Maximum length in bytes of a user payload.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports values smaller than 1024 bytes.
|
/// Currently, the store only supports values smaller than 1024 bytes.
|
||||||
const MAX_VALUE_LEN: usize = 1023;
|
const MAX_VALUE_LEN: Nat = 1023;
|
||||||
|
|
||||||
/// Maximum number of updates per transaction.
|
/// Maximum number of updates per transaction.
|
||||||
///
|
///
|
||||||
/// Currently, the store only supports transactions with at most 31 updates.
|
/// Currently, the store only supports transactions with at most 31 updates.
|
||||||
const MAX_UPDATES: usize = 31;
|
const MAX_UPDATES: Nat = 31;
|
||||||
|
|
||||||
/// Maximum number of words per virtual page.
|
/// Maximum number of words per virtual page.
|
||||||
const MAX_VIRT_PAGE_SIZE: usize = div_ceil(MAX_PAGE_SIZE, WORD_SIZE) - CONTENT_WORD;
|
const MAX_VIRT_PAGE_SIZE: Nat = div_ceil(MAX_PAGE_SIZE, WORD_SIZE) - CONTENT_WORD;
|
||||||
|
|
||||||
/// Word with all bits set to one.
|
/// Word with all bits set to one.
|
||||||
const ERASED_WORD: WORD = !(0 as WORD);
|
const ERASED_WORD: Word = Word(!(0 as WORD));
|
||||||
|
|
||||||
/// Helpers for a given storage configuration.
|
/// Helpers for a given storage configuration.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -88,7 +124,7 @@ pub struct Format {
|
|||||||
/// - Words divide a page evenly.
|
/// - Words divide a page evenly.
|
||||||
/// - There are at least 8 words in a page.
|
/// - There are at least 8 words in a page.
|
||||||
/// - There are at most `MAX_PAGE_SIZE` bytes in a page.
|
/// - There are at most `MAX_PAGE_SIZE` bytes in a page.
|
||||||
page_size: usize,
|
page_size: Nat,
|
||||||
|
|
||||||
/// The number of pages in the storage.
|
/// The number of pages in the storage.
|
||||||
///
|
///
|
||||||
@@ -96,14 +132,14 @@ pub struct Format {
|
|||||||
///
|
///
|
||||||
/// - There are at least 3 pages.
|
/// - There are at least 3 pages.
|
||||||
/// - There are at most `MAX_PAGE_INDEX + 1` pages.
|
/// - There are at most `MAX_PAGE_INDEX + 1` pages.
|
||||||
num_pages: usize,
|
num_pages: Nat,
|
||||||
|
|
||||||
/// The maximum number of times a page can be erased.
|
/// The maximum number of times a page can be erased.
|
||||||
///
|
///
|
||||||
/// # Invariant
|
/// # Invariant
|
||||||
///
|
///
|
||||||
/// - A page can be erased at most `MAX_ERASE_CYCLE` times.
|
/// - A page can be erased at most `MAX_ERASE_CYCLE` times.
|
||||||
max_page_erases: usize,
|
max_page_erases: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format {
|
impl Format {
|
||||||
@@ -115,9 +151,9 @@ impl Format {
|
|||||||
pub fn new<S: Storage>(storage: &S) -> Option<Format> {
|
pub fn new<S: Storage>(storage: &S) -> Option<Format> {
|
||||||
if Format::is_storage_supported(storage) {
|
if Format::is_storage_supported(storage) {
|
||||||
Some(Format {
|
Some(Format {
|
||||||
page_size: storage.page_size(),
|
page_size: usize_to_nat(storage.page_size()),
|
||||||
num_pages: storage.num_pages(),
|
num_pages: usize_to_nat(storage.num_pages()),
|
||||||
max_page_erases: storage.max_page_erases(),
|
max_page_erases: usize_to_nat(storage.max_page_erases()),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -143,11 +179,11 @@ impl Format {
|
|||||||
/// [`MAX_PAGE_INDEX`]: constant.MAX_PAGE_INDEX.html
|
/// [`MAX_PAGE_INDEX`]: constant.MAX_PAGE_INDEX.html
|
||||||
/// [`MAX_ERASE_CYCLE`]: constant.MAX_ERASE_CYCLE.html
|
/// [`MAX_ERASE_CYCLE`]: constant.MAX_ERASE_CYCLE.html
|
||||||
fn is_storage_supported<S: Storage>(storage: &S) -> bool {
|
fn is_storage_supported<S: Storage>(storage: &S) -> bool {
|
||||||
let word_size = storage.word_size();
|
let word_size = usize_to_nat(storage.word_size());
|
||||||
let page_size = storage.page_size();
|
let page_size = usize_to_nat(storage.page_size());
|
||||||
let num_pages = storage.num_pages();
|
let num_pages = usize_to_nat(storage.num_pages());
|
||||||
let max_word_writes = storage.max_word_writes();
|
let max_word_writes = usize_to_nat(storage.max_word_writes());
|
||||||
let max_page_erases = storage.max_page_erases();
|
let max_page_erases = usize_to_nat(storage.max_page_erases());
|
||||||
word_size == WORD_SIZE
|
word_size == WORD_SIZE
|
||||||
&& page_size % word_size == 0
|
&& page_size % word_size == 0
|
||||||
&& (MIN_NUM_WORDS_PER_PAGE * word_size <= page_size && page_size <= MAX_PAGE_SIZE)
|
&& (MIN_NUM_WORDS_PER_PAGE * word_size <= page_size && page_size <= MAX_PAGE_SIZE)
|
||||||
@@ -157,45 +193,45 @@ impl Format {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The size of a word in bytes.
|
/// The size of a word in bytes.
|
||||||
pub fn word_size(&self) -> usize {
|
pub fn word_size(&self) -> Nat {
|
||||||
WORD_SIZE
|
WORD_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The size of a page in bytes.
|
/// The size of a page in bytes.
|
||||||
///
|
///
|
||||||
/// We have `MIN_NUM_WORDS_PER_PAGE * self.word_size() <= self.page_size() <= MAX_PAGE_SIZE`.
|
/// We have `MIN_NUM_WORDS_PER_PAGE * self.word_size() <= self.page_size() <= MAX_PAGE_SIZE`.
|
||||||
pub fn page_size(&self) -> usize {
|
pub fn page_size(&self) -> Nat {
|
||||||
self.page_size
|
self.page_size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of pages in the storage, denoted by `N`.
|
/// The number of pages in the storage, denoted by `N`.
|
||||||
///
|
///
|
||||||
/// We have `MIN_NUM_PAGES <= N <= MAX_PAGE_INDEX + 1`.
|
/// We have `MIN_NUM_PAGES <= N <= MAX_PAGE_INDEX + 1`.
|
||||||
pub fn num_pages(&self) -> usize {
|
pub fn num_pages(&self) -> Nat {
|
||||||
self.num_pages
|
self.num_pages
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum page index.
|
/// The maximum page index.
|
||||||
///
|
///
|
||||||
/// We have `2 <= self.max_page() <= MAX_PAGE_INDEX`.
|
/// We have `2 <= self.max_page() <= MAX_PAGE_INDEX`.
|
||||||
pub fn max_page(&self) -> usize {
|
pub fn max_page(&self) -> Nat {
|
||||||
self.num_pages - 1
|
self.num_pages - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of times a page can be erased, denoted by `E`.
|
/// The maximum number of times a page can be erased, denoted by `E`.
|
||||||
///
|
///
|
||||||
/// We have `E <= MAX_ERASE_CYCLE`.
|
/// We have `E <= MAX_ERASE_CYCLE`.
|
||||||
pub fn max_page_erases(&self) -> usize {
|
pub fn max_page_erases(&self) -> Nat {
|
||||||
self.max_page_erases
|
self.max_page_erases
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum key.
|
/// The maximum key.
|
||||||
pub fn max_key(&self) -> usize {
|
pub fn max_key(&self) -> Nat {
|
||||||
MAX_KEY_INDEX
|
MAX_KEY_INDEX
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The maximum number of updates per transaction.
|
/// The maximum number of updates per transaction.
|
||||||
pub fn max_updates(&self) -> usize {
|
pub fn max_updates(&self) -> Nat {
|
||||||
MAX_UPDATES
|
MAX_UPDATES
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +240,7 @@ impl Format {
|
|||||||
/// A virtual page is stored in a physical page after the page header.
|
/// A virtual page is stored in a physical page after the page header.
|
||||||
///
|
///
|
||||||
/// We have `MIN_NUM_WORDS_PER_PAGE - 2 <= Q <= MAX_VIRT_PAGE_SIZE`.
|
/// We have `MIN_NUM_WORDS_PER_PAGE - 2 <= Q <= MAX_VIRT_PAGE_SIZE`.
|
||||||
pub fn virt_page_size(&self) -> usize {
|
pub fn virt_page_size(&self) -> Nat {
|
||||||
self.page_size() / self.word_size() - CONTENT_WORD
|
self.page_size() / self.word_size() - CONTENT_WORD
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +248,7 @@ impl Format {
|
|||||||
///
|
///
|
||||||
/// We have `(MIN_NUM_WORDS_PER_PAGE - 3) * self.word_size() <= self.max_value_len() <=
|
/// We have `(MIN_NUM_WORDS_PER_PAGE - 3) * self.word_size() <= self.max_value_len() <=
|
||||||
/// MAX_VALUE_LEN`.
|
/// MAX_VALUE_LEN`.
|
||||||
pub fn max_value_len(&self) -> usize {
|
pub fn max_value_len(&self) -> Nat {
|
||||||
min(
|
min(
|
||||||
(self.virt_page_size() - 1) * self.word_size(),
|
(self.virt_page_size() - 1) * self.word_size(),
|
||||||
MAX_VALUE_LEN,
|
MAX_VALUE_LEN,
|
||||||
@@ -225,7 +261,7 @@ impl Format {
|
|||||||
/// virtual page. This happens because entries may overlap up to 2 virtual pages.
|
/// virtual page. This happens because entries may overlap up to 2 virtual pages.
|
||||||
///
|
///
|
||||||
/// We have `MIN_NUM_WORDS_PER_PAGE - 3 <= M < Q`.
|
/// We have `MIN_NUM_WORDS_PER_PAGE - 3 <= M < Q`.
|
||||||
pub fn max_prefix_len(&self) -> usize {
|
pub fn max_prefix_len(&self) -> Nat {
|
||||||
self.bytes_to_words(self.max_value_len())
|
self.bytes_to_words(self.max_value_len())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +275,7 @@ impl Format {
|
|||||||
/// - `V >= (N - 1) * (Q - 1) - (Q - 1)` from `V` definition
|
/// - `V >= (N - 1) * (Q - 1) - (Q - 1)` from `V` definition
|
||||||
///
|
///
|
||||||
/// [`M`]: struct.Format.html#method.max_prefix_len
|
/// [`M`]: struct.Format.html#method.max_prefix_len
|
||||||
pub fn virt_size(&self) -> usize {
|
pub fn virt_size(&self) -> Nat {
|
||||||
(self.num_pages() - 1) * (self.virt_page_size() - 1) - self.max_prefix_len()
|
(self.num_pages() - 1) * (self.virt_page_size() - 1) - self.max_prefix_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +289,7 @@ impl Format {
|
|||||||
/// - `(N - 2) * (Q - 1) - N = (N - 2) * (Q - 2) - 2` by calculus
|
/// - `(N - 2) * (Q - 1) - N = (N - 2) * (Q - 2) - 2` by calculus
|
||||||
///
|
///
|
||||||
/// [`V`]: struct.Format.html#method.virt_size
|
/// [`V`]: struct.Format.html#method.virt_size
|
||||||
pub fn total_capacity(&self) -> usize {
|
pub fn total_capacity(&self) -> Nat {
|
||||||
// From the virtual capacity, we reserve N - 1 words for `Erase` entries and 1 word for a
|
// From the virtual capacity, we reserve N - 1 words for `Erase` entries and 1 word for a
|
||||||
// `Clear` entry.
|
// `Clear` entry.
|
||||||
self.virt_size() - self.num_pages()
|
self.virt_size() - self.num_pages()
|
||||||
@@ -270,18 +306,21 @@ impl Format {
|
|||||||
///
|
///
|
||||||
/// The init info of the page must be provided to know where the first entry of the page
|
/// The init info of the page must be provided to know where the first entry of the page
|
||||||
/// starts.
|
/// starts.
|
||||||
pub fn page_head(&self, init: InitInfo, page: usize) -> Position {
|
pub fn page_head(&self, init: InitInfo, page: Nat) -> Position {
|
||||||
Position::new(self, init.cycle, page, init.prefix)
|
Position::new(self, init.cycle, page, init.prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the storage index of the init info of a page.
|
/// Returns the storage index of the init info of a page.
|
||||||
pub fn index_init(&self, page: usize) -> StorageIndex {
|
pub fn index_init(&self, page: Nat) -> StorageIndex {
|
||||||
let byte = INIT_WORD * self.word_size();
|
let byte = INIT_WORD * self.word_size();
|
||||||
StorageIndex { page, byte }
|
StorageIndex {
|
||||||
|
page: page as usize,
|
||||||
|
byte: byte as usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the init info of a page from its storage representation.
|
/// Parses the init info of a page from its storage representation.
|
||||||
pub fn parse_init(&self, word: WORD) -> StoreResult<WordState<InitInfo>> {
|
pub fn parse_init(&self, word: Word) -> StoreResult<WordState<InitInfo>> {
|
||||||
Ok(if word == ERASED_WORD {
|
Ok(if word == ERASED_WORD {
|
||||||
WordState::Erased
|
WordState::Erased
|
||||||
} else if WORD_CHECKSUM.get(word)? != 0 {
|
} else if WORD_CHECKSUM.get(word)? != 0 {
|
||||||
@@ -297,22 +336,25 @@ impl Format {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the storage representation of an init info.
|
/// Builds the storage representation of an init info.
|
||||||
pub fn build_init(&self, init: InitInfo) -> [u8; WORD_SIZE] {
|
pub fn build_init(&self, init: InitInfo) -> WordSlice {
|
||||||
let mut word = ERASED_WORD;
|
let mut word = ERASED_WORD;
|
||||||
INIT_CYCLE.set(&mut word, init.cycle);
|
INIT_CYCLE.set(&mut word, init.cycle);
|
||||||
INIT_PREFIX.set(&mut word, init.prefix);
|
INIT_PREFIX.set(&mut word, init.prefix);
|
||||||
WORD_CHECKSUM.set(&mut word, 0);
|
WORD_CHECKSUM.set(&mut word, 0);
|
||||||
word.to_ne_bytes()
|
word.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the storage index of the compact info of a page.
|
/// Returns the storage index of the compact info of a page.
|
||||||
pub fn index_compact(&self, page: usize) -> StorageIndex {
|
pub fn index_compact(&self, page: Nat) -> StorageIndex {
|
||||||
let byte = COMPACT_WORD * self.word_size();
|
let byte = COMPACT_WORD * self.word_size();
|
||||||
StorageIndex { page, byte }
|
StorageIndex {
|
||||||
|
page: page as usize,
|
||||||
|
byte: byte as usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the compact info of a page from its storage representation.
|
/// Parses the compact info of a page from its storage representation.
|
||||||
pub fn parse_compact(&self, word: WORD) -> StoreResult<WordState<CompactInfo>> {
|
pub fn parse_compact(&self, word: Word) -> StoreResult<WordState<CompactInfo>> {
|
||||||
Ok(if word == ERASED_WORD {
|
Ok(if word == ERASED_WORD {
|
||||||
WordState::Erased
|
WordState::Erased
|
||||||
} else if WORD_CHECKSUM.get(word)? != 0 {
|
} else if WORD_CHECKSUM.get(word)? != 0 {
|
||||||
@@ -327,15 +369,15 @@ impl Format {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the storage representation of a compact info.
|
/// Builds the storage representation of a compact info.
|
||||||
pub fn build_compact(&self, compact: CompactInfo) -> [u8; WORD_SIZE] {
|
pub fn build_compact(&self, compact: CompactInfo) -> WordSlice {
|
||||||
let mut word = ERASED_WORD;
|
let mut word = ERASED_WORD;
|
||||||
COMPACT_TAIL.set(&mut word, compact.tail);
|
COMPACT_TAIL.set(&mut word, compact.tail);
|
||||||
WORD_CHECKSUM.set(&mut word, 0);
|
WORD_CHECKSUM.set(&mut word, 0);
|
||||||
word.to_ne_bytes()
|
word.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the storage representation of an internal entry.
|
/// Builds the storage representation of an internal entry.
|
||||||
pub fn build_internal(&self, internal: InternalEntry) -> [u8; WORD_SIZE] {
|
pub fn build_internal(&self, internal: InternalEntry) -> WordSlice {
|
||||||
let mut word = ERASED_WORD;
|
let mut word = ERASED_WORD;
|
||||||
match internal {
|
match internal {
|
||||||
InternalEntry::Erase { page } => {
|
InternalEntry::Erase { page } => {
|
||||||
@@ -356,11 +398,11 @@ impl Format {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WORD_CHECKSUM.set(&mut word, 0);
|
WORD_CHECKSUM.set(&mut word, 0);
|
||||||
word.to_ne_bytes()
|
word.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the first word of an entry from its storage representation.
|
/// Parses the first word of an entry from its storage representation.
|
||||||
pub fn parse_word(&self, word: WORD) -> StoreResult<WordState<ParsedWord>> {
|
pub fn parse_word(&self, word: Word) -> StoreResult<WordState<ParsedWord>> {
|
||||||
let valid = if ID_PADDING.check(word) {
|
let valid = if ID_PADDING.check(word) {
|
||||||
ParsedWord::Padding(Padding { length: 0 })
|
ParsedWord::Padding(Padding { length: 0 })
|
||||||
} else if ID_HEADER.check(word) {
|
} else if ID_HEADER.check(word) {
|
||||||
@@ -418,32 +460,35 @@ impl Format {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the storage representation of a user entry.
|
/// Builds the storage representation of a user entry.
|
||||||
pub fn build_user(&self, key: usize, value: &[u8]) -> Vec<u8> {
|
pub fn build_user(&self, key: Nat, value: &[u8]) -> Vec<u8> {
|
||||||
let length = value.len();
|
let length = usize_to_nat(value.len());
|
||||||
let word_size = self.word_size();
|
let word_size = self.word_size();
|
||||||
let footer = self.bytes_to_words(length);
|
let footer = self.bytes_to_words(length);
|
||||||
let mut result = vec![0xff; (1 + footer) * word_size];
|
let mut result = vec![0xff; ((1 + footer) * word_size) as usize];
|
||||||
result[word_size..][..length].copy_from_slice(value);
|
result[word_size as usize..][..length as usize].copy_from_slice(value);
|
||||||
let mut word = ERASED_WORD;
|
let mut word = ERASED_WORD;
|
||||||
ID_HEADER.set(&mut word);
|
ID_HEADER.set(&mut word);
|
||||||
if footer > 0 && is_erased(&result[footer * word_size..]) {
|
if footer > 0 && is_erased(&result[(footer * word_size) as usize..]) {
|
||||||
HEADER_FLIPPED.set(&mut word);
|
HEADER_FLIPPED.set(&mut word);
|
||||||
*result.last_mut().unwrap() = 0x7f;
|
*result.last_mut().unwrap() = 0x7f;
|
||||||
}
|
}
|
||||||
HEADER_LENGTH.set(&mut word, length);
|
HEADER_LENGTH.set(&mut word, length);
|
||||||
HEADER_KEY.set(&mut word, key);
|
HEADER_KEY.set(&mut word, key);
|
||||||
HEADER_CHECKSUM.set(&mut word, count_zeros(&result[footer * word_size..]));
|
HEADER_CHECKSUM.set(
|
||||||
result[..word_size].copy_from_slice(&word.to_ne_bytes());
|
&mut word,
|
||||||
|
count_zeros(&result[(footer * word_size) as usize..]),
|
||||||
|
);
|
||||||
|
result[..word_size as usize].copy_from_slice(&word.as_slice());
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the padding bit in the first word of a user entry.
|
/// Sets the padding bit in the first word of a user entry.
|
||||||
pub fn set_padding(&self, word: &mut WORD) {
|
pub fn set_padding(&self, word: &mut Word) {
|
||||||
ID_PADDING.set(word);
|
ID_PADDING.set(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the deleted bit in the first word of a user entry.
|
/// Sets the deleted bit in the first word of a user entry.
|
||||||
pub fn set_deleted(&self, word: &mut WORD) {
|
pub fn set_deleted(&self, word: &mut Word) {
|
||||||
HEADER_DELETED.set(word);
|
HEADER_DELETED.set(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,21 +497,21 @@ impl Format {
|
|||||||
/// # Preconditions
|
/// # Preconditions
|
||||||
///
|
///
|
||||||
/// - `bytes + self.word_size()` does not overflow.
|
/// - `bytes + self.word_size()` does not overflow.
|
||||||
pub fn bytes_to_words(&self, bytes: usize) -> usize {
|
pub fn bytes_to_words(&self, bytes: Nat) -> Nat {
|
||||||
div_ceil(bytes, self.word_size())
|
div_ceil(bytes, self.word_size())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The word index of the init info in a page.
|
/// The word index of the init info in a page.
|
||||||
const INIT_WORD: usize = 0;
|
const INIT_WORD: Nat = 0;
|
||||||
|
|
||||||
/// The word index of the compact info in a page.
|
/// The word index of the compact info in a page.
|
||||||
const COMPACT_WORD: usize = 1;
|
const COMPACT_WORD: Nat = 1;
|
||||||
|
|
||||||
/// The word index of the content of a page.
|
/// The word index of the content of a page.
|
||||||
///
|
///
|
||||||
/// Since a page is at least 8 words, there is always at least 6 words of content.
|
/// Since a page is at least 8 words, there is always at least 6 words of content.
|
||||||
const CONTENT_WORD: usize = 2;
|
const CONTENT_WORD: Nat = 2;
|
||||||
|
|
||||||
/// The checksum for a single word.
|
/// The checksum for a single word.
|
||||||
///
|
///
|
||||||
@@ -619,27 +664,36 @@ bitfield! {
|
|||||||
///
|
///
|
||||||
/// Then the position of a word is `(c*N + p)*Q + w`. This position monotonically increases and
|
/// Then the position of a word is `(c*N + p)*Q + w`. This position monotonically increases and
|
||||||
/// represents the consumed lifetime of the storage.
|
/// represents the consumed lifetime of the storage.
|
||||||
|
///
|
||||||
|
/// This type is kept abstract to avoid possible confusion with `Nat` and `Word` if they happen to
|
||||||
|
/// have the same representation. Here is an overview of their semantics:
|
||||||
|
///
|
||||||
|
/// | Name | Semantics | Arithmetic operations | Bit-wise operations |
|
||||||
|
/// | ---------- | --------------------------- | --------------------- | ------------------- |
|
||||||
|
/// | `Nat` | Natural numbers | Yes (no overflow) | No |
|
||||||
|
/// | `Word` | Word in flash | No | Yes |
|
||||||
|
/// | `Position` | Position in virtual storage | Yes (no overflow) | No |
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Position(usize);
|
pub struct Position(Nat);
|
||||||
|
|
||||||
impl core::ops::Add<usize> for Position {
|
impl core::ops::Add<Nat> for Position {
|
||||||
type Output = Position;
|
type Output = Position;
|
||||||
|
|
||||||
fn add(self, delta: usize) -> Position {
|
fn add(self, delta: Nat) -> Position {
|
||||||
Position(self.0 + delta)
|
Position(self.0 + delta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::ops::Sub<Position> for Position {
|
impl core::ops::Sub<Position> for Position {
|
||||||
type Output = usize;
|
type Output = Nat;
|
||||||
|
|
||||||
fn sub(self, base: Position) -> usize {
|
fn sub(self, base: Position) -> Nat {
|
||||||
self.0 - base.0
|
self.0 - base.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::ops::AddAssign<usize> for Position {
|
impl core::ops::AddAssign<Nat> for Position {
|
||||||
fn add_assign(&mut self, delta: usize) {
|
fn add_assign(&mut self, delta: Nat) {
|
||||||
self.0 += delta;
|
self.0 += delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,12 +705,12 @@ impl Position {
|
|||||||
/// - Its word index in its page.
|
/// - Its word index in its page.
|
||||||
/// - Its page index in the storage.
|
/// - Its page index in the storage.
|
||||||
/// - The number of times that page was erased.
|
/// - The number of times that page was erased.
|
||||||
pub fn new(format: &Format, cycle: usize, page: usize, word: usize) -> Position {
|
pub fn new(format: &Format, cycle: Nat, page: Nat, word: Nat) -> Position {
|
||||||
Position((cycle * format.num_pages() + page) * format.virt_page_size() + word)
|
Position((cycle * format.num_pages() + page) * format.virt_page_size() + word)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accesses the underlying position as a natural number.
|
/// Accesses the underlying position as a natural number.
|
||||||
pub fn get(self) -> usize {
|
pub fn get(self) -> Nat {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,7 +719,10 @@ impl Position {
|
|||||||
let page = self.page(format);
|
let page = self.page(format);
|
||||||
let word = CONTENT_WORD + self.word(format);
|
let word = CONTENT_WORD + self.word(format);
|
||||||
let byte = word * format.word_size();
|
let byte = word * format.word_size();
|
||||||
StorageIndex { page, byte }
|
StorageIndex {
|
||||||
|
page: page as usize,
|
||||||
|
byte: byte as usize,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the beginning of the current virtual page.
|
/// Returns the beginning of the current virtual page.
|
||||||
@@ -681,17 +738,17 @@ impl Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of times the current page was erased.
|
/// Returns the number of times the current page was erased.
|
||||||
pub fn cycle(self, format: &Format) -> usize {
|
pub fn cycle(self, format: &Format) -> Nat {
|
||||||
(self.0 / format.virt_page_size()) / format.num_pages()
|
(self.0 / format.virt_page_size()) / format.num_pages()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current page index.
|
/// Returns the current page index.
|
||||||
pub fn page(self, format: &Format) -> usize {
|
pub fn page(self, format: &Format) -> Nat {
|
||||||
(self.0 / format.virt_page_size()) % format.num_pages()
|
(self.0 / format.virt_page_size()) % format.num_pages()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current word index in the page.
|
/// Returns the current word index in the page.
|
||||||
pub fn word(self, format: &Format) -> usize {
|
pub fn word(self, format: &Format) -> Nat {
|
||||||
self.0 % format.virt_page_size()
|
self.0 % format.virt_page_size()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -711,16 +768,16 @@ pub enum WordState<T> {
|
|||||||
/// Information for an initialized page.
|
/// Information for an initialized page.
|
||||||
pub struct InitInfo {
|
pub struct InitInfo {
|
||||||
/// The number of times this page has been erased.
|
/// The number of times this page has been erased.
|
||||||
pub cycle: usize,
|
pub cycle: Nat,
|
||||||
|
|
||||||
/// The word index of the first entry in this virtual page.
|
/// The word index of the first entry in this virtual page.
|
||||||
pub prefix: usize,
|
pub prefix: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information for a page being compacted.
|
/// Information for a page being compacted.
|
||||||
pub struct CompactInfo {
|
pub struct CompactInfo {
|
||||||
/// The distance in words between head and tail at compaction.
|
/// The distance in words between head and tail at compaction.
|
||||||
pub tail: usize,
|
pub tail: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first word of an entry.
|
/// The first word of an entry.
|
||||||
@@ -740,7 +797,7 @@ pub enum ParsedWord {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Padding {
|
pub struct Padding {
|
||||||
/// The number of following padding words after the first word of the padding entry.
|
/// The number of following padding words after the first word of the padding entry.
|
||||||
pub length: usize,
|
pub length: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Header of a user entry.
|
/// Header of a user entry.
|
||||||
@@ -750,13 +807,13 @@ pub struct Header {
|
|||||||
pub flipped: bool,
|
pub flipped: bool,
|
||||||
|
|
||||||
/// The length in bytes of the user data.
|
/// The length in bytes of the user data.
|
||||||
pub length: usize,
|
pub length: Nat,
|
||||||
|
|
||||||
/// The key of the user entry.
|
/// The key of the user entry.
|
||||||
pub key: usize,
|
pub key: Nat,
|
||||||
|
|
||||||
/// The checksum of the user entry.
|
/// The checksum of the user entry.
|
||||||
pub checksum: usize,
|
pub checksum: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
@@ -775,13 +832,13 @@ pub enum InternalEntry {
|
|||||||
/// Indicates that a page should be erased.
|
/// Indicates that a page should be erased.
|
||||||
Erase {
|
Erase {
|
||||||
/// The page to be erased.
|
/// The page to be erased.
|
||||||
page: usize,
|
page: Nat,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Indicates that user entries with high key should be deleted.
|
/// Indicates that user entries with high key should be deleted.
|
||||||
Clear {
|
Clear {
|
||||||
/// The minimum key a user entry should have to be deleted.
|
/// The minimum key a user entry should have to be deleted.
|
||||||
min_key: usize,
|
min_key: Nat,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Marks the start of a transaction.
|
/// Marks the start of a transaction.
|
||||||
@@ -790,7 +847,7 @@ pub enum InternalEntry {
|
|||||||
/// entries.
|
/// entries.
|
||||||
Marker {
|
Marker {
|
||||||
/// The number of updates in the transaction.
|
/// The number of updates in the transaction.
|
||||||
count: usize,
|
count: Nat,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Indicates that a user entry should be removed.
|
/// Indicates that a user entry should be removed.
|
||||||
@@ -799,7 +856,7 @@ pub enum InternalEntry {
|
|||||||
/// already atomic.
|
/// already atomic.
|
||||||
Remove {
|
Remove {
|
||||||
/// The key of the user entry to be removed.
|
/// The key of the user entry to be removed.
|
||||||
key: usize,
|
key: Nat,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,7 +872,7 @@ pub fn is_erased(slice: &[u8]) -> bool {
|
|||||||
/// # Preconditions
|
/// # Preconditions
|
||||||
///
|
///
|
||||||
/// - `x + m` does not overflow.
|
/// - `x + m` does not overflow.
|
||||||
const fn div_ceil(x: usize, m: usize) -> usize {
|
const fn div_ceil(x: Nat, m: Nat) -> Nat {
|
||||||
(x + m - 1) / m
|
(x + m - 1) / m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,7 +882,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn size_of_format() {
|
fn size_of_format() {
|
||||||
assert_eq!(std::mem::size_of::<Format>(), 24);
|
assert_eq!(std::mem::size_of::<Format>(), 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -921,6 +978,18 @@ mod tests {
|
|||||||
assert_eq!(LEN_REMOVE.pos, 17);
|
assert_eq!(LEN_REMOVE.pos, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn word_from_slice_ok() {
|
||||||
|
assert_eq!(
|
||||||
|
Word::from_slice(&[0x04, 0x03, 0x02, 0x01]),
|
||||||
|
Word(0x01020304)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Word::from_slice(&[0x1e, 0x3c, 0x78, 0xf0]),
|
||||||
|
Word(0xf0783c1e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_erased_ok() {
|
fn is_erased_ok() {
|
||||||
assert!(is_erased(&[]));
|
assert!(is_erased(&[]));
|
||||||
|
|||||||
@@ -12,9 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Helps manipulate bit fields in 32-bits words.
|
//! Helps manipulate words as bit fields.
|
||||||
|
//!
|
||||||
|
//! This module assumes that `Word` and `Nat` are both represented as `u32`.
|
||||||
|
|
||||||
use crate::{StoreError, StoreResult};
|
use crate::format::Word;
|
||||||
|
use crate::{Nat, StoreError, StoreResult};
|
||||||
|
|
||||||
/// Represents a bit field.
|
/// Represents a bit field.
|
||||||
///
|
///
|
||||||
@@ -25,16 +28,16 @@ use crate::{StoreError, StoreResult};
|
|||||||
/// - The bit field must fit in a 32-bits word: `pos + len < 32`.
|
/// - The bit field must fit in a 32-bits word: `pos + len < 32`.
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
/// The position of the bit field.
|
/// The position of the bit field.
|
||||||
pub pos: usize,
|
pub pos: Nat,
|
||||||
|
|
||||||
/// The length of the bit field.
|
/// The length of the bit field.
|
||||||
pub len: usize,
|
pub len: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field {
|
impl Field {
|
||||||
/// Reads the value of a bit field.
|
/// Reads the value of a bit field.
|
||||||
pub fn get(&self, word: u32) -> usize {
|
pub fn get(&self, word: Word) -> Nat {
|
||||||
((word >> self.pos) & self.mask()) as usize
|
(word.0 >> self.pos) & self.mask()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value of a bit field.
|
/// Sets the value of a bit field.
|
||||||
@@ -43,12 +46,11 @@ impl Field {
|
|||||||
///
|
///
|
||||||
/// - The value must fit in the bit field: `num_bits(value) < self.len`.
|
/// - The value must fit in the bit field: `num_bits(value) < self.len`.
|
||||||
/// - The value must only change bits from 1 to 0: `self.get(*word) & value == value`.
|
/// - The value must only change bits from 1 to 0: `self.get(*word) & value == value`.
|
||||||
pub fn set(&self, word: &mut u32, value: usize) {
|
pub fn set(&self, word: &mut Word, value: Nat) {
|
||||||
let value = value as u32;
|
|
||||||
debug_assert_eq!(value & self.mask(), value);
|
debug_assert_eq!(value & self.mask(), value);
|
||||||
let mask = !(self.mask() << self.pos);
|
let mask = !(self.mask() << self.pos);
|
||||||
*word &= mask | (value << self.pos);
|
word.0 &= mask | (value << self.pos);
|
||||||
debug_assert_eq!(self.get(*word), value as usize);
|
debug_assert_eq!(self.get(*word), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a bit mask the length of the bit field.
|
/// Returns a bit mask the length of the bit field.
|
||||||
@@ -70,17 +72,17 @@ pub struct ConstField {
|
|||||||
pub field: Field,
|
pub field: Field,
|
||||||
|
|
||||||
/// The constant value.
|
/// The constant value.
|
||||||
pub value: usize,
|
pub value: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstField {
|
impl ConstField {
|
||||||
/// Checks that the bit field has its value.
|
/// Checks that the bit field has its value.
|
||||||
pub fn check(&self, word: u32) -> bool {
|
pub fn check(&self, word: Word) -> bool {
|
||||||
self.field.get(word) == self.value
|
self.field.get(word) == self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the bit field to its value.
|
/// Sets the bit field to its value.
|
||||||
pub fn set(&self, word: &mut u32) {
|
pub fn set(&self, word: &mut Word) {
|
||||||
self.field.set(word, self.value);
|
self.field.set(word, self.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,18 +94,18 @@ impl ConstField {
|
|||||||
/// - The bit must fit in a 32-bits word: `pos < 32`.
|
/// - The bit must fit in a 32-bits word: `pos < 32`.
|
||||||
pub struct Bit {
|
pub struct Bit {
|
||||||
/// The position of the bit.
|
/// The position of the bit.
|
||||||
pub pos: usize,
|
pub pos: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bit {
|
impl Bit {
|
||||||
/// Returns whether the value of the bit is zero.
|
/// Returns whether the value of the bit is zero.
|
||||||
pub fn get(&self, word: u32) -> bool {
|
pub fn get(&self, word: Word) -> bool {
|
||||||
word & (1 << self.pos) == 0
|
word.0 & (1 << self.pos) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value of the bit to zero.
|
/// Sets the value of the bit to zero.
|
||||||
pub fn set(&self, word: &mut u32) {
|
pub fn set(&self, word: &mut Word) {
|
||||||
*word &= !(1 << self.pos);
|
word.0 &= !(1 << self.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,9 +125,9 @@ impl Checksum {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns `InvalidStorage` if the external increment would be negative.
|
/// Returns `InvalidStorage` if the external increment would be negative.
|
||||||
pub fn get(&self, word: u32) -> StoreResult<usize> {
|
pub fn get(&self, word: Word) -> StoreResult<Nat> {
|
||||||
let checksum = self.field.get(word);
|
let checksum = self.field.get(word);
|
||||||
let zeros = word.count_zeros() as usize - (self.field.len - checksum.count_ones() as usize);
|
let zeros = word.0.count_zeros() - (self.field.len - checksum.count_ones());
|
||||||
checksum
|
checksum
|
||||||
.checked_sub(zeros)
|
.checked_sub(zeros)
|
||||||
.ok_or(StoreError::InvalidStorage)
|
.ok_or(StoreError::InvalidStorage)
|
||||||
@@ -139,9 +141,9 @@ impl Checksum {
|
|||||||
/// self.field.mask()`.
|
/// self.field.mask()`.
|
||||||
/// - The checksum value should fit in the checksum bit field: `num_bits(word.count_zeros() +
|
/// - The checksum value should fit in the checksum bit field: `num_bits(word.count_zeros() +
|
||||||
/// value) < self.field.len`.
|
/// value) < self.field.len`.
|
||||||
pub fn set(&self, word: &mut u32, value: usize) {
|
pub fn set(&self, word: &mut Word, value: Nat) {
|
||||||
debug_assert_eq!(self.field.get(*word), self.field.mask() as usize);
|
debug_assert_eq!(self.field.get(*word), self.field.mask());
|
||||||
self.field.set(word, word.count_zeros() as usize + value);
|
self.field.set(word, word.0.count_zeros() + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ impl Checksum {
|
|||||||
#[cfg(any(doc, test))]
|
#[cfg(any(doc, test))]
|
||||||
pub struct Length {
|
pub struct Length {
|
||||||
/// The position of the next available bit.
|
/// The position of the next available bit.
|
||||||
pub pos: usize,
|
pub pos: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helps defining contiguous bit fields.
|
/// Helps defining contiguous bit fields.
|
||||||
@@ -266,13 +268,13 @@ macro_rules! bitfield_impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Counts the number of bits equal to zero in a byte slice.
|
/// Counts the number of bits equal to zero in a byte slice.
|
||||||
pub fn count_zeros(slice: &[u8]) -> usize {
|
pub fn count_zeros(slice: &[u8]) -> Nat {
|
||||||
slice.iter().map(|&x| x.count_zeros() as usize).sum()
|
slice.iter().map(|&x| x.count_zeros()).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of bits necessary to represent a number.
|
/// Returns the number of bits necessary to represent a number.
|
||||||
pub const fn num_bits(x: usize) -> usize {
|
pub const fn num_bits(x: Nat) -> Nat {
|
||||||
8 * core::mem::size_of::<usize>() - x.leading_zeros() as usize
|
8 * core::mem::size_of::<Nat>() as Nat - x.leading_zeros()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -282,14 +284,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn field_ok() {
|
fn field_ok() {
|
||||||
let field = Field { pos: 3, len: 5 };
|
let field = Field { pos: 3, len: 5 };
|
||||||
assert_eq!(field.get(0x00000000), 0);
|
assert_eq!(field.get(Word(0x00000000)), 0);
|
||||||
assert_eq!(field.get(0x00000007), 0);
|
assert_eq!(field.get(Word(0x00000007)), 0);
|
||||||
assert_eq!(field.get(0x00000008), 1);
|
assert_eq!(field.get(Word(0x00000008)), 1);
|
||||||
assert_eq!(field.get(0x000000f8), 0x1f);
|
assert_eq!(field.get(Word(0x000000f8)), 0x1f);
|
||||||
assert_eq!(field.get(0x0000ff37), 6);
|
assert_eq!(field.get(Word(0x0000ff37)), 6);
|
||||||
let mut word = 0xffffffff;
|
let mut word = Word(0xffffffff);
|
||||||
field.set(&mut word, 3);
|
field.set(&mut word, 3);
|
||||||
assert_eq!(word, 0xffffff1f);
|
assert_eq!(word, Word(0xffffff1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -298,25 +300,25 @@ mod tests {
|
|||||||
field: Field { pos: 3, len: 5 },
|
field: Field { pos: 3, len: 5 },
|
||||||
value: 9,
|
value: 9,
|
||||||
};
|
};
|
||||||
assert!(!field.check(0x00000000));
|
assert!(!field.check(Word(0x00000000)));
|
||||||
assert!(!field.check(0x0000ffff));
|
assert!(!field.check(Word(0x0000ffff)));
|
||||||
assert!(field.check(0x00000048));
|
assert!(field.check(Word(0x00000048)));
|
||||||
assert!(field.check(0x0000ff4f));
|
assert!(field.check(Word(0x0000ff4f)));
|
||||||
let mut word = 0xffffffff;
|
let mut word = Word(0xffffffff);
|
||||||
field.set(&mut word);
|
field.set(&mut word);
|
||||||
assert_eq!(word, 0xffffff4f);
|
assert_eq!(word, Word(0xffffff4f));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bit_ok() {
|
fn bit_ok() {
|
||||||
let bit = Bit { pos: 3 };
|
let bit = Bit { pos: 3 };
|
||||||
assert!(bit.get(0x00000000));
|
assert!(bit.get(Word(0x00000000)));
|
||||||
assert!(bit.get(0xfffffff7));
|
assert!(bit.get(Word(0xfffffff7)));
|
||||||
assert!(!bit.get(0x00000008));
|
assert!(!bit.get(Word(0x00000008)));
|
||||||
assert!(!bit.get(0xffffffff));
|
assert!(!bit.get(Word(0xffffffff)));
|
||||||
let mut word = 0xffffffff;
|
let mut word = Word(0xffffffff);
|
||||||
bit.set(&mut word);
|
bit.set(&mut word);
|
||||||
assert_eq!(word, 0xfffffff7);
|
assert_eq!(word, Word(0xfffffff7));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -324,15 +326,15 @@ mod tests {
|
|||||||
let field = Checksum {
|
let field = Checksum {
|
||||||
field: Field { pos: 3, len: 5 },
|
field: Field { pos: 3, len: 5 },
|
||||||
};
|
};
|
||||||
assert_eq!(field.get(0x00000000), Err(StoreError::InvalidStorage));
|
assert_eq!(field.get(Word(0x00000000)), Err(StoreError::InvalidStorage));
|
||||||
assert_eq!(field.get(0xffffffff), Ok(31));
|
assert_eq!(field.get(Word(0xffffffff)), Ok(31));
|
||||||
assert_eq!(field.get(0xffffff07), Ok(0));
|
assert_eq!(field.get(Word(0xffffff07)), Ok(0));
|
||||||
assert_eq!(field.get(0xffffff0f), Ok(1));
|
assert_eq!(field.get(Word(0xffffff0f)), Ok(1));
|
||||||
assert_eq!(field.get(0x00ffff67), Ok(4));
|
assert_eq!(field.get(Word(0x00ffff67)), Ok(4));
|
||||||
assert_eq!(field.get(0x7fffff07), Err(StoreError::InvalidStorage));
|
assert_eq!(field.get(Word(0x7fffff07)), Err(StoreError::InvalidStorage));
|
||||||
let mut word = 0x0fffffff;
|
let mut word = Word(0x0fffffff);
|
||||||
field.set(&mut word, 4);
|
field.set(&mut word, 4);
|
||||||
assert_eq!(word, 0x0fffff47);
|
assert_eq!(word, Word(0x0fffff47));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -348,8 +348,6 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod bitfield;
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
mod format;
|
mod format;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -362,3 +360,25 @@ pub use self::buffer::{BufferCorruptFunction, BufferOptions, BufferStorage};
|
|||||||
pub use self::model::{StoreModel, StoreOperation};
|
pub use self::model::{StoreModel, StoreOperation};
|
||||||
pub use self::storage::{Storage, StorageError, StorageIndex, StorageResult};
|
pub use self::storage::{Storage, StorageError, StorageIndex, StorageResult};
|
||||||
pub use self::store::{StoreError, StoreRatio, StoreResult, StoreUpdate};
|
pub use self::store::{StoreError, StoreRatio, StoreResult, StoreUpdate};
|
||||||
|
|
||||||
|
/// Internal representation of natural numbers.
|
||||||
|
///
|
||||||
|
/// In Rust natural numbers are represented as `usize`. However, internally we represent them as
|
||||||
|
/// `u32`. This is done to preserve semantics across different targets. This is useful when tests
|
||||||
|
/// run with `usize = u64` while the actual target has `usize = u32`.
|
||||||
|
///
|
||||||
|
/// To avoid too many conversions between `usize` and `Nat` which are necessary when interfacing
|
||||||
|
/// with Rust, `usize` is used instead of `Nat` in code meant only for tests.
|
||||||
|
///
|
||||||
|
/// Currently, the store only supports targets with `usize = u32`.
|
||||||
|
type Nat = u32;
|
||||||
|
|
||||||
|
/// Returns the internal representation of a Rust natural number.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the conversion overflows.
|
||||||
|
fn usize_to_nat(x: usize) -> Nat {
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
Nat::try_from(x).unwrap()
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::format::Format;
|
use crate::format::Format;
|
||||||
use crate::{StoreError, StoreRatio, StoreResult, StoreUpdate};
|
use crate::{usize_to_nat, StoreError, StoreRatio, StoreResult, StoreUpdate};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
/// Models the mutable operations of a store.
|
/// Models the mutable operations of a store.
|
||||||
@@ -79,14 +79,14 @@ impl StoreModel {
|
|||||||
/// Returns the capacity according to the model.
|
/// Returns the capacity according to the model.
|
||||||
pub fn capacity(&self) -> StoreRatio {
|
pub fn capacity(&self) -> StoreRatio {
|
||||||
let total = self.format.total_capacity();
|
let total = self.format.total_capacity();
|
||||||
let used: usize = self.content.values().map(|x| self.entry_size(x)).sum();
|
let used = usize_to_nat(self.content.values().map(|x| self.entry_size(x)).sum());
|
||||||
StoreRatio { used, total }
|
StoreRatio { used, total }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies a transaction.
|
/// Applies a transaction.
|
||||||
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> {
|
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> {
|
||||||
// Fail if too many updates.
|
// Fail if too many updates.
|
||||||
if updates.len() > self.format.max_updates() {
|
if updates.len() > self.format.max_updates() as usize {
|
||||||
return Err(StoreError::InvalidArgument);
|
return Err(StoreError::InvalidArgument);
|
||||||
}
|
}
|
||||||
// Fail if an update is invalid.
|
// Fail if an update is invalid.
|
||||||
@@ -130,7 +130,7 @@ impl StoreModel {
|
|||||||
|
|
||||||
/// Applies a clear operation.
|
/// Applies a clear operation.
|
||||||
fn clear(&mut self, min_key: usize) -> StoreResult<()> {
|
fn clear(&mut self, min_key: usize) -> StoreResult<()> {
|
||||||
if min_key > self.format.max_key() {
|
if min_key > self.format.max_key() as usize {
|
||||||
return Err(StoreError::InvalidArgument);
|
return Err(StoreError::InvalidArgument);
|
||||||
}
|
}
|
||||||
self.content.retain(|&k, _| k < min_key);
|
self.content.retain(|&k, _| k < min_key);
|
||||||
@@ -155,14 +155,14 @@ impl StoreModel {
|
|||||||
|
|
||||||
/// Returns the word capacity of an entry.
|
/// Returns the word capacity of an entry.
|
||||||
fn entry_size(&self, value: &[u8]) -> usize {
|
fn entry_size(&self, value: &[u8]) -> usize {
|
||||||
1 + self.format.bytes_to_words(value.len())
|
1 + self.format.bytes_to_words(usize_to_nat(value.len())) as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether an update is valid.
|
/// Returns whether an update is valid.
|
||||||
fn update_valid(&self, update: &StoreUpdate) -> bool {
|
fn update_valid(&self, update: &StoreUpdate) -> bool {
|
||||||
update.key() <= self.format.max_key()
|
update.key() <= self.format.max_key() as usize
|
||||||
&& update
|
&& update
|
||||||
.value()
|
.value()
|
||||||
.map_or(true, |x| x.len() <= self.format.max_value_len())
|
.map_or(true, |x| x.len() <= self.format.max_value_len() as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,13 @@ pub type StorageResult<T> = Result<T, StorageError>;
|
|||||||
/// Abstracts a flash storage.
|
/// Abstracts a flash storage.
|
||||||
pub trait Storage {
|
pub trait Storage {
|
||||||
/// The size of a word in bytes.
|
/// The size of a word in bytes.
|
||||||
|
///
|
||||||
|
/// A word is the smallest unit of writable flash.
|
||||||
fn word_size(&self) -> usize;
|
fn word_size(&self) -> usize;
|
||||||
|
|
||||||
/// The size of a page in bytes.
|
/// The size of a page in bytes.
|
||||||
|
///
|
||||||
|
/// A page is the smallest unit of erasable flash.
|
||||||
fn page_size(&self) -> usize;
|
fn page_size(&self) -> usize;
|
||||||
|
|
||||||
/// The number of pages in the storage.
|
/// The number of pages in the storage.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::StorageError;
|
use crate::{Nat, StorageError};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
/// Errors returned by store operations.
|
/// Errors returned by store operations.
|
||||||
@@ -78,26 +78,26 @@ pub type StoreResult<T> = Result<T, StoreError>;
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct StoreRatio {
|
pub struct StoreRatio {
|
||||||
/// How much of the metric is used.
|
/// How much of the metric is used.
|
||||||
pub(crate) used: usize,
|
pub(crate) used: Nat,
|
||||||
|
|
||||||
/// How much of the metric can be used at most.
|
/// How much of the metric can be used at most.
|
||||||
pub(crate) total: usize,
|
pub(crate) total: Nat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StoreRatio {
|
impl StoreRatio {
|
||||||
/// How much of the metric is used.
|
/// How much of the metric is used.
|
||||||
pub fn used(self) -> usize {
|
pub fn used(self) -> usize {
|
||||||
self.used
|
self.used as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much of the metric can be used at most.
|
/// How much of the metric can be used at most.
|
||||||
pub fn total(self) -> usize {
|
pub fn total(self) -> usize {
|
||||||
self.total
|
self.total as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How much of the metric is remaining.
|
/// How much of the metric is remaining.
|
||||||
pub fn remaining(self) -> usize {
|
pub fn remaining(self) -> usize {
|
||||||
self.total - self.used
|
(self.total - self.used) as usize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user