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:
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::format::Format;
|
||||
use crate::{StoreError, StoreRatio, StoreResult, StoreUpdate};
|
||||
use crate::{usize_to_nat, StoreError, StoreRatio, StoreResult, StoreUpdate};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Models the mutable operations of a store.
|
||||
@@ -79,14 +79,14 @@ impl StoreModel {
|
||||
/// Returns the capacity according to the model.
|
||||
pub fn capacity(&self) -> StoreRatio {
|
||||
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 }
|
||||
}
|
||||
|
||||
/// Applies a transaction.
|
||||
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> {
|
||||
// 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);
|
||||
}
|
||||
// Fail if an update is invalid.
|
||||
@@ -130,7 +130,7 @@ impl StoreModel {
|
||||
|
||||
/// Applies a clear operation.
|
||||
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);
|
||||
}
|
||||
self.content.retain(|&k, _| k < min_key);
|
||||
@@ -155,14 +155,14 @@ impl StoreModel {
|
||||
|
||||
/// Returns the word capacity of an entry.
|
||||
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.
|
||||
fn update_valid(&self, update: &StoreUpdate) -> bool {
|
||||
update.key() <= self.format.max_key()
|
||||
update.key() <= self.format.max_key() as usize
|
||||
&& update
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user