From d734da3a0e55c8d7c3f8e2d356823556a53a2eab Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Tue, 3 Nov 2020 12:39:38 +0100 Subject: [PATCH] Move transaction capacity formula to Format --- libraries/persistent_store/src/format.rs | 31 +++++++++++++++++++++- libraries/persistent_store/src/model.rs | 33 +++++------------------- libraries/persistent_store/src/store.rs | 9 +++---- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/libraries/persistent_store/src/format.rs b/libraries/persistent_store/src/format.rs index 5935fd6..7f79328 100644 --- a/libraries/persistent_store/src/format.rs +++ b/libraries/persistent_store/src/format.rs @@ -18,7 +18,7 @@ mod bitfield; #[cfg(test)] use self::bitfield::Length; use self::bitfield::{count_zeros, num_bits, Bit, Checksum, ConstField, Field}; -use crate::{usize_to_nat, Nat, Storage, StorageIndex, StoreError, StoreResult}; +use crate::{usize_to_nat, Nat, Storage, StorageIndex, StoreError, StoreResult, StoreUpdate}; use alloc::vec::Vec; use core::cmp::min; use core::convert::TryFrom; @@ -491,6 +491,35 @@ impl Format { HEADER_DELETED.set(word); } + /// Returns the capacity required by a transaction. + pub fn transaction_capacity(&self, updates: &[StoreUpdate]) -> Nat { + match updates.len() { + // An empty transaction doesn't consume anything. + 0 => 0, + // Transactions with a single update are optimized by avoiding a marker entry. + 1 => match &updates[0] { + StoreUpdate::Insert { value, .. } => self.entry_size(value), + // Transactions with a single update which is a removal don't consume anything. + StoreUpdate::Remove { .. } => 0, + }, + // A transaction consumes one word for the marker entry in addition to its updates. + _ => 1 + updates.iter().map(|x| self.update_capacity(x)).sum::(), + } + } + + /// Returns the capacity of an update. + fn update_capacity(&self, update: &StoreUpdate) -> Nat { + match update { + StoreUpdate::Insert { value, .. } => self.entry_size(value), + StoreUpdate::Remove { .. } => 1, + } + } + + /// Returns the size of a user entry given its value. + pub fn entry_size(&self, value: &[u8]) -> Nat { + 1 + self.bytes_to_words(usize_to_nat(value.len())) + } + /// Returns the minimum number of words to represent a given number of bytes. /// /// # Preconditions diff --git a/libraries/persistent_store/src/model.rs b/libraries/persistent_store/src/model.rs index d3c718a..b558f99 100644 --- a/libraries/persistent_store/src/model.rs +++ b/libraries/persistent_store/src/model.rs @@ -79,7 +79,12 @@ impl StoreModel { /// Returns the capacity according to the model. pub fn capacity(&self) -> StoreRatio { let total = self.format.total_capacity(); - let used = usize_to_nat(self.content.values().map(|x| self.entry_size(x)).sum()); + let used = usize_to_nat( + self.content + .values() + .map(|x| self.format.entry_size(x) as usize) + .sum(), + ); StoreRatio { used, total } } @@ -99,18 +104,7 @@ impl StoreModel { return Err(StoreError::InvalidArgument); } // Fail if there is not enough capacity. - let capacity = match updates.len() { - // An empty transaction doesn't consume anything. - 0 => 0, - // Transactions with a single update are optimized by avoiding a marker entry. - 1 => match &updates[0] { - StoreUpdate::Insert { value, .. } => self.entry_size(value), - // Transactions with a single update which is a removal don't consume anything. - StoreUpdate::Remove { .. } => 0, - }, - // A transaction consumes one word for the marker entry in addition to its updates. - _ => 1 + updates.iter().map(|x| self.update_size(x)).sum::(), - }; + let capacity = self.format.transaction_capacity(&updates) as usize; if self.capacity().remaining() < capacity { return Err(StoreError::NoCapacity); } @@ -145,19 +139,6 @@ impl StoreModel { Ok(()) } - /// Returns the word capacity of an update. - fn update_size(&self, update: &StoreUpdate) -> usize { - match update { - StoreUpdate::Insert { value, .. } => self.entry_size(value), - StoreUpdate::Remove { .. } => 1, - } - } - - /// Returns the word capacity of an entry. - fn entry_size(&self, value: &[u8]) -> usize { - 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() as usize diff --git a/libraries/persistent_store/src/store.rs b/libraries/persistent_store/src/store.rs index b0d132c..b7c034a 100644 --- a/libraries/persistent_store/src/store.rs +++ b/libraries/persistent_store/src/store.rs @@ -262,20 +262,17 @@ impl Store { if count > self.format.max_updates() { return Err(StoreError::InvalidArgument); } - // Compute how much capacity (including transient) we need. + // Check that the updates are valid. let mut sorted_keys = Vec::with_capacity(count as usize); - let mut word_capacity = 1 + count; for update in updates { let key = usize_to_nat(update.key()); if key > self.format.max_key() { return Err(StoreError::InvalidArgument); } if let Some(value) = update.value() { - let value_len = usize_to_nat(value.len()); - if value_len > self.format.max_value_len() { + if usize_to_nat(value.len()) > self.format.max_value_len() { return Err(StoreError::InvalidArgument); } - word_capacity += self.format.bytes_to_words(value_len); } match sorted_keys.binary_search(&key) { Ok(_) => return Err(StoreError::InvalidArgument), @@ -283,7 +280,7 @@ impl Store { } } // Reserve the capacity. - self.reserve(word_capacity)?; + self.reserve(self.format.transaction_capacity(updates))?; // Write the marker entry. let marker = self.tail()?; let entry = self.format.build_internal(InternalEntry::Marker { count });