Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/OpenSK/libraries/persistent_store/src/buffer.rs
Line
Count
Source
1
// Copyright 2019-2020 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
//! Flash storage for testing.
16
//!
17
//! [`BufferStorage`] implements the flash [`Storage`] interface but doesn't interface with an
18
//! actual flash storage. Instead it uses a buffer in memory to represent the storage state.
19
20
use crate::{Storage, StorageError, StorageIndex, StorageResult};
21
use alloc::borrow::{Borrow, Cow};
22
use alloc::boxed::Box;
23
use alloc::vec;
24
25
/// Simulates a flash storage using a buffer in memory.
26
///
27
/// This buffer storage can be used in place of an actual flash storage. It is particularly useful
28
/// for tests and fuzzing, for which it has dedicated functionalities.
29
///
30
/// This storage tracks how many times words are written between page erase cycles, how many times
31
/// pages are erased, and whether an operation flips bits in the wrong direction. Operations panic
32
/// if those conditions are broken (optional). This storage also permits to interrupt operations for
33
/// inspection or to corrupt the operation.
34
#[derive(Clone)]
35
pub struct BufferStorage {
36
    /// Content of the storage.
37
    storage: Box<[u8]>,
38
39
    /// Options of the storage.
40
    options: BufferOptions,
41
42
    /// Number of times a word was written since the last time its page was erased.
43
    word_writes: Box<[usize]>,
44
45
    /// Number of times a page was erased.
46
    page_erases: Box<[usize]>,
47
48
    /// Interruption state.
49
    interruption: Interruption,
50
}
51
52
/// Options of a buffer storage.
53
#[derive(Clone, Debug)]
54
pub struct BufferOptions {
55
    /// Size of a word in bytes.
56
    pub word_size: usize,
57
58
    /// Size of a page in bytes.
59
    pub page_size: usize,
60
61
    /// How many times a word can be written between page erase cycles.
62
    pub max_word_writes: usize,
63
64
    /// How many times a page can be erased.
65
    pub max_page_erases: usize,
66
67
    /// Whether the storage should check the flash invariant.
68
    ///
69
    /// When set, the following conditions would panic:
70
    /// - A bit is written from 0 to 1.
71
    /// - A word is written more than [`Self::max_word_writes`].
72
    /// - A page is erased more than [`Self::max_page_erases`].
73
    pub strict_mode: bool,
74
}
75
76
/// Corrupts a slice given actual and expected value.
77
///
78
/// A corruption function is called exactly once and takes 2 arguments:
79
/// - A mutable slice representing the storage before the interrupted operation.
80
/// - A shared slice representing what the storage would have been if the operation was not
81
///   interrupted.
82
///
83
/// The corruption function may flip an arbitrary number of bits in the mutable slice, but may only
84
/// flip bits that differ between both slices.
85
pub type BufferCorruptFunction<'a> = Box<dyn FnOnce(&mut [u8], &[u8]) + 'a>;
86
87
impl BufferStorage {
88
    /// Creates a buffer storage.
89
    ///
90
    /// # Panics
91
    ///
92
    /// The following preconditions must hold:
93
    /// - `options.word_size` must be a power of two.
94
    /// - `options.page_size` must be a power of two.
95
    /// - `options.page_size` must be word-aligned.
96
    /// - `storage.len()` must be page-aligned.
97
27.3k
    pub fn new(storage: Box<[u8]>, options: BufferOptions) -> BufferStorage {
98
27.3k
        assert!(options.word_size.is_power_of_two());
99
27.3k
        assert!(options.page_size.is_power_of_two());
100
27.3k
        let num_words = storage.len() / options.word_size;
101
27.3k
        let num_pages = storage.len() / options.page_size;
102
27.3k
        let buffer = BufferStorage {
103
27.3k
            storage,
104
27.3k
            options,
105
27.3k
            word_writes: vec![0; num_words].into_boxed_slice(),
106
27.3k
            page_erases: vec![0; num_pages].into_boxed_slice(),
107
27.3k
            interruption: Interruption::Ready,
108
27.3k
        };
109
27.3k
        assert!(buffer.is_word_aligned(buffer.options.page_size));
110
27.3k
        assert!(buffer.is_page_aligned(buffer.storage.len()));
111
27.3k
        buffer
112
27.3k
    }
113
114
    /// Arms an interruption after a given delay.
115
    ///
116
    /// Before each subsequent mutable operation (write or erase), the delay is decremented if
117
    /// positive. If the delay is elapsed, the operation is saved and an error is returned.
118
    /// Subsequent operations will panic until either of:
119
    /// - The interrupted operation is [corrupted](BufferStorage::corrupt_operation).
120
    /// - The interruption is [reset](BufferStorage::reset_interruption).
121
    ///
122
    /// # Panics
123
    ///
124
    /// Panics if an interruption is already armed.
125
525k
    pub fn arm_interruption(&mut self, delay: usize) {
126
525k
        self.interruption.arm(delay);
127
525k
    }
128
129
    /// Disarms an interruption that did not trigger.
130
    ///
131
    /// Returns the remaining delay.
132
    ///
133
    /// # Panics
134
    ///
135
    /// Panics if any of the following conditions hold:
136
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
137
    /// - An interruption was armed and it has triggered.
138
376k
    pub fn disarm_interruption(&mut self) -> usize {
139
376k
        self.interruption.get().err().unwrap()
140
376k
    }
141
142
    /// Resets an interruption regardless of triggering.
143
    ///
144
    /// # Panics
145
    ///
146
    /// Panics if an interruption was not [armed](BufferStorage::arm_interruption).
147
147
    pub fn reset_interruption(&mut self) {
148
147
        let _ = self.interruption.get();
149
147
    }
150
151
    /// Corrupts an interrupted operation.
152
    ///
153
    /// Applies the corruption function to the storage. Counters are updated accordingly:
154
    /// - If a word is fully written, its counter is incremented regardless of whether other words
155
    ///   of the same operation have been fully written.
156
    /// - If a page is fully erased, its counter is incremented (and its word counters are reset).
157
    ///
158
    /// # Panics
159
    ///
160
    /// Panics if any of the following conditions hold:
161
    /// - An interruption was not [armed](BufferStorage::arm_interruption).
162
    /// - An interruption was armed but did not trigger.
163
    /// - The corruption function corrupts more bits than allowed.
164
    /// - The interrupted operation itself would have panicked.
165
119k
    pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) {
166
119k
        let operation = self.interruption.get().unwrap();
167
119k
        let range = self.operation_range(&operation).unwrap();
168
119k
        let mut before = self.storage[range.clone()].to_vec().into_boxed_slice();
169
119k
        match operation {
170
116k
            BufferOperation::Write { value: after, .. } => {
171
116k
                corrupt(&mut before, &after);
172
116k
                self.incr_word_writes(range.start, &before, &after);
173
116k
            }
174
2.56k
            BufferOperation::Erase { page } => {
175
2.56k
                let after = vec![0xff; self.page_size()].into_boxed_slice();
176
2.56k
                corrupt(&mut before, &after);
177
2.56k
                if before == after {
178
84
                    self.incr_page_erases(page);
179
2.47k
                }
180
            }
181
        };
182
119k
        self.storage[range].copy_from_slice(&before);
183
119k
    }
