Introduce distinct integer types

This PR does the following things:
- Give incompatible representations for integers with different semantics:
  - `usize` is used for natural numbers for the public API.
  - `Nat` is used internally for natural numbers (essentially a stable `usize`).
  - `Word` is used for sequences of bits representing words in flash.
  - `Position` is used for word positions in the virtual storage.
- Only use fixed size integers to preserve overflow behavior between targets.
- Use little-endian representation instead of native representation for `Word`.

Alternatives:
- Run tests and fuzzing on 32-bits architecture (or some compatibility mode).
  This approach would have better readability than the current solution (less
  conversions at public API). However it would require additional setup and
  might not be viable long-term by restricting machines on which fuzzing is
  possible.
- Accept the behavior difference for tests and fuzzing. This approach would also
  have better readability. However checking for arithmetic overflow (and other
  `usize` related concerns like memory size) is more important.
This commit is contained in:
Julien Cretin
2020-10-29 17:46:01 +01:00
parent dea28f622f
commit 9778ea7fd2
6 changed files with 254 additions and 159 deletions

View File

@@ -348,8 +348,6 @@
#[macro_use]
extern crate alloc;
#[macro_use]
mod bitfield;
mod buffer;
mod format;
#[cfg(feature = "std")]
@@ -362,3 +360,25 @@ pub use self::buffer::{BufferCorruptFunction, BufferOptions, BufferStorage};
pub use self::model::{StoreModel, StoreOperation};
pub use self::storage::{Storage, StorageError, StorageIndex, StorageResult};
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()
}