Merge pull request #264 from ia0/store_update_ref

Make StoreUpdate generic over the byte slice ownership
This commit is contained in:
Julien Cretin
2021-01-20 17:25:19 +01:00
committed by GitHub
4 changed files with 26 additions and 15 deletions

View File

@@ -303,7 +303,7 @@ impl<'a> Fuzzer<'a> {
} }
/// Generates a possibly invalid update. /// Generates a possibly invalid update.
fn update(&mut self) -> StoreUpdate { fn update(&mut self) -> StoreUpdate<Vec<u8>> {
match self.entropy.read_range(0, 1) { match self.entropy.read_range(0, 1) {
0 => { 0 => {
let key = self.key(); let key = self.key();

View File

@@ -20,6 +20,7 @@ use self::bitfield::Length;
use self::bitfield::{count_zeros, num_bits, Bit, Checksum, ConstField, Field}; use self::bitfield::{count_zeros, num_bits, Bit, Checksum, ConstField, Field};
use crate::{usize_to_nat, Nat, Storage, StorageIndex, StoreError, StoreResult, StoreUpdate}; use crate::{usize_to_nat, Nat, Storage, StorageIndex, StoreError, StoreResult, StoreUpdate};
use alloc::vec::Vec; use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::min; use core::cmp::min;
use core::convert::TryFrom; use core::convert::TryFrom;
@@ -492,13 +493,16 @@ impl Format {
} }
/// Returns the capacity required by a transaction. /// Returns the capacity required by a transaction.
pub fn transaction_capacity(&self, updates: &[StoreUpdate]) -> Nat { pub fn transaction_capacity<ByteSlice: Borrow<[u8]>>(
&self,
updates: &[StoreUpdate<ByteSlice>],
) -> Nat {
match updates.len() { match updates.len() {
// An empty transaction doesn't consume anything. // An empty transaction doesn't consume anything.
0 => 0, 0 => 0,
// Transactions with a single update are optimized by avoiding a marker entry. // Transactions with a single update are optimized by avoiding a marker entry.
1 => match &updates[0] { 1 => match &updates[0] {
StoreUpdate::Insert { value, .. } => self.entry_size(value), StoreUpdate::Insert { value, .. } => self.entry_size(value.borrow()),
// Transactions with a single update which is a removal don't consume anything. // Transactions with a single update which is a removal don't consume anything.
StoreUpdate::Remove { .. } => 0, StoreUpdate::Remove { .. } => 0,
}, },
@@ -508,9 +512,9 @@ impl Format {
} }
/// Returns the capacity of an update. /// Returns the capacity of an update.
fn update_capacity(&self, update: &StoreUpdate) -> Nat { fn update_capacity<ByteSlice: Borrow<[u8]>>(&self, update: &StoreUpdate<ByteSlice>) -> Nat {
match update { match update {
StoreUpdate::Insert { value, .. } => self.entry_size(value), StoreUpdate::Insert { value, .. } => self.entry_size(value.borrow()),
StoreUpdate::Remove { .. } => 1, StoreUpdate::Remove { .. } => 1,
} }
} }
@@ -523,7 +527,10 @@ impl Format {
/// Checks if a transaction is valid and returns its sorted keys. /// Checks if a transaction is valid and returns its sorted keys.
/// ///
/// Returns `None` if the transaction is invalid. /// Returns `None` if the transaction is invalid.
pub fn transaction_valid(&self, updates: &[StoreUpdate]) -> Option<Vec<Nat>> { pub fn transaction_valid<ByteSlice: Borrow<[u8]>>(
&self,
updates: &[StoreUpdate<ByteSlice>],
) -> Option<Vec<Nat>> {
if usize_to_nat(updates.len()) > self.max_updates() { if usize_to_nat(updates.len()) > self.max_updates() {
return None; return None;
} }

View File

@@ -34,7 +34,7 @@ pub enum StoreOperation {
/// Applies a transaction. /// Applies a transaction.
Transaction { Transaction {
/// The list of updates to be applied. /// The list of updates to be applied.
updates: Vec<StoreUpdate>, updates: Vec<StoreUpdate<Vec<u8>>>,
}, },
/// Deletes all keys above a threshold. /// Deletes all keys above a threshold.
@@ -89,7 +89,7 @@ impl StoreModel {
} }
/// Applies a transaction. /// Applies a transaction.
fn transaction(&mut self, updates: Vec<StoreUpdate>) -> StoreResult<()> { fn transaction(&mut self, updates: Vec<StoreUpdate<Vec<u8>>>) -> StoreResult<()> {
// Fail if the transaction is invalid. // Fail if the transaction is invalid.
if self.format.transaction_valid(&updates).is_none() { if self.format.transaction_valid(&updates).is_none() {
return Err(StoreError::InvalidArgument); return Err(StoreError::InvalidArgument);

View File

@@ -25,6 +25,7 @@ pub use crate::{
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::{max, min, Ordering}; use core::cmp::{max, min, Ordering};
use core::convert::TryFrom; use core::convert::TryFrom;
use core::option::NoneError; use core::option::NoneError;
@@ -159,15 +160,15 @@ impl StoreHandle {
/// Represents an update to the store as part of a transaction. /// Represents an update to the store as part of a transaction.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum StoreUpdate { pub enum StoreUpdate<ByteSlice: Borrow<[u8]>> {
/// Inserts or replaces an entry in the store. /// Inserts or replaces an entry in the store.
Insert { key: usize, value: Vec<u8> }, Insert { key: usize, value: ByteSlice },
/// Removes an entry from the store. /// Removes an entry from the store.
Remove { key: usize }, Remove { key: usize },
} }
impl StoreUpdate { impl<ByteSlice: Borrow<[u8]>> StoreUpdate<ByteSlice> {
/// Returns the key affected by the update. /// Returns the key affected by the update.
pub fn key(&self) -> usize { pub fn key(&self) -> usize {
match *self { match *self {
@@ -179,7 +180,7 @@ impl StoreUpdate {
/// Returns the value written by the update. /// Returns the value written by the update.
pub fn value(&self) -> Option<&[u8]> { pub fn value(&self) -> Option<&[u8]> {
match self { match self {
StoreUpdate::Insert { value, .. } => Some(value), StoreUpdate::Insert { value, .. } => Some(value.borrow()),
StoreUpdate::Remove { .. } => None, StoreUpdate::Remove { .. } => None,
} }
} }
@@ -280,14 +281,17 @@ impl<S: Storage> Store<S> {
/// - There are too many updates. /// - There are too many updates.
/// - The updates overlap, i.e. their keys are not disjoint. /// - The updates overlap, i.e. their keys are not disjoint.
/// - The updates are invalid, e.g. key out of bound or value too long. /// - The updates are invalid, e.g. key out of bound or value too long.
pub fn transaction(&mut self, updates: &[StoreUpdate]) -> StoreResult<()> { pub fn transaction<ByteSlice: Borrow<[u8]>>(
&mut self,
updates: &[StoreUpdate<ByteSlice>],
) -> StoreResult<()> {
let count = usize_to_nat(updates.len()); let count = usize_to_nat(updates.len());
if count == 0 { if count == 0 {
return Ok(()); return Ok(());
} }
if count == 1 { if count == 1 {
match updates[0] { match updates[0] {
StoreUpdate::Insert { key, ref value } => return self.insert(key, value), StoreUpdate::Insert { key, ref value } => return self.insert(key, value.borrow()),
StoreUpdate::Remove { key } => return self.remove(key), StoreUpdate::Remove { key } => return self.remove(key),
} }
} }
@@ -310,7 +314,7 @@ impl<S: Storage> Store<S> {
for update in updates { for update in updates {
let length = match *update { let length = match *update {
StoreUpdate::Insert { key, ref value } => { StoreUpdate::Insert { key, ref value } => {
let entry = self.format.build_user(usize_to_nat(key), value)?; let entry = self.format.build_user(usize_to_nat(key), value.borrow())?;
let word_size = self.format.word_size(); let word_size = self.format.word_size();
let footer = usize_to_nat(entry.len()) / word_size - 1; let footer = usize_to_nat(entry.len()) / word_size - 1;
self.write_slice(tail, &entry[..(footer * word_size) as usize])?; self.write_slice(tail, &entry[..(footer * word_size) as usize])?;