184
185
    /// Returns the number of times a word was written.
186
1.15G
    pub fn get_word_writes(&self, word: usize) -> usize {
187
1.15G
        self.word_writes[word]
188
1.15G
    }
189
190
    /// Returns the number of times a page was erased.
191
3.27M
    pub fn get_page_erases(&self, page: usize) -> usize {
192
3.27M
        self.page_erases[page]
193
3.27M
    }
194
195
    /// Sets the number of times a page was erased.
196
41.4k
    pub fn set_page_erases(&mut self, page: usize, cycle: usize) {
197
41.4k
        self.page_erases[page] = cycle;
198
41.4k
    }
199
200
    /// Returns whether a number is word-aligned.
201
2.71M
    fn is_word_aligned(&self, x: usize) -> bool {
202
2.71M
        x & (self.options.word_size - 1) == 0
203
2.71M
    }
204
205
    /// Returns whether a number is page-aligned.
206
27.3k
    fn is_page_aligned(&self, x: usize) -> bool {
207
27.3k
        x & (self.options.page_size - 1) == 0
208
27.3k
    }
209
210
    /// Updates the counters as if a page was erased.
211
    ///
212
    /// The page counter of that page is incremented and the word counters of that page are reset.
213
    ///
214
    /// # Panics
215
    ///
