wasm_dbms_memory/memory_access.rs
1// Rust guideline compliant 2026-04-27
2// X-WHERE-CLAUSE, M-CANONICAL-DOCS
3
4//! Trait abstracting page-level memory operations.
5//!
6//! [`MemoryAccess`] is the primary interface consumed by registry and
7//! ledger code in this crate. [`MemoryManager`](crate::MemoryManager)
8//! implements it with direct writes; the DBMS crate provides a
9//! journaled wrapper that records original bytes before each write.
10
11use wasm_dbms_api::prelude::{Encode, MemoryResult, Page, PageOffset};
12
13use crate::memory_manager::UNCLAIMED_PAGES_PAGE;
14use crate::unclaimed_pages::UnclaimedPages;
15
16/// Abstraction over page-level memory operations.
17///
18/// All table-registry and ledger functions are generic over this trait
19/// so that callers can transparently add write-ahead journaling or
20/// other interceptors without modifying the memory crate.
21pub trait MemoryAccess {
22 /// Returns the size of a single memory page.
23 fn page_size(&self) -> u64;
24
25 /// Grows the underlying memory by exactly one page and returns the
26 /// freshly allocated page number.
27 ///
28 /// The returned page is zero-initialized. This primitive is **not
29 /// journaled** — page growth cannot be rolled back, so a transaction
30 /// that aborts after a `grow_one_page` simply leaks the new page.
31 fn grow_one_page(&mut self) -> MemoryResult<Page>;
32
33 /// Zeros out an entire page. Used by [`MemoryAccess::unclaim_page`]
34 /// to scrub residual data before publishing the page to the
35 /// unclaimed-pages ledger.
36 fn zero_page(&mut self, page: Page) -> MemoryResult<()>;
37
38 /// Hands out a page for use by a caller.
39 ///
40 /// Reuses a page from the unclaimed-pages ledger when one is
41 /// available; otherwise grows the memory by one page.
42 fn claim_page(&mut self) -> MemoryResult<Page> {
43 let mut ledger: UnclaimedPages = self.read_at(UNCLAIMED_PAGES_PAGE, 0)?;
44 if let Some(page) = ledger.pop() {
45 self.write_at(UNCLAIMED_PAGES_PAGE, 0, &ledger)?;
46 return Ok(page);
47 }
48 self.grow_one_page()
49 }
50
51 /// Returns `page` to the unclaimed-pages ledger so it can be reused
52 /// by a future [`MemoryAccess::claim_page`] call.
53 ///
54 /// The page contents are zeroed before being published to the
55 /// ledger.
56 fn unclaim_page(&mut self, page: Page) -> MemoryResult<()> {
57 self.zero_page(page)?;
58 let mut ledger: UnclaimedPages = self.read_at(UNCLAIMED_PAGES_PAGE, 0)?;
59 ledger.push(page)?;
60 self.write_at(UNCLAIMED_PAGES_PAGE, 0, &ledger)
61 }
62
63 /// Reads a typed value from the specified page and offset.
64 fn read_at<D>(&mut self, page: Page, offset: PageOffset) -> MemoryResult<D>
65 where
66 D: Encode;
67
68 /// Writes a typed value at the specified page and offset.
69 fn write_at<E>(&mut self, page: Page, offset: PageOffset, data: &E) -> MemoryResult<()>
70 where
71 E: Encode;
72
73 /// Writes raw bytes at the specified page and offset, bypassing
74 /// alignment and encoding checks.
75 fn write_at_raw(&mut self, page: Page, offset: PageOffset, buf: &[u8]) -> MemoryResult<()>;
76
77 /// Zeros out the region occupied by `data` at the specified page
78 /// and offset.
79 fn zero<E>(&mut self, page: Page, offset: PageOffset, data: &E) -> MemoryResult<()>
80 where
81 E: Encode;
82
83 /// Zeros out `len` raw bytes at the specified page and offset.
84 ///
85 /// Used by the migration apply pipeline when scrubbing a record whose
86 /// size is known only at runtime (from a stored snapshot).
87 fn zero_raw(&mut self, page: Page, offset: PageOffset, len: PageOffset) -> MemoryResult<()>;
88
89 /// Reads raw bytes into `buf` at the specified page and offset.
90 ///
91 /// Returns the number of bytes actually read.
92 fn read_at_raw(
93 &mut self,
94 page: Page,
95 offset: PageOffset,
96 buf: &mut [u8],
97 ) -> MemoryResult<usize>;
98}