ic_stable_structures/
file_mem.rs

1use crate::{Memory, WASM_PAGE_SIZE};
2use std::cell::RefCell;
3use std::fs::File;
4use std::io::{Read, Seek, SeekFrom, Write};
5use std::rc::Rc;
6
7/// A `Memory` backed by a file.
8#[derive(Clone)]
9pub struct FileMemory(Rc<RefCell<File>>);
10
11impl FileMemory {
12    pub fn new(file: File) -> Self {
13        Self(Rc::new(RefCell::new(file)))
14    }
15}
16
17impl Memory for FileMemory {
18    fn size(&self) -> u64 {
19        let len = self.0.borrow().metadata().unwrap().len();
20        assert_eq!(
21            len % WASM_PAGE_SIZE,
22            0,
23            "File size must correspond to exact page sizes"
24        );
25        len / WASM_PAGE_SIZE
26    }
27
28    fn grow(&self, pages: u64) -> i64 {
29        let previous_size = self.size();
30        self.0
31            .borrow()
32            .set_len((previous_size + pages) * WASM_PAGE_SIZE)
33            .expect("grow must succeed");
34        assert_eq!(self.size(), previous_size + pages);
35        previous_size as i64
36    }
37
38    fn read(&self, offset: u64, dst: &mut [u8]) {
39        self.0
40            .borrow_mut()
41            .seek(SeekFrom::Start(offset))
42            .expect("out of bounds");
43        let bytes_read = self.0.borrow_mut().read(dst).expect("out of bounds");
44        assert_eq!(bytes_read, dst.len(), "out of bounds");
45    }
46
47    fn write(&self, offset: u64, src: &[u8]) {
48        self.0
49            .borrow_mut()
50            .seek(SeekFrom::Start(offset))
51            .expect("out of bounds");
52        let bytes_written = self.0.borrow_mut().write(src).expect("out of bounds");
53        assert_eq!(bytes_written, src.len(), "out of bounds");
54    }
55}
56
57#[cfg(test)]
58mod test {
59    use super::*;
60    use crate::write;
61    use proptest::prelude::*;
62
63    fn make_vec_memory() -> Rc<RefCell<Vec<u8>>> {
64        Rc::new(RefCell::new(Vec::new()))
65    }
66
67    fn make_file_memory() -> FileMemory {
68        FileMemory::new(tempfile::tempfile().unwrap())
69    }
70
71    #[test]
72    fn write_and_read_random_bytes() {
73        let vec_mem = make_vec_memory();
74        let file_mem = make_file_memory();
75
76        proptest!(|(
77            data in proptest::collection::vec(0..u8::MAX, 0..2*WASM_PAGE_SIZE as usize),
78            offset in 0..10*WASM_PAGE_SIZE
79        )| {
80            // Write a random blob into the memory, growing the memory as it needs to.
81            write(&file_mem, offset, &data);
82
83            // Verify the blob can be read back.
84            let mut bytes = vec![0; data.len()];
85            file_mem.read(offset, &mut bytes);
86            assert_eq!(bytes, data);
87
88            // Do the same write to vec mem and verify both memories are identical.
89            write(&vec_mem, offset, &data);
90            assert_eq!(vec_mem.size(), file_mem.size());
91            let mut buf = vec![0; (file_mem.size() * WASM_PAGE_SIZE) as usize];
92            file_mem.read(0, &mut buf);
93            assert_eq!(buf.as_slice(), vec_mem.borrow().as_slice());
94        });
95    }
96}