216
    /// Panics if the [maximum number of erase cycles per page](BufferOptions::max_page_erases) is
217
    /// reached.
218
36.7k
    fn incr_page_erases(&mut self, page: usize) {
219
        // Check that pages are not erased too many times.
220
36.7k
        if self.options.strict_mode {
221
23.3k
            assert!(self.page_erases[page] < self.max_page_erases());
222
13.3k
        }
223
36.7k
        self.page_erases[page] += 1;
224
36.7k
        let num_words = self.page_size() / self.word_size();
225
7.01M
        for word in 0..num_words {
226
7.01M
            self.word_writes[page * num_words + word] = 0;
227
7.01M
        }
228
36.7k
    }
229
230
    /// Updates the word counters as if a partial write occurred.
231
    ///
232
    /// The partial write is described as if `complete` was supposed to be written to the storage
233
    /// starting at byte `index`, but actually only `value` was written. Word counters are
234
    /// incremented only if their value would change and they would be completely written.
235
    ///
236
    /// # Preconditions
237
    ///
238
    /// - `index` must be word-aligned.
239
    /// - `value` and `complete` must have the same word-aligned length.
240
    ///
241
    /// # Panics
242
    ///
243
    /// Panics if the [maximum number of writes per word](BufferOptions::max_word_writes) is
244
    /// reached.
245
1.34M
    fn incr_word_writes(&mut self, index: usize, value: &[u8], complete: &[u8]) {
246
1.34M
        let word_size = self.word_size();
247
29.6M
        for i in 0..value.len() / word_size {
248
29.6M
            let range = core::ops::Range {
249
29.6M
                start: i * word_size,
250
29.6M
                end: (i + 1) * word_size,
251
29.6M
            };
252
            // Partial word writes do not count.
253
29.6M
            if value[range.clone()] != complete[range.clone()] {
254
271k
                continue;
255
29.3M
            }
256
            // Words are written only if necessary.
257
29.3M
            if value[range.clone()] == self.storage[index..][range] {
258
23.6k
                continue;
259
29.3M
            }
260
29.3M
            let word = index / word_size + i;
261
            // Check that words are not written too many times.
262
29.3M
            if self.options.strict_mode {
263
8.76M
                assert!(self.word_writes[word] < self.max_word_writes());
264
20.5M
            }
265
29.3M
            self.word_writes[word] += 1;
266
        }
267
1.34M
    }
268
269
    /// Returns the storage range of an operation.
270
1.50M
    fn operation_range(
271
1.50M
        &self,
272
1.50M
        operation: &BufferOperation<impl Borrow<[u8]>>,
273
1.50M
    ) -> StorageResult<core::ops::Range<usize>> {
274
1.50M
        match *operation {
275
1.46M
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
276
41.8k
            BufferOperation::Erase { page } => {
277
41.8k
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
278
            }
279
        }
280
1.50M
    }
<persistent_store::buffer::BufferStorage>::operation_range::<alloc::boxed::Box<[u8]>>
Line
Count
Source
270
119k
    fn operation_range(
271
119k
        &self,
272
119k
        operation: &BufferOperation<impl Borrow<[u8]>>,
273
119k
    ) -> StorageResult<core::ops::Range<usize>> {
274
119k
        match *operation {
275
116k
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
276
2.56k
            BufferOperation::Erase { page } => {
277
2.56k
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
278
            }
279
        }
280
119k
    }
