Skip to main content

wasefire_store/
model.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Store specification.
16
17use std::collections::HashMap;
18
19use wasefire_error::Error;
20
21use crate::format::Format;
22use crate::{StoreRatio, StoreUpdate, usize_to_nat};
23
24/// Models the mutable operations of a store.
25///
26/// The model doesn't model the storage and read-only operations. This is done by the
27/// [driver](crate::StoreDriver).
28#[derive(Clone, Debug)]
29pub struct StoreModel {
30    /// Represents the content of the store.
31    content: HashMap<usize, Box<[u8]>>,
32
33    /// The modeled storage configuration.
34    format: Format,
35}
36
37/// Mutable operations on a store.
38#[derive(Clone, Debug)]
39pub enum StoreOperation {
40    /// Applies a transaction.
41    Transaction {
42        /// The list of updates to be applied.
43        updates: Vec<StoreUpdate<Vec<u8>>>,
44    },
45
46    /// Deletes all keys above a threshold.
47    Clear {
48        /// The minimum key to be deleted.
49        min_key: usize,
50    },
51
52    /// Compacts the store until a given capacity is immediately available.
53    Prepare {
54        /// How much capacity should be immediately available after compaction.
55        length: usize,
56    },
57}
58
59impl StoreModel {
60    /// Creates an empty model for a given storage configuration.
61    pub fn new(format: Format) -> StoreModel {
62        let content = HashMap::new();
63        StoreModel { content, format }
64    }
65
66    /// Returns the modeled content.
67    pub fn content(&self) -> &HashMap<usize, Box<[u8]>> {
68        &self.content
69    }
70
71    /// Returns the storage configuration.
72    pub fn format(&self) -> &Format {
73        &self.format
74    }
75
76    /// Simulates a store operation.
77    pub fn apply(&mut self, operation: StoreOperation) -> Result<(), Error> {
78        match operation {
79            StoreOperation::Transaction { updates } => self.transaction(updates),
80            StoreOperation::Clear { min_key } => self.clear(min_key),
81            StoreOperation::Prepare { length } => self.prepare(length),
82        }
83    }
84
85    /// Returns the capacity according to the model.
86    pub fn capacity(&self) -> StoreRatio {
87        let total = self.format.total_capacity();
88        let used =
89            usize_to_nat(self.content.values().map(|x| self.format.entry_size(x) as usize).sum());
90        StoreRatio { used, total }
91    }
92
93    /// Applies a transaction.
94    fn transaction(&mut self, updates: Vec<StoreUpdate<Vec<u8>>>) -> Result<(), Error> {
95        // Fail if the transaction is invalid.
96        if self.format.transaction_valid(&updates).is_none() {
97            return Err(crate::INVALID_ARGUMENT);
98        }
99        // Fail if there is not enough capacity.
100        let capacity = self.format.transaction_capacity(&updates) as usize;
101        if self.capacity().remaining() < capacity {
102            return Err(crate::NO_CAPACITY);
103        }
104        // Apply the updates.
105        for update in updates {
106            match update {
107                StoreUpdate::Insert { key, value } => {
108                    self.content.insert(key, value.into_boxed_slice());
109                }
110                StoreUpdate::Remove { key } => {
111                    self.content.remove(&key);
112                }
113            }
114        }
115        Ok(())
116    }
117
118    /// Applies a clear operation.
119    fn clear(&mut self, min_key: usize) -> Result<(), Error> {
120        if min_key > self.format.max_key() as usize {
121            return Err(crate::INVALID_ARGUMENT);
122        }
123        self.content.retain(|&k, _| k < min_key);
124        Ok(())
125    }
126
127    /// Applies a prepare operation.
128    fn prepare(&self, length: usize) -> Result<(), Error> {
129        if self.capacity().remaining() < length {
130            return Err(crate::NO_CAPACITY);
131        }
132        Ok(())
133    }
134}