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}