ruda_runtime/runtime/storage/
base.rs1use crate::runtime::{memory_management::ManagedMemoryBinding, server::IoError, storage_id_type};
2use core::fmt::Debug;
3
4storage_id_type!(StorageId);
6
7impl core::fmt::Display for StorageId {
8 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9 f.write_fmt(format_args!("StorageId({})", self.value))
10 }
11}
12
13#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct StorageUtilization {
16 pub offset: u64,
18 pub size: u64,
20}
21
22#[derive(new, Clone, Debug)]
24pub struct StorageHandle {
25 pub id: StorageId,
27 pub utilization: StorageUtilization,
29}
30
31impl StorageHandle {
32 pub fn size(&self) -> u64 {
38 self.utilization.size
39 }
40
41 pub fn offset(&self) -> u64 {
43 self.utilization.offset
44 }
45
46 pub fn offset_start(&self, offset_bytes: u64) -> Self {
48 let utilization = StorageUtilization {
49 offset: self.offset().checked_add(offset_bytes)
50 .expect("storage offset overflow"),
51 size: self.size().checked_sub(offset_bytes)
52 .expect("storage prefix exceeds resource size"),
53 };
54
55 Self {
56 id: self.id,
57 utilization,
58 }
59 }
60
61 pub fn offset_end(&self, offset_bytes: u64) -> Self {
63 let utilization = StorageUtilization {
64 offset: self.offset(),
65 size: self.size().checked_sub(offset_bytes)
66 .expect("storage suffix exceeds resource size"),
67 };
68
69 Self {
70 id: self.id,
71 utilization,
72 }
73 }
74}
75
76pub trait ComputeStorage: Send {
78 type Resource: Send;
81
82 fn alignment(&self) -> usize;
84
85 fn get(&mut self, handle: &StorageHandle) -> Self::Resource;
87
88 fn get_pinned(&mut self, handle: &StorageHandle, _binding: ManagedMemoryBinding) -> Self::Resource {
90 self.get(handle)
91 }
92
93 fn supports_relocation(&self) -> bool { false }
95
96 fn available_memory(&self) -> Option<u64> { None }
98
99 fn relocation_barrier(&mut self) -> Result<(), IoError> {
101 Err(IoError::UnsupportedIoOperation { backtrace: ruda_core::backtrace::BackTrace::capture() })
102 }
103
104 fn relocation_copy(&mut self, _source: &StorageHandle, _target: &StorageHandle) -> Result<(), IoError> {
106 Err(IoError::UnsupportedIoOperation { backtrace: ruda_core::backtrace::BackTrace::capture() })
107 }
108
109 fn relocation_copy_batch<'a>(
111 &mut self,
112 copies: impl IntoIterator<Item = (&'a StorageHandle, &'a StorageHandle)>,
113 ) -> Result<(), IoError>
114 where
115 Self: Sized,
116 {
117 for (source, target) in copies { self.relocation_copy(source, target)?; }
118 Ok(())
119 }
120
121 fn relocation_complete(&mut self) -> Result<(), IoError> {
123 self.relocation_barrier()
124 }
125
126 fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
128
129 fn dealloc(&mut self, id: StorageId);
133
134 fn flush(&mut self);
136}
137
138#[derive(new, Debug)]
140pub struct ManagedResource<Resource: Send> {
141 #[allow(unused)]
145 binding: ManagedMemoryBinding,
146 resource: Resource,
147}
148
149impl<Resource: Send> ManagedResource<Resource> {
150 pub fn resource(&self) -> &Resource {
159 &self.resource
160 }
161}
162
163
164#[cfg(test)]
165mod handle_safety_tests {
166 use super::*;
167
168 #[test]
169 fn valid_handle_slicing_preserves_bounds() {
170 let handle = StorageHandle::new(StorageId::new(), StorageUtilization { offset: 8, size: 16 });
171 let sliced = handle.offset_start(4).offset_end(3);
172 assert_eq!(sliced.offset(), 12);
173 assert_eq!(sliced.size(), 9);
174 assert_eq!(handle.offset_start(16).size(), 0);
175 }
176
177 #[test]
178 #[should_panic(expected = "storage prefix exceeds resource size")]
179 fn oversized_prefix_never_wraps_in_release_mode() {
180 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
181 .offset_start(2);
182 }
183
184 #[test]
185 #[should_panic(expected = "storage suffix exceeds resource size")]
186 fn oversized_suffix_never_wraps_in_release_mode() {
187 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
188 .offset_end(2);
189 }
190
191 #[test]
192 #[should_panic(expected = "storage offset overflow")]
193 fn offset_overflow_never_wraps_in_release_mode() {
194 StorageHandle::new(StorageId::new(), StorageUtilization { offset: u64::MAX, size: 2 })
195 .offset_start(1);
196 }
197}