Skip to main content

wasm_dbms_memory/
provider.rs

1// Rust guideline compliant 2026-02-28
2
3use wasm_dbms_api::prelude::{MemoryError, MemoryResult};
4
5/// The size of a WASM memory page in bytes (64 KiB).
6pub const WASM_PAGE_SIZE: u64 = 65536;
7
8/// Memory Provider trait defines the interface for interacting with the underlying memory.
9///
10/// Abstracting memory access allows different implementations for production
11/// (e.g. stable memory) and testing (heap-based).
12pub trait MemoryProvider {
13    /// The size of a memory page in bytes.
14    const PAGE_SIZE: u64;
15
16    /// Gets the current size of the memory in bytes.
17    fn size(&self) -> u64;
18
19    /// Gets the amount of pages currently allocated.
20    fn pages(&self) -> u64;
21
22    /// Attempts to grow the memory by `new_pages` (added pages).
23    ///
24    /// Returns an error if it wasn't possible. Otherwise, returns the previous size that was reserved.
25    ///
26    /// Actual reserved size after the growth will be `previous_size + (new_pages * PAGE_SIZE)`.
27    fn grow(&mut self, new_pages: u64) -> MemoryResult<u64>;
28
29    /// Reads data from memory starting at `offset` into the provided buffer `buf`.
30    ///
31    /// Returns an error if `offset + buf.len()` exceeds the current memory size.
32    fn read(&self, offset: u64, buf: &mut [u8]) -> MemoryResult<()>;
33
34    /// Writes data from the provided buffer `buf` into memory starting at `offset`.
35    ///
36    /// Returns an error if `offset + buf.len()` exceeds the current memory size.
37    fn write(&mut self, offset: u64, buf: &[u8]) -> MemoryResult<()>;
38}
39
40/// An implementation of [`MemoryProvider`] that uses heap memory for testing purposes.
41#[derive(Debug, Default)]
42pub struct HeapMemoryProvider {
43    memory: Vec<u8>,
44}
45
46impl MemoryProvider for HeapMemoryProvider {
47    const PAGE_SIZE: u64 = WASM_PAGE_SIZE; // 64 KiB
48
49    fn grow(&mut self, new_pages: u64) -> MemoryResult<u64> {
50        let previous_size = self.size();
51        let additional_size = (new_pages * Self::PAGE_SIZE) as usize;
52        self.memory
53            .resize(previous_size as usize + additional_size, 0);
54        Ok(previous_size)
55    }
56
57    fn size(&self) -> u64 {
58        self.memory.len() as u64
59    }
60
61    fn pages(&self) -> u64 {
62        self.size() / Self::PAGE_SIZE
63    }
64
65    fn read(&self, offset: u64, buf: &mut [u8]) -> MemoryResult<()> {
66        // check if the read is within bounds
67        if offset + buf.len() as u64 > self.size() {
68            return Err(MemoryError::OutOfBounds);
69        }
70
71        buf.copy_from_slice(&self.memory[offset as usize..(offset as usize + buf.len())]);
72        Ok(())
73    }
74
75    fn write(&mut self, offset: u64, buf: &[u8]) -> MemoryResult<()> {
76        // check if the write is within bounds
77        if offset + buf.len() as u64 > self.size() {
78            return Err(MemoryError::OutOfBounds);
79        }
80
81        self.memory[offset as usize..(offset as usize + buf.len())].copy_from_slice(buf);
82        Ok(())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88
89    use super::*;
90
91    #[test]
92    fn test_should_grow_heap_memory() {
93        let mut provider = HeapMemoryProvider::default();
94        assert_eq!(provider.size(), 0);
95
96        let previous_size = provider.grow(2).unwrap();
97        assert_eq!(previous_size, 0);
98        assert_eq!(provider.size(), 2 * HeapMemoryProvider::PAGE_SIZE);
99
100        let previous_size = provider.grow(1).unwrap();
101        assert_eq!(previous_size, 2 * HeapMemoryProvider::PAGE_SIZE);
102        assert_eq!(provider.size(), 3 * HeapMemoryProvider::PAGE_SIZE);
103    }
104
105    #[test]
106    fn test_should_read_and_write_heap_memory() {
107        let mut provider = HeapMemoryProvider::default();
108        provider.grow(1).unwrap(); // grow by 1 page (64 KiB)
109        let data_to_write = vec![1, 2, 3, 4, 5];
110        provider.write(0, &data_to_write).unwrap();
111        let mut buffer = vec![0; 5];
112        provider.read(0, &mut buffer).unwrap();
113        assert_eq!(buffer, data_to_write);
114    }
115
116    #[test]
117    fn test_should_not_read_out_of_bounds_heap_memory() {
118        let mut provider = HeapMemoryProvider::default();
119        provider.grow(1).unwrap(); // grow by 1 page (64 KiB)
120        let mut buffer = vec![0; 10];
121        let result = provider.read(HeapMemoryProvider::PAGE_SIZE - 5, &mut buffer);
122        assert!(result.is_err());
123        assert!(matches!(result.err().unwrap(), MemoryError::OutOfBounds));
124    }
125
126    #[test]
127    fn test_should_not_write_out_of_bounds_heap_memory() {
128        let mut provider = HeapMemoryProvider::default();
129        provider.grow(1).unwrap(); // grow by 1 page (64 KiB)
130        let data_to_write = vec![1, 2, 3, 4, 5];
131        let result = provider.write(HeapMemoryProvider::PAGE_SIZE - 3, &data_to_write);
132        assert!(result.is_err());
133        assert!(matches!(result.err().unwrap(), MemoryError::OutOfBounds));
134    }
135
136    #[test]
137    fn test_should_get_amount_of_pages_heap_memory() {
138        let mut provider = HeapMemoryProvider::default();
139        assert_eq!(provider.pages(), 0);
140
141        provider.grow(3).unwrap(); // grow by 3 pages
142        assert_eq!(provider.pages(), 3);
143
144        provider.grow(2).unwrap(); // grow by 2 more pages
145        assert_eq!(provider.pages(), 5);
146    }
147}