Move transaction validity check to Format
This commit is contained in:
@@ -520,6 +520,32 @@ impl Format {
|
|||||||
1 + self.bytes_to_words(usize_to_nat(value.len()))
|
1 + self.bytes_to_words(usize_to_nat(value.len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a transaction is valid and returns its sorted keys.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the transaction is invalid.
|
||||||
|
pub fn transaction_valid(&self, updates: &[StoreUpdate]) -> Option<Vec<Nat>> {
|
||||||
|
if usize_to_nat(updates.len()) > self.max_updates() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut sorted_keys = Vec::with_capacity(updates.len());
|
||||||
|
for update in updates {
|
||||||
|
let key = usize_to_nat(update.key());
|
||||||
|
if key > self.max_key() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(value) = update.value() {
|
||||||
|
if usize_to_nat(value.len()) > self.max_value_len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match sorted_keys.binary_search(&key) {
|
||||||
|
Ok(_) => return None,
|
||||||
|
Err(pos) => sorted_keys.insert(pos, key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(sorted_keys)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the minimum number of words to represent a given number of bytes.
|
/// Returns the minimum number of words to represent a given number of bytes.
|
||||||
///
|
///
|
||||||
/// # Preconditions
|
/// # Preconditions
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
use crate::format::Format;
|
use crate::format::Format;
|
||||||
use crate::{usize_to_nat, StoreError, StoreRatio, StoreResult, StoreUpdate};
|
use crate::{usize_to_nat, StoreError, StoreRatio, StoreResult, StoreUpdate};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// Models the mutable operations of a store.
|
/// Models the mutable operations of a store.
|
||||||
///
|
///
|
||||||
@@ -90,17 +90,8 @@ impl StoreModel {
|
|||||||
|
|
||||||
/// Applies a transaction.
|
/// Applies a transaction.
|
||||||
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> {
|
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> {
|
||||||
// Fail if too many updates.
|
// Fail if the transaction is invalid.
|
||||||
if updates.len() > self.format.max_updates() as usize {
|
if self.format.transaction_valid(&updates).is_none() {
|
||||||
return Err(StoreError::InvalidArgument);
|
|
||||||
}
|
|
||||||
// Fail if an update is invalid.
|
|
||||||
if !updates.iter().all(|x| self.update_valid(x)) {
|
|
||||||
return Err(StoreError::InvalidArgument);
|
|
||||||
}
|
|
||||||
// Fail if updates are not disjoint, i.e. there are duplicate keys.
|
|
||||||
let keys: HashSet<_> = updates.iter().map(|x| x.key()).collect();
|
|
||||||
if keys.len() != updates.len() {
|
|
||||||
return Err(StoreError::InvalidArgument);
|
return Err(StoreError::InvalidArgument);
|
||||||
}
|
}
|
||||||
// Fail if there is not enough capacity.
|
// Fail if there is not enough capacity.
|
||||||
@@ -138,12 +129,4 @@ impl StoreModel {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether an update is valid.
|
|
||||||
fn update_valid(&self, update: &StoreUpdate) -> bool {
|
|
||||||
update.key() <= self.format.max_key() as usize
|
|
||||||
&& update
|
|
||||||
.value()
|
|
||||||
.map_or(true, |x| x.len() <= self.format.max_value_len() as usize)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,26 +259,11 @@ impl<S: Storage> Store<S> {
|
|||||||
StoreUpdate::Remove { key } => return self.remove(key),
|
StoreUpdate::Remove { key } => return self.remove(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if count > self.format.max_updates() {
|
// Get the sorted keys. Fail if the transaction is invalid.
|
||||||
return Err(StoreError::InvalidArgument);
|
let sorted_keys = match self.format.transaction_valid(updates) {
|
||||||
}
|
None => return Err(StoreError::InvalidArgument),
|
||||||
// Check that the updates are valid.
|
Some(x) => x,
|
||||||
let mut sorted_keys = Vec::with_capacity(count as usize);
|
};
|
||||||
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() {
|
|
||||||
if usize_to_nat(value.len()) > self.format.max_value_len() {
|
|
||||||
return Err(StoreError::InvalidArgument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match sorted_keys.binary_search(&key) {
|
|
||||||
Ok(_) => return Err(StoreError::InvalidArgument),
|
|
||||||
Err(pos) => sorted_keys.insert(pos, key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reserve the capacity.
|
// Reserve the capacity.
|
||||||
self.reserve(self.format.transaction_capacity(updates))?;
|
self.reserve(self.format.transaction_capacity(updates))?;
|
||||||
// Write the marker entry.
|
// Write the marker entry.
|
||||||
|
|||||||
Reference in New Issue
Block a user