use crate::runtime::{memory_management::ManagedMemoryBinding, server::IoError, storage_id_type};
use core::fmt::Debug;
storage_id_type!(StorageId);
impl core::fmt::Display for StorageId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_fmt(format_args!("StorageId({})", self.value))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StorageUtilization {
pub offset: u64,
pub size: u64,
}
#[derive(new, Clone, Debug)]
pub struct StorageHandle {
pub id: StorageId,
pub utilization: StorageUtilization,
}
impl StorageHandle {
pub fn size(&self) -> u64 {
self.utilization.size
}
pub fn offset(&self) -> u64 {
self.utilization.offset
}
pub fn offset_start(&self, offset_bytes: u64) -> Self {
let utilization = StorageUtilization {
offset: self.offset().checked_add(offset_bytes)
.expect("storage offset overflow"),
size: self.size().checked_sub(offset_bytes)
.expect("storage prefix exceeds resource size"),
};
Self {
id: self.id,
utilization,
}
}
pub fn offset_end(&self, offset_bytes: u64) -> Self {
let utilization = StorageUtilization {
offset: self.offset(),
size: self.size().checked_sub(offset_bytes)
.expect("storage suffix exceeds resource size"),
};
Self {
id: self.id,
utilization,
}
}
}
pub trait ComputeStorage: Send {
type Resource: Send;
fn alignment(&self) -> usize;
fn get(&mut self, handle: &StorageHandle) -> Self::Resource;
fn alloc(&mut self, size: u64) -> Result<StorageHandle, IoError>;
fn dealloc(&mut self, id: StorageId);
fn flush(&mut self);
}
#[derive(new, Debug)]
pub struct ManagedResource<Resource: Send> {
#[allow(unused)]
binding: ManagedMemoryBinding,
resource: Resource,
}
impl<Resource: Send> ManagedResource<Resource> {
pub fn resource(&self) -> &Resource {
&self.resource
}
}
#[cfg(test)]
mod handle_safety_tests {
use super::*;
#[test]
fn valid_handle_slicing_preserves_bounds() {
let handle = StorageHandle::new(StorageId::new(), StorageUtilization { offset: 8, size: 16 });
let sliced = handle.offset_start(4).offset_end(3);
assert_eq!(sliced.offset(), 12);
assert_eq!(sliced.size(), 9);
assert_eq!(handle.offset_start(16).size(), 0);
}
#[test]
#[should_panic(expected = "storage prefix exceeds resource size")]
fn oversized_prefix_never_wraps_in_release_mode() {
StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
.offset_start(2);
}
#[test]
#[should_panic(expected = "storage suffix exceeds resource size")]
fn oversized_suffix_never_wraps_in_release_mode() {
StorageHandle::new(StorageId::new(), StorageUtilization { offset: 0, size: 1 })
.offset_end(2);
}
#[test]
#[should_panic(expected = "storage offset overflow")]
fn offset_overflow_never_wraps_in_release_mode() {
StorageHandle::new(StorageId::new(), StorageUtilization { offset: u64::MAX, size: 2 })
.offset_start(1);
}
}