wasefire_store/
format.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Storage representation of a store.
16
17#[macro_use]
18pub mod bitfield;
19
20use alloc::vec::Vec;
21use core::borrow::Borrow;
22use core::cmp::min;
23use core::convert::TryFrom;
24
25use wasefire_error::Error;
26
27#[cfg(test)]
28use self::bitfield::Length;
29use self::bitfield::{Bit, Checksum, ConstField, Field, count_zeros, num_bits};
30use crate::{Nat, Storage, StorageIndex, StoreUpdate, usize_to_nat};
31
32/// Internal representation of a word in flash.
33///
34/// Currently, the store only supports storages where a word is 32 bits, i.e. the [word
35/// size](Storage::word_size) is 4 bytes.
36#[allow(clippy::upper_case_acronyms)]
37type WORD = u32;
38
39/// Abstract representation of a word in flash.
40///
41/// This type is kept abstract to avoid possible confusion with [`Nat`] if they happen to have the
42/// same representation. This is because they have different semantics, [`Nat`] represents natural
43/// numbers while `Word` represents sequences of bits (and thus has no arithmetic).
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
45pub struct Word(WORD);
46
47/// Byte slice representation of a word in flash.
48///
49/// The slice is in little-endian representation.
50pub type WordSlice = [u8; core::mem::size_of::<WORD>()];
51
52impl Word {
53    /// Converts a byte slice into a word.
54    ///
55    /// # Panics
56    ///
57    /// Panics if `slice.len()` is not [`WORD_SIZE`] bytes.
58    pub fn from_slice(slice: &[u8]) -> Word {
59        Word(WORD::from_le_bytes(<WordSlice>::try_from(slice).unwrap()))
60    }
61
62    /// Converts a word into a byte slice.
63    pub fn as_slice(self) -> WordSlice {
64        self.0.to_le_bytes()
65    }
66}
67
68/// Size of a word in bytes.
69///
70/// Currently, the store only supports storages where the [word size](Storage::word_size) is 4
71/// bytes.
72pub const WORD_SIZE: Nat = core::mem::size_of::<WORD>() as Nat;
73
74/// Minimum number of words per page.
75///
76/// Currently, the store only supports storages where pages have at least 8 [words](WORD_SIZE), i.e.
77/// the [page size](Storage::page_size) is at least 32 bytes.
78pub const MIN_PAGE_SIZE: Nat = 8;
79
80/// Maximum size of a page in bytes.
81///
82/// Currently, the store only supports storages where pages have at most 1024 [words](WORD_SIZE),
83/// i.e. the [page size](Storage::page_size) is at most 4096 bytes.
84pub const MAX_PAGE_SIZE: Nat = 4096;
85
86/// Maximum number of erase cycles.
87///
88/// Currently, the store only supports storages where the [maximum number of erase
89/// cycles](Storage::max_page_erases) fits in 16 bits, i.e. it is at most 65535.
90pub const MAX_ERASE_CYCLE: Nat = 65535;
91
92/// Minimum number of pages.
93///
94/// Currently, the store only supports storages where the [number of pages](Storage::num_pages) is
95/// at least 3.
96pub const MIN_NUM_PAGES: Nat = 3;
97
98/// Maximum page index.
99///
100/// Currently, the store only supports storages where the [number of pages](Storage::num_pages) is
101/// at most 64, i.e. the maximum page index is 63.
102pub const MAX_PAGE_INDEX: Nat = 63;
103
104/// Maximum key index.
105///
106/// Currently, the store only supports 4096 keys, i.e. the maximum key index is 4095.
107pub const MAX_KEY_INDEX: Nat = 4095;
108
109/// Maximum length in bytes of a user payload.
110///
111/// Currently, the store only supports values at most 1023 bytes long. This may be further reduced
112/// depending on the [page size](Storage::page_size), see [`Format::max_value_len`].
113pub const MAX_VALUE_LEN: Nat = 1023;
114
115/// Maximum number of updates per transaction.
116///
117/// Currently, the store only supports transactions with at most 31 updates.
118pub const MAX_UPDATES: Nat = 31;
119
120/// Maximum number of words per virtual page.
121///
122/// A virtual page has [`CONTENT_WORD`] less [words](WORD_SIZE) than the storage [page
123/// size](Storage::page_size). Those words are used to store the page header. Since a page has at
124/// least [8](MIN_PAGE_SIZE) words, a virtual page has at least 6 words.
125pub const MAX_VIRT_PAGE_SIZE: Nat = MAX_PAGE_SIZE / WORD_SIZE - CONTENT_WORD;
126
127/// Word with all bits set to one.
128///
129/// After a page is erased, all words are equal to this value.
130pub const ERASED_WORD: Word = Word(!(0 as WORD));
131
132/// Helpers for a given storage configuration.
133#[derive(Clone, Debug)]
134pub struct Format {
135    /// The size in bytes of a page in the storage.
136    ///
137    /// # Invariant
138    ///
139    /// - [Words](WORD_SIZE) divide a page evenly.
140    /// - There are at least [`MIN_PAGE_SIZE`] words in a page.
141    /// - There are at most [`MAX_PAGE_SIZE`] bytes in a page.
142    page_size: Nat,
143
144    /// The number of pages in the storage.
145    ///
146    /// # Invariant
147    ///
148    /// - There are at least [`MIN_NUM_PAGES`] pages.
149    /// - There are at most [`MAX_PAGE_INDEX`] + 1 pages.
150    num_pages: Nat,
151
152    /// The maximum number of times a page can be erased.
153    ///
154    /// # Invariant
155    ///
156    /// - A page can be erased at most [`MAX_ERASE_CYCLE`] times.
157    max_page_erases: Nat,
158}
159
160impl Format {
161    /// Extracts the format from a storage.
162    ///
163    /// Returns `None` if the storage is not [supported](Format::is_storage_supported).
164    pub fn new<S: Storage>(storage: &S) -> Option<Format> {
165        if Format::is_storage_supported(storage) {
166            Some(Format {
167                page_size: usize_to_nat(storage.page_size()),
168                num_pages: usize_to_nat(storage.num_pages()),
169                max_page_erases: usize_to_nat(storage.max_page_erases()),
170            })
171        } else {
172            None
173        }
174    }
175
176    /// Returns whether a storage is supported.
177    ///
178    /// A storage is supported if the following conditions hold:
179    /// - The [`Storage::word_size`] is [`WORD_SIZE`] bytes.
180    /// - The [`Storage::word_size`] evenly divides the [`Storage::page_size`].
181    /// - The [`Storage::page_size`] is between [`MIN_PAGE_SIZE`] words and [`MAX_PAGE_SIZE`] bytes.
182    /// - The [`Storage::num_pages`] is between [`MIN_NUM_PAGES`] and [`MAX_PAGE_INDEX`] + 1.
183    /// - The [`Storage::max_word_writes`] is at least 2.
184    /// - The [`Storage::max_page_erases`] is at most [`MAX_ERASE_CYCLE`].
185    pub fn is_storage_supported<S: Storage>(storage: &S) -> bool {
186        let word_size = usize_to_nat(storage.word_size());
187        let page_size = usize_to_nat(storage.page_size());
188        let num_pages = usize_to_nat(storage.num_pages());
189        let max_word_writes = usize_to_nat(storage.max_word_writes());
190        let max_page_erases = usize_to_nat(storage.max_page_erases());
191        word_size == WORD_SIZE
192            && page_size.is_multiple_of(word_size)
193            && (MIN_PAGE_SIZE * word_size ..= MAX_PAGE_SIZE).contains(&page_size)
194            && (MIN_NUM_PAGES ..= MAX_PAGE_INDEX + 1).contains(&num_pages)
195            && max_word_writes >= 2
196            && max_page_erases <= MAX_ERASE_CYCLE
197    }
198
199    /// The size of a word in bytes.
200    pub fn word_size(&self) -> Nat {
201        WORD_SIZE
202    }
203
204    /// The size of a page in bytes.
205    ///
206    /// This is at least [`MIN_PAGE_SIZE`] [words](WORD_SIZE) and at most [`MAX_PAGE_SIZE`] bytes.
207    pub fn page_size(&self) -> Nat {
208        self.page_size
209    }
210
211    /// The number of pages in the storage, denoted by N.
212    ///
213    /// We have [`MIN_NUM_PAGES`] <= N <= [`MAX_PAGE_INDEX`] + 1.
214    pub fn num_pages(&self) -> Nat {
215        self.num_pages
216    }
217
218    /// The maximum page index.
219    ///
220    /// This is at least [`MIN_NUM_PAGES`] - 1 and at most [`MAX_PAGE_INDEX`].
221    pub fn max_page(&self) -> Nat {
222        self.num_pages - 1
223    }
224
225    /// The maximum number of times a page can be erased, denoted by E.
226    ///
227    /// We have E <= [`MAX_ERASE_CYCLE`].
228    pub fn max_page_erases(&self) -> Nat {
229        self.max_page_erases
230    }
231
232    /// The maximum key.
233    pub fn max_key(&self) -> Nat {
234        MAX_KEY_INDEX
235    }
236
237    /// The maximum number of updates per transaction.
238    pub fn max_updates(&self) -> Nat {
239        MAX_UPDATES
240    }
241
242    /// The size of a virtual page in words, denoted by Q.
243    ///
244    /// A virtual page is stored in a physical page after the page header.
245    ///
246    /// We have [`MIN_PAGE_SIZE`] - 2 <= Q <= [`MAX_VIRT_PAGE_SIZE`].
247    pub fn virt_page_size(&self) -> Nat {
248        self.page_size() / self.word_size() - CONTENT_WORD
249    }
250
251    /// The maximum length in bytes of a user payload.
252    ///
253    /// This is at least [`MIN_PAGE_SIZE`] - 3 [words](WORD_SIZE) and at most [`MAX_VALUE_LEN`].
254    pub fn max_value_len(&self) -> Nat {
255        min((self.virt_page_size() - 1) * self.word_size(), MAX_VALUE_LEN)
256    }
257
258    /// The maximum prefix length in words, denoted by M.
259    ///
260    /// A prefix is the first words of a virtual page that belong to the last entry of the previous
261    /// virtual page. This happens because entries may overlap up to 2 virtual pages.
262    ///
263    /// We have [`MIN_PAGE_SIZE`] - 3 <= M < Q.
264    pub fn max_prefix_len(&self) -> Nat {
265        self.bytes_to_words(self.max_value_len())
266    }
267
268    /// The virtual window size in words, denoted by W.
269    ///
270    /// This is the span of virtual storage that is accessible. In particular, all store content
271    /// fits within this window.
272    ///
273    /// We have W = (N - 1) * Q - M.
274    pub fn window_size(&self) -> Nat {
275        (self.num_pages() - 1) * self.virt_page_size() - self.max_prefix_len()
276    }
277
278    /// The total virtual capacity in words, denoted by V.
279    ///
280    /// This is the span of virtual storage after which we trigger a compaction. This is smaller
281    /// than the virtual window because compaction may transiently overflow out of this virtual
282    /// capacity.
283    ///
284    /// We have V = W - (N - 1) = (N - 1) * (Q - 1) - M.
285    pub fn virt_size(&self) -> Nat {
286        (self.num_pages() - 1) * (self.virt_page_size() - 1) - self.max_prefix_len()
287    }
288
289    /// The total user capacity in words, denoted by C.
290    ///
291    /// We have C = V - N = (N - 1) * (Q - 2) - M - 1.
292    ///
293    /// We can show C >= (N - 2) * (Q - 2) - 2 with the following steps:
294    /// - M <= Q - 1 from M < Q from [M](Format::max_prefix_len)'s definition
295    /// - C >= (N - 1) * (Q - 2) - (Q - 1) - 1 from C's definition
296    /// - C >= (N - 2) * (Q - 2) - 2 by calculus
297    pub fn total_capacity(&self) -> Nat {
298        // From the virtual capacity, we reserve N - 1 words for `Erase` entries and 1 word for a
299        // `Clear` entry.
300        self.virt_size() - self.num_pages()
301    }
302
303    /// The total virtual lifetime in words, denoted by L.
304    ///
305    /// We have L = (E * N + N - 1) * Q.
306    pub fn total_lifetime(&self) -> Position {
307        Position::new(self, self.max_page_erases(), self.num_pages() - 1, 0)
308    }
309
310    /// Returns the word position of the first entry of a page.
311    pub fn page_head(&self, init: InitInfo, page: Nat) -> Position {
312        Position::new(self, init.cycle, page, init.prefix)
313    }
314
315    /// Returns the storage index of the init info of a page.
316    pub fn index_init(&self, page: Nat) -> StorageIndex {
317        let byte = INIT_WORD * self.word_size();
318        StorageIndex { page: page as usize, byte: byte as usize }
319    }
320
321    /// Parses the init info of a page from its storage representation.
322    pub fn parse_init(&self, word: Word) -> Result<WordState<InitInfo>, Error> {
323        Ok(if word == ERASED_WORD {
324            WordState::Erased
325        } else if WORD_CHECKSUM.get(word)? != 0 {
326            WordState::Partial
327        } else {
328            let cycle = INIT_CYCLE.get(word);
329            let prefix = INIT_PREFIX.get(word);
330            if cycle > self.max_page_erases() || prefix > self.max_prefix_len() {
331                return Err(crate::INVALID_STORAGE);
332            }
333            WordState::Valid(InitInfo { cycle, prefix })
334        })
335    }
336
337    /// Builds the storage representation of an init info.
338    pub fn build_init(&self, init: InitInfo) -> Result<WordSlice, Error> {
339        let mut word = ERASED_WORD;
340        INIT_CYCLE.set(&mut word, init.cycle)?;
341        INIT_PREFIX.set(&mut word, init.prefix)?;
342        WORD_CHECKSUM.set(&mut word, 0)?;
343        Ok(word.as_slice())
344    }
345
346    /// Returns the storage index of the compact info of a page.
347    pub fn index_compact(&self, page: Nat) -> StorageIndex {
348        let byte = COMPACT_WORD * self.word_size();
349        StorageIndex { page: page as usize, byte: byte as usize }
350    }
351
352    /// Parses the compact info of a page from its storage representation.
353    pub fn parse_compact(&self, word: Word) -> Result<WordState<CompactInfo>, Error> {
354        Ok(if word == ERASED_WORD {
355            WordState::Erased
356        } else if WORD_CHECKSUM.get(word)? != 0 {
357            WordState::Partial
358        } else {
359            let tail = COMPACT_TAIL.get(word);
360            if tail > self.window_size() {
361                return Err(crate::INVALID_STORAGE);
362            }
363            WordState::Valid(CompactInfo { tail })
364        })
365    }
366
367    /// Builds the storage representation of a compact info.
368    pub fn build_compact(&self, compact: CompactInfo) -> Result<WordSlice, Error> {
369        let mut word = ERASED_WORD;
370        COMPACT_TAIL.set(&mut word, compact.tail)?;
371        WORD_CHECKSUM.set(&mut word, 0)?;
372        Ok(word.as_slice())
373    }
374
375    /// Builds the storage representation of an internal entry.
376    pub fn build_internal(&self, internal: InternalEntry) -> Result<WordSlice, Error> {
377        let mut word = ERASED_WORD;
378        match internal {
379            InternalEntry::Erase { page } => {
380                ID_ERASE.set(&mut word)?;
381                ERASE_PAGE.set(&mut word, page)?;
382            }
383            InternalEntry::Clear { min_key } => {
384                ID_CLEAR.set(&mut word)?;
385                CLEAR_MIN_KEY.set(&mut word, min_key)?;
386            }
387            InternalEntry::Marker { count } => {
388                ID_MARKER.set(&mut word)?;
389                MARKER_COUNT.set(&mut word, count)?;
390            }
391            InternalEntry::Remove { key } => {
392                ID_REMOVE.set(&mut word)?;
393                REMOVE_KEY.set(&mut word, key)?;
394            }
395        }
396        WORD_CHECKSUM.set(&mut word, 0)?;
397        Ok(word.as_slice())
398    }
399
400    /// Parses the first word of an entry from its storage representation.
401    pub fn parse_word(&self, word: Word) -> Result<WordState<ParsedWord>, Error> {
402        let valid = if ID_PADDING.check(word) {
403            ParsedWord::Padding(Padding { length: 0 })
404        } else if ID_HEADER.check(word) {
405            if HEADER_DELETED.get(word) {
406                let length = HEADER_LENGTH.get(word);
407                if length > self.max_value_len() {
408                    return Err(crate::INVALID_STORAGE);
409                }
410                let length = self.bytes_to_words(length);
411                ParsedWord::Padding(Padding { length })
412            } else {
413                let flipped = HEADER_FLIPPED.get(word);
414                let length = HEADER_LENGTH.get(word);
415                let key = HEADER_KEY.get(word);
416                let checksum = HEADER_CHECKSUM.get(word)?;
417                ParsedWord::Header(Header { flipped, length, key, checksum })
418            }
419        } else if ID_ERASE.check(word) {
420            let page = ERASE_PAGE.get(word);
421            ParsedWord::Internal(InternalEntry::Erase { page })
422        } else if ID_CLEAR.check(word) {
423            let min_key = CLEAR_MIN_KEY.get(word);
424            ParsedWord::Internal(InternalEntry::Clear { min_key })
425        } else if ID_MARKER.check(word) {
426            let count = MARKER_COUNT.get(word);
427            ParsedWord::Internal(InternalEntry::Marker { count })
428        } else if ID_REMOVE.check(word) {
429            let key = REMOVE_KEY.get(word);
430            ParsedWord::Internal(InternalEntry::Remove { key })
431        } else if word == ERASED_WORD {
432            return Ok(WordState::Erased);
433        } else {
434            return Ok(WordState::Partial);
435        };
436        if let ParsedWord::Internal(internal) = &valid {
437            if WORD_CHECKSUM.get(word)? != 0 {
438                return Ok(WordState::Partial);
439            }
440            let invalid = match internal {
441                InternalEntry::Erase { page } => *page > self.max_page(),
442                InternalEntry::Clear { min_key } => *min_key > self.max_key(),
443                InternalEntry::Marker { count } => *count > MAX_UPDATES,
444                InternalEntry::Remove { key } => *key > self.max_key(),
445            };
446            if invalid {
447                return Err(crate::INVALID_STORAGE);
448            }
449        }
450        Ok(WordState::Valid(valid))
451    }
452
453    /// Builds the storage representation of a user entry.
454    pub fn build_user(&self, key: Nat, value: &[u8]) -> Result<Vec<u8>, Error> {
455        let length = usize_to_nat(value.len());
456        let word_size = self.word_size();
457        let footer = self.bytes_to_words(length);
458        let mut result = vec![0xff; ((1 + footer) * word_size) as usize];
459        result[word_size as usize ..][.. length as usize].copy_from_slice(value);
460        let mut word = ERASED_WORD;
461        ID_HEADER.set(&mut word)?;
462        if footer > 0 && is_erased(&result[(footer * word_size) as usize ..]) {
463            HEADER_FLIPPED.set(&mut word);
464            *result.last_mut().unwrap() = 0x7f;
465        }
466        HEADER_LENGTH.set(&mut word, length)?;
467        HEADER_KEY.set(&mut word, key)?;
468        HEADER_CHECKSUM.set(&mut word, count_zeros(&result[(footer * word_size) as usize ..]))?;
469        result[.. word_size as usize].copy_from_slice(&word.as_slice());
470        Ok(result)
471    }
472
473    /// Sets the padding bit in the first word of a user entry.
474    pub fn set_padding(&self, word: &mut Word) -> Result<(), Error> {
475        ID_PADDING.set(word)
476    }
477
478    /// Sets the deleted bit in the first word of a user entry.
479    pub fn set_deleted(&self, word: &mut Word) {
480        HEADER_DELETED.set(word);
481    }
482
483    /// Returns the capacity required by a transaction.
484    pub fn transaction_capacity<ByteSlice: Borrow<[u8]>>(
485        &self, updates: &[StoreUpdate<ByteSlice>],
486    ) -> Nat {
487        match updates.len() {
488            // An empty transaction doesn't consume anything.
489            0 => 0,
490            // Transactions with a single update are optimized by avoiding a marker entry.
491            1 => match &updates[0] {
492                StoreUpdate::Insert { value, .. } => self.entry_size(value.borrow()),
493                // Transactions with a single update which is a removal don't consume anything.
494                StoreUpdate::Remove { .. } => 0,
495            },
496            // A transaction consumes one word for the marker entry in addition to its updates.
497            _ => 1 + updates.iter().map(|x| self.update_capacity(x)).sum::<Nat>(),
498        }
499    }
500
501    /// Returns the capacity of an update.
502    fn update_capacity<ByteSlice: Borrow<[u8]>>(&self, update: &StoreUpdate<ByteSlice>) -> Nat {
503        match update {
504            StoreUpdate::Insert { value, .. } => self.entry_size(value.borrow()),
505            StoreUpdate::Remove { .. } => 1,
506        }
507    }
508
509    /// Returns the size of a user entry given its value.
510    pub fn entry_size(&self, value: &[u8]) -> Nat {
511        1 + self.bytes_to_words(usize_to_nat(value.len()))
512    }
513
514    /// Checks if a transaction is valid and returns its sorted keys.
515    ///
516    /// Returns `None` if the transaction is invalid.
517    pub fn transaction_valid<ByteSlice: Borrow<[u8]>>(
518        &self, updates: &[StoreUpdate<ByteSlice>],
519    ) -> Option<Vec<Nat>> {
520        if usize_to_nat(updates.len()) > self.max_updates() {
521            return None;
522        }
523        let mut sorted_keys = Vec::with_capacity(updates.len());
524        for update in updates {
525            let key = usize_to_nat(update.key());
526            if key > self.max_key() {
527                return None;
528            }
529            if let Some(value) = update.value()
530                && usize_to_nat(value.len()) > self.max_value_len()
531            {
532                return None;
533            }
534            match sorted_keys.binary_search(&key) {
535                Ok(_) => return None,
536                Err(pos) => sorted_keys.insert(pos, key),
537            }
538        }
539        Some(sorted_keys)
540    }
541
542    /// Returns the minimum number of words to represent a given number of bytes.
543    ///
544    /// # Preconditions
545    ///
546    /// - `bytes` + [`Self::word_size`] does not overflow.
547    pub fn bytes_to_words(&self, bytes: Nat) -> Nat {
548        bytes.div_ceil(self.word_size())
549    }
550}
551
552/// The word index of the init info in a page.
553pub const INIT_WORD: Nat = 0;
554
555/// The word index of the compact info in a page.
556pub const COMPACT_WORD: Nat = 1;
557
558/// The word index of the content of a page.
559///
560/// This is also the length in words of the page header.
561pub const CONTENT_WORD: Nat = 2;
562
563/// The checksum for a single word.
564///
565/// Since checksums are the number of bits set to zero and a word is 32 bits, we need 5 bits to
566/// store numbers between 0 and 27 (which is 32 - 5).
567pub const WORD_CHECKSUM: Checksum = Checksum { field: Field { pos: 27, len: 5 } };
568
569// The fields of the init info of a page.
570bitfield! {
571    /// The number of times the page has been erased.
572    pub INIT_CYCLE: Field <= MAX_ERASE_CYCLE,
573
574    /// The word index of the first entry in this virtual page.
575    pub INIT_PREFIX: Field <= MAX_VALUE_LEN.div_ceil(WORD_SIZE),
576
577    #[cfg(test)]
578    pub LEN_INIT: Length,
579}
580
581// The fields of the compact info of a page.
582bitfield! {
583    /// The distance in words between head and tail at compaction.
584    ///
585    /// In particular, compaction copies non-deleted user entries from the head to the tail as long
586    /// as entries span the page to be compacted.
587    pub COMPACT_TAIL: Field <= MAX_VIRT_PAGE_SIZE * MAX_PAGE_INDEX,
588
589    #[cfg(test)]
590    pub LEN_COMPACT: Length,
591}
592
593// Overview of the first word of the different kind of entries.
594//
595// Each column represents a bit of the word. The first 2 lines give the position in hexadecimal of
596// the bit in the word (the exponent of 2 when the word is written in binary). Each entry starts
597// with the sequence of bits of its identifier. The dots following the identifier are the number of
598// bits necessary to hold the information of the entry (including the checksum). The remaining free
599// bits after the dots are not used by the entry.
600//
601//         0               1
602//         0123456789abcdef0123456789abcdef
603// padding 0
604//  header 10..............................
605//   erase 11000...........
606//   clear 11001.................
607//  marker 11010..........
608//  remove 11011.................
609//
610// NOTE: We could pad the internal entries to the right by extending their identifier. This permits
611// to free some space for shorter identifier for future kind of entries.
612
613// The fields of a padding entry.
614bitfield! {
615    /// The identifier for padding entries.
616    pub ID_PADDING: ConstField = [0],
617}
618
619// The fields of a user entry.
620bitfield! {
621    /// The identifier for user entries.
622    pub ID_HEADER: ConstField = [1 0],
623
624    /// Whether the user entry is deleted.
625    pub HEADER_DELETED: Bit,
626
627    /// Whether the last bit of the user data is flipped.
628    pub HEADER_FLIPPED: Bit,
629
630    /// The length in bytes of the user data.
631    // NOTE: It is possible to support values of length 1024 by having a separate kind of entries
632    // when the value is empty. We could then subtract one from the length here.
633    pub HEADER_LENGTH: Field <= MAX_VALUE_LEN,
634
635    /// The key of the user entry.
636    pub HEADER_KEY: Field <= MAX_KEY_INDEX,
637
638    /// The checksum of the user entry.
639    ///
640    /// This counts the number of bits set to zero in both the first and last words of the user
641    /// entry, except in the checksum itself. So it needs 6 bits to store numbers between 0 and 58.
642    // NOTE: It may be possible to save one bit by storing:
643    // - the footer checksum (as a field) if the value is not empty
644    // - the header checksum (as a checksum) if the value is empty
645    pub HEADER_CHECKSUM: Checksum <= 58,
646
647    #[cfg(test)]
648    pub LEN_HEADER: Length,
649}
650
651// The fields of an erase entry.
652bitfield! {
653    /// The identifier for erase entries.
654    pub ID_ERASE: ConstField = [1 1 0 0 0],
655
656    /// The page to be erased.
657    pub ERASE_PAGE: Field <= MAX_PAGE_INDEX,
658
659    #[cfg(test)]
660    pub LEN_ERASE: Length,
661}
662
663// The fields of a clear entry.
664bitfield! {
665    /// The identifier for clear entries.
666    pub ID_CLEAR: ConstField = [1 1 0 0 1],
667
668    /// The minimum key to be cleared.
669    ///
670    /// All entries with a key below this limit are not cleared. All other entries are deleted.
671    pub CLEAR_MIN_KEY: Field <= MAX_KEY_INDEX,
672
673    #[cfg(test)]
674    pub LEN_CLEAR: Length,
675}
676
677// The fields of a marker entry.
678bitfield! {
679    /// The identifier for marker entries.
680    pub ID_MARKER: ConstField = [1 1 0 1 0],
681
682    /// The number of updates in this transaction.
683    ///
684    /// The update entries follow this marker entry.
685    pub MARKER_COUNT: Field <= MAX_UPDATES,
686
687    #[cfg(test)]
688    pub LEN_MARKER: Length,
689}
690
691// The fields of a remove entry.
692bitfield! {
693    /// The identifier for remove entries.
694    pub ID_REMOVE: ConstField = [1 1 0 1 1],
695
696    /// The key of the user entry to be removed.
697    pub REMOVE_KEY: Field <= MAX_KEY_INDEX,
698
699    #[cfg(test)]
700    pub LEN_REMOVE: Length,
701}
702
703/// The position of a word in the virtual storage.
704///
705/// With the notations defined in [`Format`], let:
706/// - w denote a word offset in a virtual page, thus between 0 and Q - 1
707/// - p denote a page offset, thus between 0 and N - 1
708/// - c denote the number of times a page was erased, thus between 0 and E
709///
710/// The position of a word is (c * N + p) * Q + w. This position monotonically increases and
711/// represents the consumed lifetime of the storage.
712///
713/// This type is kept abstract to avoid possible confusion with [`Nat`] and [`Word`] if they happen
714/// to have the same representation. Here is an overview of their semantics:
715///
716/// | Name       | Semantics                   | Arithmetic operations | Bit-wise operations |
717/// | ---------- | --------------------------- | --------------------- | ------------------- |
718/// | [`Nat`]    | Natural numbers             | Yes (no overflow)     | No                  |
719/// | [`Word`]   | Word in flash               | No                    | Yes                 |
720/// | `Position` | Position in virtual storage | Yes (no overflow)     | No                  |
721#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
722pub struct Position(Nat);
723
724impl core::ops::Add<Nat> for Position {
725    type Output = Position;
726
727    fn add(self, delta: Nat) -> Position {
728        Position(self.0 + delta)
729    }
730}
731
732impl core::ops::Sub<Position> for Position {
733    type Output = Nat;
734
735    fn sub(self, base: Position) -> Nat {
736        self.0 - base.0
737    }
738}
739
740impl core::ops::AddAssign<Nat> for Position {
741    fn add_assign(&mut self, delta: Nat) {
742        self.0 += delta;
743    }
744}
745
746impl Position {
747    /// Create a word position given its coordinates.
748    ///
749    /// The coordinates of a word are:
750    /// - Its word index in its virtual page.
751    /// - Its page index in the storage.
752    /// - The number of times its page was erased.
753    pub fn new(format: &Format, cycle: Nat, page: Nat, word: Nat) -> Position {
754        Position((cycle * format.num_pages() + page) * format.virt_page_size() + word)
755    }
756
757    /// Accesses the underlying position as a natural number.
758    pub fn get(self) -> Nat {
759        self.0
760    }
761
762    /// Returns the associated storage index.
763    pub fn index(self, format: &Format) -> StorageIndex {
764        let page = self.page(format);
765        let word = CONTENT_WORD + self.word(format);
766        let byte = word * format.word_size();
767        StorageIndex { page: page as usize, byte: byte as usize }
768    }
769
770    /// Returns the beginning of the current virtual page.
771    pub fn page_begin(self, format: &Format) -> Position {
772        let virt_page_size = format.virt_page_size();
773        Position((self.0 / virt_page_size) * virt_page_size)
774    }
775
776    /// Returns the beginning of the next virtual page.
777    pub fn next_page(self, format: &Format) -> Position {
778        let virt_page_size = format.virt_page_size();
779        Position((self.0 / virt_page_size + 1) * virt_page_size)
780    }
781
782    /// Returns the number of times the current page was erased.
783    pub fn cycle(self, format: &Format) -> Nat {
784        (self.0 / format.virt_page_size()) / format.num_pages()
785    }
786
787    /// Returns the current page index.
788    pub fn page(self, format: &Format) -> Nat {
789        (self.0 / format.virt_page_size()) % format.num_pages()
790    }
791
792    /// Returns the current word index in the page.
793    pub fn word(self, format: &Format) -> Nat {
794        self.0 % format.virt_page_size()
795    }
796}
797
798/// Possible states of some storage representation as a word.
799pub enum WordState<T> {
800    /// The word is still erased.
801    Erased,
802
803    /// The word is partially written.
804    Partial,
805
806    /// Holds the decoded version of a valid word.
807    Valid(T),
808}
809
810/// Information for an initialized page.
811pub struct InitInfo {
812    /// The number of times this page has been erased.
813    pub cycle: Nat,
814
815    /// The word index of the first entry in this virtual page.
816    pub prefix: Nat,
817}
818
819/// Information for a page being compacted.
820pub struct CompactInfo {
821    /// The distance in words between head and tail at compaction.
822    pub tail: Nat,
823}
824
825/// The first word of an entry.
826#[derive(Debug)]
827pub enum ParsedWord {
828    /// Padding entry.
829    Padding(Padding),
830
831    /// Header of a user entry.
832    Header(Header),
833
834    /// Internal entry.
835    Internal(InternalEntry),
836}
837
838/// Padding entry.
839#[derive(Debug)]
840pub struct Padding {
841    /// The number of following padding words after the first word of the padding entry.
842    pub length: Nat,
843}
844
845/// Header of a user entry.
846#[derive(Debug)]
847pub struct Header {
848    /// Whether the last bit of the user data is flipped.
849    pub flipped: bool,
850
851    /// The length in bytes of the user data.
852    pub length: Nat,
853
854    /// The key of the user entry.
855    pub key: Nat,
856
857    /// The checksum of the user entry.
858    pub checksum: Nat,
859}
860
861impl Header {
862    /// Checks the validity of a user entry.
863    ///
864    /// If the user entry has no payload, the `footer` must be set to `None`. Otherwise it should be
865    /// the last word of the entry.
866    pub fn check(&self, footer: Option<&[u8]>) -> bool {
867        footer.map_or(0, count_zeros) == self.checksum
868    }
869}
870
871/// Internal entry.
872#[derive(Debug)]
873pub enum InternalEntry {
874    /// Indicates that a page should be erased.
875    Erase {
876        /// The page to be erased.
877        page: Nat,
878    },
879
880    /// Indicates that user entries with high key should be deleted.
881    Clear {
882        /// The minimum key a user entry should have to be deleted.
883        min_key: Nat,
884    },
885
886    /// Marks the start of a transaction.
887    ///
888    /// The marker is followed by a given number of updates, which are either user entries or
889    /// remove entries.
890    Marker {
891        /// The number of updates in the transaction.
892        count: Nat,
893    },
894
895    /// Indicates that a user entry should be removed.
896    ///
897    /// This is only useful (and valid) as part of a transaction, since removing a single entry is
898    /// already atomic.
899    Remove {
900        /// The key of the user entry to be removed.
901        key: Nat,
902    },
903}
904
905/// Returns whether a slice has all bits equal to one.
906pub fn is_erased(slice: &[u8]) -> bool {
907    slice.iter().all(|&x| x == 0xff)
908}
909
910#[cfg(test)]
911mod tests {
912    use super::*;
913
914    #[test]
915    fn size_of_format() {
916        assert_eq!(std::mem::size_of::<Format>(), 12);
917    }
918
919    #[test]
920    fn checksum_ok() {
921        let Field { pos, len } = WORD_CHECKSUM.field;
922        // There is enough bits to represents the number of zeros preceding the checksum.
923        assert_eq!(len, num_bits(pos));
924        // The checksum is the last field of a word.
925        assert_eq!(pos + len, 8 * WORD_SIZE);
926        // The data of words using the checksum don't overlap the checksum.
927        let words = &[&LEN_INIT, &LEN_COMPACT, &LEN_ERASE, &LEN_CLEAR, &LEN_MARKER, &LEN_REMOVE];
928        for word in words {
929            assert!(word.pos < pos);
930        }
931    }
932
933    #[test]
934    fn init_ok() {
935        assert_eq!(INIT_CYCLE.pos, 0);
936        assert_eq!(INIT_CYCLE.len, 16);
937        assert_eq!(INIT_PREFIX.pos, 16);
938        assert_eq!(INIT_PREFIX.len, 9);
939        assert_eq!(LEN_INIT.pos, 25);
940    }
941
942    #[test]
943    fn compact_ok() {
944        assert_eq!(COMPACT_TAIL.pos, 0);
945        assert_eq!(COMPACT_TAIL.len, 16);
946        assert_eq!(LEN_COMPACT.pos, 16);
947    }
948
949    #[test]
950    fn header_ok() {
951        assert_eq!(ID_HEADER.field.pos, 0);
952        assert_eq!(ID_HEADER.field.len, 2);
953        assert_eq!(ID_HEADER.value, 0b01);
954        assert_eq!(HEADER_DELETED.pos, 2);
955        assert_eq!(HEADER_FLIPPED.pos, 3);
956        assert_eq!(HEADER_LENGTH.pos, 4);
957        assert_eq!(HEADER_LENGTH.len, 10);
958        assert_eq!(HEADER_KEY.pos, 14);
959        assert_eq!(HEADER_KEY.len, 12);
960        assert_eq!(HEADER_CHECKSUM.field.pos, 26);
961        assert_eq!(HEADER_CHECKSUM.field.len, 6);
962        assert_eq!(LEN_HEADER.pos, 32);
963    }
964
965    #[test]
966    fn erase_ok() {
967        assert_eq!(ID_ERASE.field.pos, 0);
968        assert_eq!(ID_ERASE.field.len, 5);
969        assert_eq!(ID_ERASE.value, 0b00011);
970        assert_eq!(ERASE_PAGE.pos, 5);
971        assert_eq!(ERASE_PAGE.len, 6);
972        assert_eq!(LEN_ERASE.pos, 11);
973    }
974
975    #[test]
976    fn clear_ok() {
977        assert_eq!(ID_CLEAR.field.pos, 0);
978        assert_eq!(ID_CLEAR.field.len, 5);
979        assert_eq!(ID_CLEAR.value, 0b10011);
980        assert_eq!(CLEAR_MIN_KEY.pos, 5);
981        assert_eq!(CLEAR_MIN_KEY.len, 12);
982        assert_eq!(LEN_CLEAR.pos, 17);
983    }
984
985    #[test]
986    fn marker_ok() {
987        assert_eq!(ID_MARKER.field.pos, 0);
988        assert_eq!(ID_MARKER.field.len, 5);
989        assert_eq!(ID_MARKER.value, 0b01011);
990        assert_eq!(MARKER_COUNT.pos, 5);
991        assert_eq!(MARKER_COUNT.len, 5);
992        assert_eq!(LEN_MARKER.pos, 10);
993    }
994
995    #[test]
996    fn remove_ok() {
997        assert_eq!(ID_REMOVE.field.pos, 0);
998        assert_eq!(ID_REMOVE.field.len, 5);
999        assert_eq!(ID_REMOVE.value, 0b11011);
1000        assert_eq!(REMOVE_KEY.pos, 5);
1001        assert_eq!(REMOVE_KEY.len, 12);
1002        assert_eq!(LEN_REMOVE.pos, 17);
1003    }
1004
1005    #[test]
1006    fn word_from_slice_ok() {
1007        assert_eq!(Word::from_slice(&[0x04, 0x03, 0x02, 0x01]), Word(0x01020304));
1008        assert_eq!(Word::from_slice(&[0x1e, 0x3c, 0x78, 0xf0]), Word(0xf0783c1e));
1009    }
1010
1011    #[test]
1012    fn is_erased_ok() {
1013        assert!(is_erased(&[]));
1014        assert!(is_erased(&[0xff]));
1015        assert!(is_erased(&[0xff, 0xff]));
1016        assert!(!is_erased(&[0x00]));
1017        assert!(!is_erased(&[0xff, 0xfe]));
1018        assert!(!is_erased(&[0x7f, 0xff]));
1019    }
1020
1021    #[test]
1022    fn positions_fit_in_a_word() {
1023        // All reachable positions are smaller than this value, which is one past the last position.
1024        // It is simply the total number of virtual words, i.e. the number of words per virtual page
1025        // times the number of virtual pages times the number of times a virtual page can be used
1026        // (one more than the number of times it can be erased since we can write before the first
1027        // erase cycle and after the last erase cycle).
1028        assert_eq!((MAX_ERASE_CYCLE + 1) * (MAX_PAGE_INDEX + 1) * MAX_VIRT_PAGE_SIZE, 0xff800000);
1029    }
1030
1031    #[test]
1032    fn position_offsets_fit_in_a_halfword() {
1033        // The store stores in RAM the entry positions as their offset from the head. Those offsets
1034        // are represented as u16. The bound below is a large over-approximation of the maximal
1035        // offset. We first make sure it fits in a u16.
1036        const MAX_POS: Nat = (MAX_PAGE_INDEX + 1) * MAX_VIRT_PAGE_SIZE;
1037        assert!(MAX_POS <= u16::MAX as Nat);
1038        // We also check the actual value for up-to-date documentation, since it's a constant.
1039        assert_eq!(MAX_POS, 0xff80);
1040    }
1041}