Coverage Report

Created: 2026-01-13 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/OpenSK/libraries/persistent_store/src/fragment.rs
Line
Count
Source
1
// Copyright 2021 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
//! Support for fragmented entries.
16
//!
17
//! This module permits to handle entries larger than the [maximum value
18
//! length](Store::max_value_length) by storing ordered consecutive fragments in a sequence of keys.
19
//! The first keys hold fragments of maximal length, followed by a possibly partial fragment. The
20
//! remaining keys are not used.
21
22
use crate::{Storage, Store, StoreError, StoreHandle, StoreResult, StoreUpdate};
23
use alloc::vec::Vec;
24
use core::ops::Range;
25
26
/// Represents a sequence of keys.
27
#[allow(clippy::len_without_is_empty)]
28
pub trait Keys {
29
    /// Returns the number of keys.
30
    fn len(&self) -> usize;
31
32
    /// Returns the position of a key in the sequence.
33
    fn pos(&self, key: usize) -> Option<usize>;
34
35
    /// Returns the key of a position in the sequence.
36
    ///
37
    /// # Preconditions
38
    ///
39
    /// The position must be within the length: `pos` < [`Self::len`].
40
    fn key(&self, pos: usize) -> usize;
41
}
42
43
impl Keys for Range<usize> {
44
0
    fn len(&self) -> usize {
45
0
        self.end - self.start
46
0
    }
47
48
0
    fn pos(&self, key: usize) -> Option<usize> {
49
0
        if self.start <= key && key < self.end {
50
0
            Some(key - self.start)
51
        } else {
52
0
            None
53
        }
54
0
    }
55
56
0
    fn key(&self, pos: usize) -> usize {
57
0
        debug_assert!(pos < Keys::len(self));
58
0
        self.start + pos
59
0
    }
60
}
61
62
/// Reads the concatenated value of a sequence of keys.
63
pub fn read(store: &Store<impl Storage>, keys: &impl Keys) -> StoreResult<Option<Vec<u8>>> {
64
    let handles = get_handles(store, keys)?;
65
    if handles.is_empty() {
66
        return Ok(None);
67
    }
68
    let mut result = Vec::with_capacity(handles.len() * store.max_value_length());
69
    for handle in handles {
70
        result.extend(handle.get_value(store)?);
71
    }
72
    Ok(Some(result))
73
}
74
75
/// Reads a range from the concatenated value of a sequence of keys.
76
///
77
/// This is equivalent to calling [`read`] then taking the range except that:
78
/// - Only the needed chunks are read.
79
/// - The range is truncated to fit in the value.
80
pub fn read_range(
81
    store: &Store<impl Storage>,
82
    keys: &impl Keys,
83
    range: Range<usize>,
84
) -> StoreResult<Option<Vec<u8>>> {
85
    let range_len = match range.end.checked_sub(range.start) {
86
        None => return Err(StoreError::InvalidArgument),
87
        Some(x) => x,
88
    };
89
    let handles = get_handles(store, keys)?;
90
    if handles.is_empty() {
91
        return Ok(None);
92
    }
93
    let mut result = Vec::with_capacity(range_len);
94
    let mut offset = 0;
95
    for handle in handles {
96
        let start = range.start.saturating_sub(offset);
97
        let length = handle.get_length(store)?;
98
        let end = core::cmp::min(range.end.saturating_sub(offset), length);
99
        offset += length;
100
        if start < end {
101
            result.extend(&handle.get_value(store)?[start..end]);
102
        }
103
    }
104
    Ok(Some(result))
105
}
106
107
/// Writes a value to a sequence of keys as chunks.
108
pub fn write(store: &mut Store<impl Storage>, keys: &impl Keys, value: &[u8]) -> StoreResult<()> {
109
    let handles = get_handles(store, keys)?;
110
    let keys_len = keys.len();
111
    let mut updates = Vec::with_capacity(keys_len);
112
    let mut chunks = value.chunks(store.max_value_length());
113
    for pos in 0..keys_len {
114
        let key = keys.key(pos);
115
        match (handles.get(pos), chunks.next()) {
116
            // No existing handle and no new chunk: nothing to do.
117
            (None, None) => (),
118
            // Existing handle and no new chunk: remove old handle.
119
            (Some(_), None) => updates.push(StoreUpdate::Remove { key }),
120
            // Existing handle with same value as new chunk: nothing to do.
121
            (Some(handle), Some(value)) if handle.get_value(store)? == value => (),
122
            // New chunk: Write (or overwrite) the new value.
123
            (_, Some(value)) => updates.push(StoreUpdate::Insert { key, value }),
124
        }
125
    }
126
    if chunks.next().is_some() {
127
        // The value is too long.
128
        return Err(StoreError::InvalidArgument);
129
    }
130
    store.transaction(&updates)
131
}
132
133
/// Deletes the value of a sequence of keys.
134
pub fn delete(store: &mut Store<impl Storage>, keys: &impl Keys) -> StoreResult<()> {
135
    let updates: Vec<StoreUpdate<Vec<u8>>> = get_handles(store, keys)?
136
        .iter()
137
        .map(|handle| StoreUpdate::Remove {
138
            key: handle.get_key(),
139
        })
140
        .collect();
141
    store.transaction(&updates)
142
}
143
144
/// Returns the handles of a sequence of keys.
145
///
146
/// The handles are truncated to the keys that are present.
147
fn get_handles(store: &Store<impl Storage>, keys: &impl Keys) -> StoreResult<Vec<StoreHandle>> {
148
    let keys_len = keys.len();
149
    let mut handles: Vec<Option<StoreHandle>> = vec![None; keys_len];
150
    for handle in store.iter()? {
151
        let handle = handle?;
152
        let pos = match keys.pos(handle.get_key()) {
153
            Some(pos) => pos,
154
            None => continue,
155
        };
156
        if pos >= keys_len {
157
            return Err(StoreError::InvalidArgument);
158
        }
159
        if let Some(old_handle) = &handles[pos] {
160
            if old_handle.get_key() != handle.get_key() {
161
                // The user provided a non-injective `pos` function.
162
                return Err(StoreError::InvalidArgument);
163
            } else {
164
                return Err(StoreError::InvalidStorage);
165
            }
166
        }
167
        handles[pos] = Some(handle);
168
    }
169
    let num_handles = handles.iter().filter(|x| x.is_some()).count();
170
    let mut result = Vec::with_capacity(num_handles);
171
    for (i, handle) in handles.into_iter().enumerate() {
172
        match (i < num_handles, handle) {
173
            (true, Some(handle)) => result.push(handle),
174
            (false, None) => (),
175
            // We should have `num_handles` Somes followed by Nones.
176
            _ => return Err(StoreError::InvalidStorage),
177
        }
178
    }
179
    Ok(result)
180
}
181
182
#[cfg(test)]
183
mod tests {
184
    use super::*;
185
    use crate::test::MINIMAL;
186
187
    #[test]
188
    fn read_empty_entry() {
189
        let store = MINIMAL.new_store();
190
        assert_eq!(read(&store, &(0..4)), Ok(None));
191
    }
192
193
    #[test]
194
    fn read_single_chunk() {
195
        let mut store = MINIMAL.new_store();
196
        let value = b"hello".to_vec();
197
        assert_eq!(store.insert(0, &value), Ok(()));
198
        assert_eq!(read(&store, &(0..4)), Ok(Some(value)));
199
    }
200
201
    #[test]
202
    fn read_multiple_chunks() {
203
        let mut store = MINIMAL.new_store();
204
        let value: Vec<_> = (0..60).collect();
205
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
206
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
207
        assert_eq!(read(&store, &(0..4)), Ok(Some(value)));
208
    }
209
210
    #[test]
211
    fn read_range_first_chunk() {
212
        let mut store = MINIMAL.new_store();
213
        let value: Vec<_> = (0..60).collect();
214
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
215
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
216
        assert_eq!(
217
            read_range(&store, &(0..4), 0..10),
218
            Ok(Some((0..10).collect()))
219
        );
220
        assert_eq!(
221
            read_range(&store, &(0..4), 10..20),
222
            Ok(Some((10..20).collect()))
223
        );
224
        assert_eq!(
225
            read_range(&store, &(0..4), 40..52),
226
            Ok(Some((40..52).collect()))
227
        );
228
    }
229
230
    #[test]
231
    fn read_range_second_chunk() {
232
        let mut store = MINIMAL.new_store();
233
        let value: Vec<_> = (0..60).collect();
234
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
235
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
236
        assert_eq!(read_range(&store, &(0..4), 52..53), Ok(Some(vec![52])));
237
        assert_eq!(read_range(&store, &(0..4), 53..54), Ok(Some(vec![53])));
238
        assert_eq!(read_range(&store, &(0..4), 59..60), Ok(Some(vec![59])));
239
    }
240
241
    #[test]
242
    fn read_range_both_chunks() {
243
        let mut store = MINIMAL.new_store();
244
        let value: Vec<_> = (0..60).collect();
245
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
246
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
247
        assert_eq!(
248
            read_range(&store, &(0..4), 40..60),
249
            Ok(Some((40..60).collect()))
250
        );
251
        assert_eq!(
252
            read_range(&store, &(0..4), 0..60),
253
            Ok(Some((0..60).collect()))
254
        );
255
    }
256
257
    #[test]
258
    fn read_range_outside() {
259
        let mut store = MINIMAL.new_store();
260
        let value: Vec<_> = (0..60).collect();
261
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
262
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
263
        assert_eq!(
264
            read_range(&store, &(0..4), 40..100),
265
            Ok(Some((40..60).collect()))
266
        );
267
        assert_eq!(read_range(&store, &(0..4), 60..100), Ok(Some(vec![])));
268
    }
269
270
    #[test]
271
    fn write_single_chunk() {
272
        let mut store = MINIMAL.new_store();
273
        let value = b"hello".to_vec();
274
        assert_eq!(write(&mut store, &(0..4), &value), Ok(()));
275
        assert_eq!(store.find(0), Ok(Some(value)));
276
        assert_eq!(store.find(1), Ok(None));
277
        assert_eq!(store.find(2), Ok(None));
278
        assert_eq!(store.find(3), Ok(None));
279
    }
280
281
    #[test]
282
    fn write_multiple_chunks() {
283
        let mut store = MINIMAL.new_store();
284
        let value: Vec<_> = (0..60).collect();
285
        assert_eq!(write(&mut store, &(0..4), &value), Ok(()));
286
        assert_eq!(store.find(0), Ok(Some((0..52).collect())));
287
        assert_eq!(store.find(1), Ok(Some((52..60).collect())));
288
        assert_eq!(store.find(2), Ok(None));
289
        assert_eq!(store.find(3), Ok(None));
290
    }
291
292
    #[test]
293
    fn overwrite_less_chunks() {
294
        let mut store = MINIMAL.new_store();
295
        let value: Vec<_> = (0..60).collect();
296
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
297
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
298
        let value: Vec<_> = (42..69).collect();
299
        assert_eq!(write(&mut store, &(0..4), &value), Ok(()));
300
        assert_eq!(store.find(0), Ok(Some((42..69).collect())));
301
        assert_eq!(store.find(1), Ok(None));
302
        assert_eq!(store.find(2), Ok(None));
303
        assert_eq!(store.find(3), Ok(None));
304
    }
305
306
    #[test]
307
    fn overwrite_needed_chunks() {
308
        let mut store = MINIMAL.new_store();
309
        let mut value: Vec<_> = (0..60).collect();
310
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
311
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
312
        // Current lifetime is 2 words of overhead (2 insert) and 60 bytes of data.
313
        let mut lifetime = 2 + 60 / 4;
314
        assert_eq!(store.lifetime().unwrap().used(), lifetime);
315
        // Update the value.
316
        value.extend(60..80);
317
        assert_eq!(write(&mut store, &(0..4), &value), Ok(()));
318
        // Added lifetime is 1 word of overhead (1 insert) and (80 - 52) bytes of data.
319
        lifetime += 1 + (80 - 52) / 4;
320
        assert_eq!(store.lifetime().unwrap().used(), lifetime);
321
    }
322
323
    #[test]
324
    fn delete_empty() {
325
        let mut store = MINIMAL.new_store();
326
        assert_eq!(delete(&mut store, &(0..4)), Ok(()));
327
        assert_eq!(store.find(0), Ok(None));
328
        assert_eq!(store.find(1), Ok(None));
329
        assert_eq!(store.find(2), Ok(None));
330
        assert_eq!(store.find(3), Ok(None));
331
    }
332
333
    #[test]
334
    fn delete_chunks() {
335
        let mut store = MINIMAL.new_store();
336
        let value: Vec<_> = (0..60).collect();
337
        assert_eq!(store.insert(0, &value[..52]), Ok(()));
338
        assert_eq!(store.insert(1, &value[52..]), Ok(()));
339
        assert_eq!(delete(&mut store, &(0..4)), Ok(()));
340
        assert_eq!(store.find(0), Ok(None));
341
        assert_eq!(store.find(1), Ok(None));
342
        assert_eq!(store.find(2), Ok(None));
343
        assert_eq!(store.find(3), Ok(None));
344
    }
345
}