use wasm_dbms_api::prelude::{MemoryError, MemoryResult};
pub const WASM_PAGE_SIZE: u64 = 65536;
pub trait MemoryProvider {
const PAGE_SIZE: u64;
fn size(&self) -> u64;
fn pages(&self) -> u64;
fn grow(&mut self, new_pages: u64) -> MemoryResult<u64>;
fn read(&mut self, offset: u64, buf: &mut [u8]) -> MemoryResult<()>;
fn write(&mut self, offset: u64, buf: &[u8]) -> MemoryResult<()>;
}
#[derive(Debug, Default)]
pub struct HeapMemoryProvider {
memory: Vec<u8>,
}
impl MemoryProvider for HeapMemoryProvider {
const PAGE_SIZE: u64 = WASM_PAGE_SIZE;
fn grow(&mut self, new_pages: u64) -> MemoryResult<u64> {
let previous_size = self.size();
let additional_size = (new_pages * Self::PAGE_SIZE) as usize;
self.memory
.resize(previous_size as usize + additional_size, 0);
Ok(previous_size)
}
fn size(&self) -> u64 {
self.memory.len() as u64
}
fn pages(&self) -> u64 {
self.size() / Self::PAGE_SIZE
}
fn read(&mut self, offset: u64, buf: &mut [u8]) -> MemoryResult<()> {
if offset + buf.len() as u64 > self.size() {
return Err(MemoryError::OutOfBounds);
}
buf.copy_from_slice(&self.memory[offset as usize..(offset as usize + buf.len())]);
Ok(())
}
fn write(&mut self, offset: u64, buf: &[u8]) -> MemoryResult<()> {
if offset + buf.len() as u64 > self.size() {
return Err(MemoryError::OutOfBounds);
}
self.memory[offset as usize..(offset as usize + buf.len())].copy_from_slice(buf);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_grow_heap_memory() {
let mut provider = HeapMemoryProvider::default();
assert_eq!(provider.size(), 0);
let previous_size = provider.grow(2).unwrap();
assert_eq!(previous_size, 0);
assert_eq!(provider.size(), 2 * HeapMemoryProvider::PAGE_SIZE);
let previous_size = provider.grow(1).unwrap();
assert_eq!(previous_size, 2 * HeapMemoryProvider::PAGE_SIZE);
assert_eq!(provider.size(), 3 * HeapMemoryProvider::PAGE_SIZE);
}
#[test]
fn test_should_read_and_write_heap_memory() {
let mut provider = HeapMemoryProvider::default();
provider.grow(1).unwrap(); let data_to_write = vec![1, 2, 3, 4, 5];
provider.write(0, &data_to_write).unwrap();
let mut buffer = vec![0; 5];
provider.read(0, &mut buffer).unwrap();
assert_eq!(buffer, data_to_write);
}
#[test]
fn test_should_not_read_out_of_bounds_heap_memory() {
let mut provider = HeapMemoryProvider::default();
provider.grow(1).unwrap(); let mut buffer = vec![0; 10];
let result = provider.read(HeapMemoryProvider::PAGE_SIZE - 5, &mut buffer);
assert!(result.is_err());
assert!(matches!(result.err().unwrap(), MemoryError::OutOfBounds));
}
#[test]
fn test_should_not_write_out_of_bounds_heap_memory() {
let mut provider = HeapMemoryProvider::default();
provider.grow(1).unwrap(); let data_to_write = vec![1, 2, 3, 4, 5];
let result = provider.write(HeapMemoryProvider::PAGE_SIZE - 3, &data_to_write);
assert!(result.is_err());
assert!(matches!(result.err().unwrap(), MemoryError::OutOfBounds));
}
#[test]
fn test_should_get_amount_of_pages_heap_memory() {
let mut provider = HeapMemoryProvider::default();
assert_eq!(provider.pages(), 0);
provider.grow(3).unwrap(); assert_eq!(provider.pages(), 3);
provider.grow(2).unwrap(); assert_eq!(provider.pages(), 5);
}
}