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 alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
90
91 fn dealloc(&mut self, id: StorageId);
95
96 fn flush(&mut self);
98}
99
100#[derive(new, Debug)]
102pub struct ManagedResource<Resource: Send> {
103 #[allow(unused)]
107 binding: ManagedMemoryBinding,
108 resource: Resource,
109}
110
111impl<Resource: Send> ManagedResource<Resource> {
112 pub fn resource(&self) -> &Resource {
121 &self.resource
122 }
123}
124
125
126#[cfg(test)]
127mod handle_safety_tests {
128 use super::*;
129
130 #[test]
131 fn valid_handle_slicing_preserves_bounds() {
132 let handle = StorageHandle::new(StorageId::new(), StorageUtilization { offset: 8, size: 16 });
133 let sliced = handle.offset_start(4).offset_end(3);
134 assert_eq!(sliced.offset(), 12);
135 assert_eq!(sliced.size(), 9);
136 assert_eq!(handle.offset_start(16).size(), 0);
137 }
138
139 #[test]
140 #[should_panic(expected = "storage prefix exceeds resource size")]
141 fn oversized_prefix_never_wraps_in_release_mode() {
142 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
143 .offset_start(2);
144 }
145
146 #[test]
147 #[should_panic(expected = "storage suffix exceeds resource size")]
148 fn oversized_suffix_never_wraps_in_release_mode() {
149 StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
150 .offset_end(2);
151 }
152
153 #[test]
154 #[should_panic(expected = "storage offset overflow")]
155 fn offset_overflow_never_wraps_in_release_mode() {
156 StorageHandle::new(StorageId::new(), StorageUtilization { offset: u64::MAX, size: 2 })
157 .offset_start(1);
158 }
159}