1use std::collections::BTreeMap;
11
12use crate::types::{MemAddr, PluginId, Size};
13
14const PAGE_BITS: u32 = 12;
16const PAGE_SIZE: usize = 1 << PAGE_BITS; #[inline]
21fn page_of(addr: u64) -> u64 {
22 addr >> PAGE_BITS
23}
24
25#[inline]
27fn offset_in_page(addr: u64) -> usize {
28 (addr & ((1u64 << PAGE_BITS) - 1)) as usize
29}
30
31#[derive(Debug)]
33pub enum MemoryError {
34 OutOfBounds(MemAddr, Size, Size),
36 DoubleFree(MemAddr),
38 UseAfterFree(MemAddr),
40 NotAllocated(MemAddr),
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct LinearMemory {
56 pub(crate) pages: BTreeMap<u64, Box<[u8; PAGE_SIZE]>>,
58
59 pub(crate) bounds: Size,
63}
64
65impl LinearMemory {
66 pub fn empty(size: Size) -> Self {
70 LinearMemory {
71 pages: BTreeMap::new(),
72 bounds: size,
73 }
74 }
75
76 pub fn addr_in_bounds(&self, addr: MemAddr, len: Size) -> bool {
80 match addr.checked_add(len) {
82 Some(end) => end <= self.bounds,
83 None => false,
84 }
85 }
86
87 pub fn read(&self, addr: MemAddr) -> u8 {
93 match self.pages.get(&page_of(addr)) {
94 Some(page) => page[offset_in_page(addr)],
95 None => 0,
96 }
97 }
98
99 pub fn write(&self, addr: MemAddr, val: u8) -> Self {
105 let mut new = self.clone();
106 new.write_mut(addr, val);
107 new
108 }
109
110 pub fn write_mut(&mut self, addr: MemAddr, val: u8) {
114 let pg = page_of(addr);
115 let off = offset_in_page(addr);
116 let page = self
117 .pages
118 .entry(pg)
119 .or_insert_with(|| Box::new([0u8; PAGE_SIZE]));
120 page[off] = val;
121 }
122
123 #[inline]
125 pub fn bounds(&self) -> Size {
126 self.bounds
127 }
128
129 #[inline]
131 pub fn written_bytes(&self) -> usize {
132 self.pages.len() * PAGE_SIZE
133 }
134
135 #[inline]
137 pub fn is_empty(&self) -> bool {
138 self.pages.is_empty()
139 }
140
141 pub fn read_range(&self, addr: MemAddr, len: Size) -> Result<Vec<u8>, MemoryError> {
149 if !self.addr_in_bounds(addr, len) {
150 return Err(MemoryError::OutOfBounds(addr, len, self.bounds));
151 }
152
153 let mut result = Vec::with_capacity(len as usize);
154 let mut pos = addr;
155 let end = addr + len;
156 while pos < end {
157 let pg = page_of(pos);
158 let off = offset_in_page(pos);
159 let page_remaining = PAGE_SIZE - off;
161 let needed = (end - pos) as usize;
162 let chunk = page_remaining.min(needed);
163
164 match self.pages.get(&pg) {
165 Some(page) => result.extend_from_slice(&page[off..off + chunk]),
166 None => result.resize(result.len() + chunk, 0),
167 }
168 pos += chunk as u64;
169 }
170 Ok(result)
171 }
172
173 pub fn write_range(&self, addr: MemAddr, data: &[u8]) -> Result<Self, MemoryError> {
181 let len = data.len() as Size;
182 if !self.addr_in_bounds(addr, len) {
183 return Err(MemoryError::OutOfBounds(addr, len, self.bounds));
184 }
185
186 let mut new = self.clone();
187 new.write_range_mut(addr, data);
188 Ok(new)
189 }
190
191 pub fn write_range_mut(&mut self, addr: MemAddr, data: &[u8]) {
193 let mut pos = addr;
194 let mut src_off = 0usize;
195 while src_off < data.len() {
196 let pg = page_of(pos);
197 let off = offset_in_page(pos);
198 let page_remaining = PAGE_SIZE - off;
199 let remaining_data = data.len() - src_off;
200 let chunk = page_remaining.min(remaining_data);
201
202 let page = self
203 .pages
204 .entry(pg)
205 .or_insert_with(|| Box::new([0u8; PAGE_SIZE]));
206 page[off..off + chunk].copy_from_slice(&data[src_off..src_off + chunk]);
207
208 pos += chunk as u64;
209 src_off += chunk;
210 }
211 }
212}
213
214impl Default for LinearMemory {
215 fn default() -> Self {
217 LinearMemory::empty(0)
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Default)]
225pub enum ResourceStatus {
226 #[default]
230 Unallocated,
231
232 Allocated {
236 owner: PluginId,
238 },
239
240 Freed,
244}
245
246impl ResourceStatus {
247 #[inline]
249 pub fn is_allocated(&self) -> bool {
250 matches!(self, ResourceStatus::Allocated { .. })
251 }
252
253 #[inline]
255 pub fn is_freed(&self) -> bool {
256 matches!(self, ResourceStatus::Freed)
257 }
258
259 #[inline]
261 pub fn is_unallocated(&self) -> bool {
262 matches!(self, ResourceStatus::Unallocated)
263 }
264
265 #[inline]
267 pub fn owner(&self) -> Option<PluginId> {
268 match self {
269 ResourceStatus::Allocated { owner } => Some(*owner),
270 _ => None,
271 }
272 }
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
282pub struct MetaState {
283 pub(crate) resources: BTreeMap<MemAddr, ResourceStatus>,
288
289 pub(crate) freed_set: Vec<MemAddr>,
294}
295
296impl MetaState {
297 pub fn empty() -> Self {
301 MetaState {
302 resources: BTreeMap::new(),
303 freed_set: Vec::new(),
304 }
305 }
306
307 fn is_freed_addr(&self, addr: MemAddr) -> bool {
309 self.freed_set.binary_search(&addr).is_ok()
310 }
311
312 fn insert_freed_addr(freed_set: &mut Vec<MemAddr>, addr: MemAddr) {
314 match freed_set.binary_search(&addr) {
315 Ok(_) => {} Err(pos) => freed_set.insert(pos, addr),
317 }
318 }
319
320 pub fn is_live(&self, addr: MemAddr) -> bool {
324 match self.resources.get(&addr) {
325 Some(ResourceStatus::Allocated { .. }) => !self.is_freed_addr(addr),
326 _ => false,
327 }
328 }
329
330 pub fn alloc(&self, addr: MemAddr, owner: PluginId) -> Self {
336 let mut new_resources = self.resources.clone();
337 new_resources.insert(addr, ResourceStatus::Allocated { owner });
338 MetaState {
339 resources: new_resources,
340 freed_set: self.freed_set.clone(),
341 }
342 }
343
344 pub fn free(&self, addr: MemAddr) -> Self {
351 let mut new_freed_set = self.freed_set.clone();
352 Self::insert_freed_addr(&mut new_freed_set, addr);
353 MetaState {
354 resources: self.resources.clone(),
355 freed_set: new_freed_set,
356 }
357 }
358
359 pub fn alloc_mut(&mut self, addr: MemAddr, owner: PluginId) {
361 self.resources
362 .insert(addr, ResourceStatus::Allocated { owner });
363 }
364
365 pub fn free_mut(&mut self, addr: MemAddr) -> Result<(), MemoryError> {
375 if self.is_freed_addr(addr) {
376 return Err(MemoryError::DoubleFree(addr));
377 }
378
379 match self.resources.get_mut(&addr) {
380 Some(status @ ResourceStatus::Allocated { .. }) => {
381 *status = ResourceStatus::Freed;
382 Self::insert_freed_addr(&mut self.freed_set, addr);
383 Ok(())
384 }
385 Some(ResourceStatus::Freed) => Err(MemoryError::DoubleFree(addr)),
386 _ => Err(MemoryError::NotAllocated(addr)),
387 }
388 }
389
390 pub fn allocated_count(&self) -> usize {
392 self.resources.values().filter(|s| s.is_allocated()).count()
393 }
394
395 #[inline]
397 pub fn freed_count(&self) -> usize {
398 self.freed_set.len()
399 }
400
401 #[inline]
403 pub fn resource_count(&self) -> usize {
404 self.resources.len()
405 }
406
407 #[inline]
409 pub fn is_freed(&self, addr: MemAddr) -> bool {
410 self.freed_set.binary_search(&addr).is_ok()
411 }
412
413 #[inline]
415 pub fn get_status(&self, addr: MemAddr) -> Option<&ResourceStatus> {
416 self.resources.get(&addr)
417 }
418}
419
420impl Default for MetaState {
421 fn default() -> Self {
422 MetaState::empty()
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429
430 #[test]
431 fn test_linear_memory_empty() {
432 let mem = LinearMemory::empty(1024);
433 assert_eq!(mem.bounds(), 1024);
434 assert!(mem.is_empty());
435 }
436
437 #[test]
438 fn test_linear_memory_read_uninitialized() {
439 let mem = LinearMemory::empty(1024);
440 assert_eq!(mem.read(0), 0);
442 assert_eq!(mem.read(100), 0);
443 assert_eq!(mem.read(1023), 0);
444 }
445
446 #[test]
447 fn test_linear_memory_write_read() {
448 let mem = LinearMemory::empty(1024);
449 let mem = mem.write(100, 42);
450 assert_eq!(mem.read(100), 42);
451 assert_eq!(mem.read(99), 0);
453 assert_eq!(mem.read(101), 0);
454 }
455
456 #[test]
457 fn test_linear_memory_bounds_check() {
458 let mem = LinearMemory::empty(1024);
459 assert!(mem.addr_in_bounds(0, 1024));
460 assert!(mem.addr_in_bounds(1023, 1));
461 assert!(!mem.addr_in_bounds(1024, 1));
462 assert!(!mem.addr_in_bounds(0, 1025));
463 assert!(!mem.addr_in_bounds(u64::MAX, 1));
465 }
466
467 #[test]
468 fn test_linear_memory_read_range() {
469 let mem = LinearMemory::empty(1024);
470 let mem = mem.write(10, 1);
471 let mem = mem.write(11, 2);
472 let mem = mem.write(12, 3);
473
474 let result = mem.read_range(10, 3);
475 assert!(result.is_ok());
476 assert_eq!(result.ok(), Some(vec![1, 2, 3]));
477 }
478
479 #[test]
480 fn test_linear_memory_read_range_out_of_bounds() {
481 let mem = LinearMemory::empty(100);
482 let result = mem.read_range(90, 20);
483 assert!(matches!(result, Err(MemoryError::OutOfBounds(_, _, _))));
484 }
485
486 #[test]
487 fn test_resource_status() {
488 let unalloc = ResourceStatus::Unallocated;
489 assert!(unalloc.is_unallocated());
490 assert!(!unalloc.is_allocated());
491 assert!(!unalloc.is_freed());
492 assert_eq!(unalloc.owner(), None);
493
494 let alloc = ResourceStatus::Allocated { owner: 42 };
495 assert!(!alloc.is_unallocated());
496 assert!(alloc.is_allocated());
497 assert!(!alloc.is_freed());
498 assert_eq!(alloc.owner(), Some(42));
499
500 let freed = ResourceStatus::Freed;
501 assert!(!freed.is_unallocated());
502 assert!(!freed.is_allocated());
503 assert!(freed.is_freed());
504 assert_eq!(freed.owner(), None);
505 }
506
507 #[test]
508 fn test_meta_state_empty() {
509 let ms = MetaState::empty();
510 assert_eq!(ms.allocated_count(), 0);
511 assert_eq!(ms.freed_count(), 0);
512 assert!(!ms.is_live(0));
513 }
514
515 #[test]
516 fn test_meta_state_alloc_makes_live() {
517 let ms = MetaState::empty();
518 let ms = ms.alloc(100, 1);
519 assert!(ms.is_live(100));
520 assert_eq!(ms.allocated_count(), 1);
521 }
522
523 #[test]
524 fn test_meta_state_free_makes_not_live() {
525 let ms = MetaState::empty();
526 let ms = ms.alloc(100, 1);
527 assert!(ms.is_live(100));
528
529 let ms = ms.free(100);
530 assert!(!ms.is_live(100));
531 assert!(ms.is_freed(100));
532 }
533
534 #[test]
535 fn test_meta_state_alloc_preserves_live() {
536 let ms = MetaState::empty();
538 let ms = ms.alloc(100, 1);
539 let ms = ms.alloc(200, 2);
540
541 assert!(ms.is_live(100));
543 assert!(ms.is_live(200));
544 }
545
546 #[test]
547 fn test_meta_state_free_preserves_live() {
548 let ms = MetaState::empty();
550 let ms = ms.alloc(100, 1);
551 let ms = ms.alloc(200, 2);
552
553 let ms = ms.free(100);
555
556 assert!(!ms.is_live(100));
558 assert!(ms.is_live(200));
559 }
560
561 #[test]
562 fn test_meta_state_double_free_detection() {
563 let mut ms = MetaState::empty();
564 ms.alloc_mut(100, 1);
565
566 assert!(ms.free_mut(100).is_ok());
568
569 let result = ms.free_mut(100);
571 assert!(matches!(result, Err(MemoryError::DoubleFree(100))));
572 }
573
574 #[test]
575 fn test_linear_memory_page_spanning_write_read() {
576 let mem = LinearMemory::empty(8192);
578 let boundary = PAGE_SIZE as u64 - 2; let data = vec![0xAA, 0xBB, 0xCC, 0xDD]; let mem = mem.write_range(boundary, &data).unwrap();
581
582 let read_back = mem.read_range(boundary, 4).unwrap();
583 assert_eq!(read_back, data);
584
585 assert_eq!(mem.read(boundary), 0xAA);
587 assert_eq!(mem.read(boundary + 1), 0xBB);
588 assert_eq!(mem.read(boundary + 2), 0xCC); assert_eq!(mem.read(boundary + 3), 0xDD);
590 }
591}