1use crate::pool::{PageRun, PAGE_BYTES};
4
5const PAGE_WORDS: usize = PAGE_BYTES / 4;
6
7pub struct PageBuffer<const PAGES: usize> {
19 pages: [[u32; PAGE_WORDS]; PAGES],
20}
21
22impl<const PAGES: usize> Default for PageBuffer<PAGES> {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl<const PAGES: usize> PageBuffer<PAGES> {
29 pub const fn new() -> Self {
31 Self {
32 pages: [[0; PAGE_WORDS]; PAGES],
33 }
34 }
35
36 pub const fn page_count(&self) -> usize {
38 PAGES
39 }
40
41 fn contains(run: PageRun, len: usize) -> bool {
42 (run.end_page() as usize) <= PAGES && len <= run.capacity_bytes()
43 }
44
45 pub fn bytes(&self, run: PageRun, len: usize) -> Option<&[u8]> {
48 if !Self::contains(run, len) {
49 return None;
50 }
51 Some(unsafe {
55 core::slice::from_raw_parts(
56 self.pages
57 .as_ptr()
58 .cast::<u8>()
59 .add(run.first_page as usize * PAGE_BYTES),
60 len,
61 )
62 })
63 }
64
65 pub fn bytes_mut(&mut self, run: PageRun, len: usize) -> Option<&mut [u8]> {
67 if !Self::contains(run, len) {
68 return None;
69 }
70 Some(unsafe {
72 core::slice::from_raw_parts_mut(
73 self.pages
74 .as_mut_ptr()
75 .cast::<u8>()
76 .add(run.first_page as usize * PAGE_BYTES),
77 len,
78 )
79 })
80 }
81
82 pub fn write(&mut self, run: PageRun, offset: usize, data: &[u8]) -> bool {
84 let Some(end) = offset.checked_add(data.len()) else {
85 return false;
86 };
87 match self.bytes_mut(run, end) {
88 Some(bytes) => {
89 bytes[offset..].copy_from_slice(data);
90 true
91 }
92 None => false,
93 }
94 }
95
96 pub fn move_pages(&mut self, from: PageRun, to_page: u32) -> bool {
100 let to = PageRun {
101 first_page: to_page,
102 page_count: from.page_count,
103 };
104 if from.end_page() as usize > PAGES || to.end_page() as usize > PAGES {
105 return false;
106 }
107 self.pages.copy_within(
108 from.first_page as usize..from.end_page() as usize,
109 to_page as usize,
110 );
111 true
112 }
113
114 pub fn page_address(&mut self, page: u32) -> Option<*mut u8> {
117 ((page as usize) < PAGES).then(|| {
118 self.pages
119 .as_mut_ptr()
120 .cast::<u8>()
121 .wrapping_add(page as usize * PAGE_BYTES)
122 })
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn writes_and_reads_across_a_page_boundary_without_staging() {
132 let mut buffer = PageBuffer::<3>::new();
133 let run = PageRun {
134 first_page: 0,
135 page_count: 2,
136 };
137 let payload = [1u8, 2, 3, 4, 5, 6, 7, 8];
138 assert!(buffer.write(run, PAGE_BYTES - 4, &payload));
139 let bytes = buffer.bytes(run, PAGE_BYTES + 4).unwrap();
140 assert_eq!(&bytes[PAGE_BYTES - 4..], &payload);
141 }
142
143 #[test]
144 fn refuses_views_outside_the_buffer_or_run() {
145 let mut buffer = PageBuffer::<2>::new();
146 let run = PageRun {
147 first_page: 1,
148 page_count: 1,
149 };
150 assert!(buffer.bytes(run, PAGE_BYTES + 1).is_none());
151 assert!(!buffer.write(run, PAGE_BYTES - 1, &[0, 0]));
152 let outside = PageRun {
153 first_page: 1,
154 page_count: 2,
155 };
156 assert!(buffer.bytes(outside, 1).is_none());
157 assert!(!buffer.move_pages(outside, 0));
158 assert!(buffer.page_address(2).is_none());
159 assert!(buffer.page_address(1).is_some());
160 }
161
162 #[test]
163 fn move_pages_preserves_bytes_even_when_ranges_overlap() {
164 let mut buffer = PageBuffer::<4>::new();
165 let source = PageRun {
166 first_page: 1,
167 page_count: 3,
168 };
169 buffer.write(source, 0, &[9; 8]);
170 buffer.write(source, 3 * PAGE_BYTES - 2, &[7, 8]);
171 assert!(buffer.move_pages(source, 0));
172 let moved = PageRun {
173 first_page: 0,
174 page_count: 3,
175 };
176 assert_eq!(&buffer.bytes(moved, 8).unwrap()[..8], &[9; 8]);
177 assert_eq!(
178 &buffer.bytes(moved, 3 * PAGE_BYTES).unwrap()[3 * PAGE_BYTES - 2..],
179 &[7, 8]
180 );
181 }
182}