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 relocation_barrier(&mut self) -> Result<(), IoError> {
98 Err(IoError::UnsupportedIoOperation { backtrace: ruda_core::backtrace::BackTrace::capture() })
99 }
100
101 fn relocation_copy(&mut self, _source: &StorageHandle, _target: &StorageHandle) -> Result<(), IoError> {
103 Err(IoError::UnsupportedIoOperation { backtrace: ruda_core::backtrace::BackTrace::capture() })
104 }
105
106 fn relocation_complete(&mut self) -> Result<(), IoError> {
108 self.relocation_barrier()
109 }
110
111 fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
113
114 fn dealloc(&mut self, id: StorageId);
118
119 fn flush(&mut self);
121}
122
123#[derive(new, Debug)]
125pub struct ManagedResource<Resource: Send> {
126 #[allow(unused)]
130 binding: ManagedMemoryBinding,
131 resource: Resource,
132}
133
134impl<Resource: Send> ManagedResource<Resource> {
135 pub fn resource(&self) -> &Resource {
144 &self.resource
145 }
146}
147
148
149#[cfg(test)]
150mod handle_safety_tests {
151 use super::*;
152
153 #[test]
154 fn valid_handle_slicing_preserves_bounds() {
155 let handle = StorageHandle::new(StorageId::new(), StorageUtilization { offset: 8, size: 16 });
156 let sliced = handle.offset_start(4).offset_end(3);
157 assert_eq!(sliced.offset(), 12);
158 assert_eq!(sliced.size(), 9);
159 assert_eq!(handle.offset_start(16).size(), 0);
160 }
161
162 #[test]
163 #[should_panic(expected = "storage prefix exceeds resource size")]
164 fn oversized_prefix_never_wraps_in_release_mode() {
165 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
166 .offset_start(2);
167 }
168
169 #[test]
170 #[should_panic(expected = "storage suffix exceeds resource size")]
171 fn oversized_suffix_never_wraps_in_release_mode() {
172 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
173 .offset_end(2);
174 }
175
176 #[test]
177 #[should_panic(expected = "storage offset overflow")]
178 fn offset_overflow_never_wraps_in_release_mode() {
179 StorageHandle::new(StorageId::new(), StorageUtilization { offset: u64::MAX, size: 2 })
180 .offset_start(1);
181 }
182}