<persistent_store::buffer::BufferStorage>::operation_range::<&[u8]>
Line
Count
Source
270
1.38M
    fn operation_range(
271
1.38M
        &self,
272
1.38M
        operation: &BufferOperation<impl Borrow<[u8]>>,
273
1.38M
    ) -> StorageResult<core::ops::Range<usize>> {
274
1.38M
        match *operation {
275
1.34M
            BufferOperation::Write { index, ref value } => index.range(value.borrow().len(), self),
276
39.2k
            BufferOperation::Erase { page } => {
277
39.2k
                StorageIndex { page, byte: 0 }.range(self.page_size(), self)
278
            }
279
        }
280
1.38M
    }
281
}
282
283
impl Storage for BufferStorage {
284
1.65M
    fn word_size(&self) -> usize {
285
1.65M
        self.options.word_size
286
1.65M
    }
287
288
636M
    fn page_size(&self) -> usize {
289
636M
        self.options.page_size
290
636M
    }
291
292
318M
    fn num_pages(&self) -> usize {
293
318M
        self.storage.len() / self.options.page_size
294
318M
    }
295
296
9.03M
    fn max_word_writes(&self) -> usize {
297
9.03M
        self.options.max_word_writes
298
9.03M
    }
299
300
568k
    fn max_page_erases(&self) -> usize {
301
568k
        self.options.max_page_erases
302
568k
    }
303
304
316M
    fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult<Cow<[u8]>> {
305
316M
        Ok(Cow::Borrowed(&self.storage[index.range(length, self)?]))
306
316M
    }
307
308
1.34M
    fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> {
309
1.34M
        if !self.is_word_aligned(index.byte) || !self.is_word_aligned(value.len()) {
310
0
            return Err(StorageError::NotAligned);
311
1.34M
        }
312
1.34M
        let operation = BufferOperation::Write { index, value };
313
1.34M
        let range = self.operation_range(&operation)?;
314
        // Interrupt operation if armed and delay expired.
315
1.34M
        self.interruption.tick(&operation)?;
316
        // Check and update counters.
317
1.22M
        self.incr_word_writes(range.start, value, value);
318
        // Check that bits are correctly flipped.
319
1.22M
        if self.options.strict_mode {
320
35.0M
            for (byte, &val) in range.clone().zip(value.iter()) {
321
35.0M
                assert_eq!(self.storage[byte] & val, val);
322
            }
323
588k
        }
324
        // Write to the storage.
325
1.22M
        self.storage[range].copy_from_slice(value);
326
1.22M
        Ok(())
327
1.34M
    }
328
329
39.2k
    fn erase_page(&mut self, page: usize) -> StorageResult<()> {
330
39.2k
        let operation = BufferOperation::Erase { page };
331
39.2k
        let range = self.operation_range(&operation)?;
332
        // Interrupt operation if armed and delay expired.
333
39.2k
        self.interruption.tick(&operation)?;
334
        // Check and update counters.
335
36.7k
        self.incr_page_erases(page);
336
        // Write to the storage.
337
28.0M
        for byte in &mut self.storage[range] {
338
28.0M
            *byte = 0xff;
339
28.0M
        }
340
36.7k
        Ok(())
341
39.2k
    }
342
}
343
344
impl core::fmt::Display for BufferStorage {
345
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
346
0
        let num_pages = self.num_pages();
347
0
        let num_words = self.page_size() / self.word_size();
348
0
        let num_bytes = self.word_size();
349
0
        for page in 0..num_pages {
350
0
            write!(f, "[{}]", self.page_erases[page])?;
351
0
            for word in 0..num_words {
352
0
                write!(f, " [{}]", self.word_writes[page * num_words + word])?;
353
0
                for byte in 0..num_bytes {
354
0
                    let index = (page * num_words + word) * num_bytes + byte;
355
0
                    write!(f, "{:02x}", self.storage[index])?;
356
                }
357
            }
358
0
            writeln!(f)?;
359
        }
360
0
        Ok(())
361
0
    }
