qualia_core_db/solvers/calculus/
workspace.rs1pub const SENTINEL_PASS_BYTES: usize = 42 * 1024 * 1024;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum WorkspaceError {
7 SizeOverflow,
8 SentinelExceeded { required: usize },
9 BufferTooSmall { required: usize, available: usize },
10}
11
12pub const fn checked_workspace_bytes<T>(elements: usize) -> Result<usize, WorkspaceError> {
13 let Some(required) = elements.checked_mul(core::mem::size_of::<T>()) else {
14 return Err(WorkspaceError::SizeOverflow);
15 };
16 if required > SENTINEL_PASS_BYTES {
17 return Err(WorkspaceError::SentinelExceeded { required });
18 }
19 Ok(required)
20}
21
22pub struct Workspace<'a, T> {
23 storage: &'a mut [T],
24 used: usize,
25}
26
27impl<'a, T> Workspace<'a, T> {
28 pub fn new(storage: &'a mut [T]) -> Result<Self, WorkspaceError> {
29 checked_workspace_bytes::<T>(storage.len())?;
30 Ok(Self { storage, used: 0 })
31 }
32
33 pub const fn capacity(&self) -> usize {
34 self.storage.len()
35 }
36
37 pub const fn used(&self) -> usize {
38 self.used
39 }
40
41 pub const fn remaining(&self) -> usize {
42 self.storage.len() - self.used
43 }
44
45 pub fn reset(&mut self) {
46 self.used = 0;
47 }
48
49 pub fn take(&mut self, elements: usize) -> Result<&mut [T], WorkspaceError> {
50 let end = self
51 .used
52 .checked_add(elements)
53 .ok_or(WorkspaceError::SizeOverflow)?;
54 if end > self.storage.len() {
55 return Err(WorkspaceError::BufferTooSmall {
56 required: end,
57 available: self.storage.len(),
58 });
59 }
60 let start = self.used;
61 self.used = end;
62 Ok(&mut self.storage[start..end])
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn workspace_is_caller_owned_and_resettable() {
72 let mut storage = [0.0_f64; 16];
73 let mut workspace = Workspace::new(&mut storage).unwrap();
74 assert_eq!(workspace.take(5).unwrap().len(), 5);
75 assert_eq!(workspace.used(), 5);
76 assert_eq!(workspace.remaining(), 11);
77 workspace.reset();
78 assert_eq!(workspace.used(), 0);
79 }
80
81 #[test]
82 fn sentinel_and_capacity_fail_closed() {
83 assert!(matches!(
84 checked_workspace_bytes::<u64>(SENTINEL_PASS_BYTES / 8 + 1),
85 Err(WorkspaceError::SentinelExceeded { .. })
86 ));
87 let mut storage = [0_u32; 4];
88 let mut workspace = Workspace::new(&mut storage).unwrap();
89 assert_eq!(
90 workspace.take(5),
91 Err(WorkspaceError::BufferTooSmall {
92 required: 5,
93 available: 4
94 })
95 );
96 }
97}