362
}
363
364
/// Represents a storage operation.
365
///
366
/// It is polymorphic over the ownership of the byte slice to avoid unnecessary copies.
367
#[derive(Clone, Debug, PartialEq, Eq)]
368
enum BufferOperation<ByteSlice: Borrow<[u8]>> {
369
    /// Represents a write operation.
370
    Write {
371
        /// The storage index at which the write should occur.
372
        index: StorageIndex,
373
374
        /// The slice that should be written.
375
        value: ByteSlice,
376
    },
377
378
    /// Represents an erase operation.
379
    Erase {
380
        /// The page that should be erased.
381
        page: usize,
382
    },
383
}
384
385
/// Represents a storage operation owning its byte slices.
386
type OwnedBufferOperation = BufferOperation<Box<[u8]>>;
387
388
/// Represents a storage operation sharing its byte slices.
389
type SharedBufferOperation<'a> = BufferOperation<&'a [u8]>;
390
391
impl<'a> SharedBufferOperation<'a> {
392
119k
    fn to_owned(&self) -> OwnedBufferOperation {
393
119k
        match *self {
394
116k
            BufferOperation::Write { index, value } => BufferOperation::Write {
395
116k
                index,
396
116k
                value: value.to_vec().into_boxed_slice(),
397
116k
            },
398
2.56k
            BufferOperation::Erase { page } => BufferOperation::Erase { page },
399
        }
400
119k
    }
401
}
402
403
/// Controls when an operation is interrupted.
404
///
405
/// This can be used to simulate power-offs while the device is writing to the storage or erasing a
406
/// page in the storage.
407
#[derive(Clone)]
408
enum Interruption {
409
    /// Mutable operations have normal behavior.
410
    Ready,
411
412
    /// If the delay is positive, mutable operations decrement it. If the count is zero, mutable
413
    /// operations fail and are saved.
414
    Armed { delay: usize },
415
416
    /// Mutable operations panic.
417
    Saved { operation: OwnedBufferOperation },
418
}
419
420
impl Interruption {
421
    /// Arms an interruption for a given delay.
422
    ///
423
    /// # Panics
424
    ///
425
    /// Panics if an interruption is already armed.
426
525k
    fn arm(&mut self, delay: usize) {
427
525k
        match self {
428
525k
            Interruption::Ready => *self = Interruption::Armed { delay },
429
0
            _ => panic!(),
430
        }
431
525k
    }
432
433
    /// Disarms an interruption.
434
    ///
435
    /// Returns the interrupted operation if any, otherwise the remaining delay.
436
    ///
437
    /// # Panics
438
    ///
439
    /// Panics if an interruption was not armed.
440
496k
    fn get(&mut self) -> Result<OwnedBufferOperation, usize> {
441
496k
        let mut interruption = Interruption::Ready;
442
496k
        core::mem::swap(self, &mut interruption);
443
496k
        match interruption {
444
377k
            Interruption::Armed { delay } => Err(delay),
445
119k
            Interruption::Saved { operation } => Ok(operation),
446
0
            _ => panic!(),
447
        }
448
496k
    }
449
450
    /// Interrupts an operation if the delay is over.
451
    ///
452
    /// Decrements the delay if positive. Otherwise, the operation is stored and an error is
453
    /// returned to interrupt the operation.
454
    ///
455
    /// # Panics
456
    ///
457
    /// Panics if an operation has already been interrupted and the interruption has not been
458
    /// disarmed.
459
1.38M
    fn tick(&mut self, operation: &SharedBufferOperation) -> StorageResult<()> {
460
1.33M
        match self {
461
50.9k
            Interruption::Ready => (),
462
1.33M
            Interruption::Armed { delay } if *delay == 0 => {
463
119k
                let operation = operation.to_owned();
464
119k
                *self = Interruption::Saved { operation };
465
119k
                return Err(StorageError::CustomError);
466
            }
467
1.21M
            Interruption::Armed { delay } => *delay -= 1,
468
0
            Interruption::Saved { .. } => panic!(),
469
        }
470
1.26M
        Ok(())
471
1.38M
    }
472
}
473
474
#[cfg(test)]
475
mod tests {
476
    use super::*;
477
478
    const NUM_PAGES: usize = 2;
479
    const OPTIONS: BufferOptions = BufferOptions {
480
        word_size: 4,
481
        page_size: 16,
482
        max_word_writes: 2,
483
        max_page_erases: 3,
484
        strict_mode: true,
485
    };
486
    // Those words are decreasing bit patterns. Bits are only changed from 1 to 0 and at least one
487
    // bit is changed.
488
    const BLANK_WORD: &[u8] = &[0xff, 0xff, 0xff, 0xff];
489
    const FIRST_WORD: &[u8] = &[0xee, 0xdd, 0xbb, 0x77];
490
    const SECOND_WORD: &[u8] = &[0xca, 0xc9, 0xa9, 0x65];
491
    const THIRD_WORD: &[u8] = &[0x88, 0x88, 0x88, 0x44];
492
493
    fn new_storage() -> Box<[u8]> {
494
        vec![0xff; NUM_PAGES * OPTIONS.page_size].into_boxed_slice()
495
    }
496
497
    #[test]
498
    fn words_are_decreasing() {
499
        fn assert_is_decreasing(prev: &[u8], next: &[u8]) {
500
            for (&prev, &next) in prev.iter().zip(next.iter()) {
501
                assert_eq!(prev & next, next);
502
                assert!(prev != next);
503
            }
504
        }
505
        assert_is_decreasing(BLANK_WORD, FIRST_WORD);
506
        assert_is_decreasing(FIRST_WORD, SECOND_WORD);
507
        assert_is_decreasing(SECOND_WORD, THIRD_WORD);
508
    }
509
510
    #[test]
511
    fn options_ok() {
512
        let buffer = BufferStorage::new(new_storage(), OPTIONS);
513
        assert_eq!(buffer.word_size(), OPTIONS.word_size);
514
        assert_eq!(buffer.page_size(), OPTIONS.page_size);
515
        assert_eq!(buffer.num_pages(), NUM_PAGES);
516
        assert_eq!(buffer.max_word_writes(), OPTIONS.max_word_writes);
517
        assert_eq!(buffer.max_page_erases(), OPTIONS.max_page_erases);
518
    }
519
520
    #[test]
521
    fn read_write_ok() {
522
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
523
        let index = StorageIndex { page: 0, byte: 0 };
524
        let next_index = StorageIndex { page: 0, byte: 4 };
525
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
526
        buffer.write_slice(index, FIRST_WORD).unwrap();
527
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
528
        assert_eq!(buffer.read_slice(next_index, 4).unwrap(), BLANK_WORD);
529
    }
530
531
    #[test]
532
    fn erase_ok() {
533
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
534
        let index = StorageIndex { page: 0, byte: 0 };
535
        let other_index = StorageIndex { page: 1, byte: 0 };
536
        buffer.write_slice(index, FIRST_WORD).unwrap();
537
        buffer.write_slice(other_index, FIRST_WORD).unwrap();
538
        assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
539
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
540
        buffer.erase_page(0).unwrap();
541
        assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
542
        assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
543
    }
544
545
    #[test]
546
    fn invalid_range() {
547
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
548
        let index = StorageIndex { page: 0, byte: 12 };
549
        let half_index = StorageIndex { page: 0, byte: 14 };
550
        let over_index = StorageIndex { page: 0, byte: 16 };
551
        let bad_page = StorageIndex { page: 2, byte: 0 };
552
553
        // Reading a word in the storage is ok.
554
        assert!(buffer.read_slice(index, 4).is_ok());
555
        // Reading a half-word in the storage is ok.
556
        assert!(buffer.read_slice(half_index, 2).is_ok());
557
        // Reading even a single byte outside a page is not ok.
558
        assert!(buffer.read_slice(over_index, 1).is_err());
559
        // But reading an empty slice just after a page is ok.
560
        assert!(buffer.read_slice(over_index, 0).is_ok());
561
        // Reading even an empty slice outside the storage is not ok.
562
        assert!(buffer.read_slice(bad_page, 0).is_err());
563
564
        // Writing a word in the storage is ok.
565
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
566
        // Writing an unaligned word is not ok.
567
        assert!(buffer.write_slice(half_index, FIRST_WORD).is_err());
568
        // Writing a word outside a page is not ok.
569
        assert!(buffer.write_slice(over_index, FIRST_WORD).is_err());
570
        // But writing an empty slice just after a page is ok.
571
        assert!(buffer.write_slice(over_index, &[]).is_ok());
572
        // Writing even an empty slice outside the storage is not ok.
573
        assert!(buffer.write_slice(bad_page, &[]).is_err());
574
575
        // Only pages in the storage can be erased.
576
        assert!(buffer.erase_page(0).is_ok());
577
        assert!(buffer.erase_page(2).is_err());
578
    }
579
580
    #[test]
581
    fn write_twice_ok() {
582
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
583
        let index = StorageIndex { page: 0, byte: 4 };
584
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
585
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
586
    }
587
588
    #[test]
589
    fn write_twice_and_once_ok() {
590
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
591
        let index = StorageIndex { page: 0, byte: 0 };
592
        let next_index = StorageIndex { page: 0, byte: 4 };
593
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
594
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
595
        assert!(buffer.write_slice(next_index, THIRD_WORD).is_ok());
596
    }
597
598
    #[test]
599
    #[should_panic]
600
    fn write_three_times_panics() {
601
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
602
        let index = StorageIndex { page: 0, byte: 4 };
603
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
604
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
605
        let _ = buffer.write_slice(index, THIRD_WORD);
606
    }
607
608
    #[test]
609
    fn write_twice_then_once_ok() {
610
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
611
        let index = StorageIndex { page: 0, byte: 0 };
612
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
613
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
614
        assert!(buffer.erase_page(0).is_ok());
615
        assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
616
    }
617
618
    #[test]
619
    fn erase_three_times_ok() {
620
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
621
        assert!(buffer.erase_page(0).is_ok());
622
        assert!(buffer.erase_page(0).is_ok());
623
        assert!(buffer.erase_page(0).is_ok());
624
    }
625
626
    #[test]
627
    fn erase_three_times_and_once_ok() {
628
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
629
        assert!(buffer.erase_page(0).is_ok());
630
        assert!(buffer.erase_page(0).is_ok());
631
        assert!(buffer.erase_page(0).is_ok());
632
        assert!(buffer.erase_page(1).is_ok());
633
    }
634
635
    #[test]
636
    #[should_panic]
637
    fn erase_four_times_panics() {
638
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
639
        assert!(buffer.erase_page(0).is_ok());
640
        assert!(buffer.erase_page(0).is_ok());
641
        assert!(buffer.erase_page(0).is_ok());
642
        let _ = buffer.erase_page(0).is_ok();
643
    }
644
645
    #[test]
646
    #[should_panic]
647
    fn switch_zero_to_one_panics() {
648
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
649
        let index = StorageIndex { page: 0, byte: 0 };
650
        assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
651
        let _ = buffer.write_slice(index, FIRST_WORD);
652
    }
653
654
    #[test]
655
    fn interrupt_delay_ok() {
656
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
657
658
        // Interrupt the second operation.
659
        buffer.arm_interruption(1);
660
661
        // The first operation should not fail.
662
        buffer
663
            .write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8])
664
            .unwrap();
665
        // The delay should be decremented.
666
        assert_eq!(buffer.disarm_interruption(), 0);
667
        // The storage should have been modified.
668
        assert_eq!(&buffer.storage[..8], &[0x5c; 8]);
669
        assert!(buffer.storage[8..].iter().all(|&x| x == 0xff));
670
    }
671
672
    #[test]
673
    fn interrupt_save_ok() {
674
        let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
675
676
        // Interrupt the second operation.
677
        buffer.arm_interruption(1);
678
679
        // The second operation should fail.
680
        buffer
681
            .write_slice(StorageIndex { page: 0, byte: 0 }, &[0x5c; 8])
682
            .unwrap();
683
        assert!(buffer
684
            .write_slice(StorageIndex { page: 0, byte: 8 }, &[0x93; 8])
685
            .is_err());
686
        // The operation should represent the change.
687
        buffer.corrupt_operation(Box::new(|_, value| assert_eq!(value, &[0x93; 8])));
688
        // The storage should not have been modified.
689
        assert_eq!(&buffer.storage[..8], &[0x5c; 8]);
690
        assert!(buffer.storage[8..].iter().all(|&x| x == 0xff));
691
    }
692
}