use super::{
Any, Buffer, MemoryDescriptor, Result, StorageError, StorageKind, nixl::NixlDescriptor,
};
#[derive(Clone)]
pub struct OffsetBuffer {
base: Buffer,
offset: usize,
size: usize,
}
impl std::fmt::Debug for OffsetBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OffsetBuffer")
.field("base", &self.base)
.field("offset", &self.offset)
.field("size", &self.size)
.finish()
}
}
impl OffsetBuffer {
pub fn new(base: Buffer, offset: usize, size: usize) -> Result<Self> {
let end = offset
.checked_add(size)
.ok_or_else(|| StorageError::Unsupported("offset overflow".into()))?;
if end > base.size() {
return Err(StorageError::Unsupported(
"offset region exceeds base allocation bounds".into(),
));
}
Ok(Self { base, offset, size })
}
pub fn from_inner_address(base: Buffer, address: usize, size: usize) -> Result<Self> {
let end = address
.checked_add(size)
.ok_or_else(|| StorageError::Unsupported("address + size overflow".into()))?;
let base_end = base
.addr()
.checked_add(base.size())
.ok_or_else(|| StorageError::Unsupported("base address + size overflow".into()))?;
if address < base.addr() || end > base_end {
return Err(StorageError::Unsupported("address out of bounds".into()));
}
let offset = address - base.addr();
Self::new(base, offset, size)
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn base(&self) -> &Buffer {
&self.base
}
}
impl MemoryDescriptor for OffsetBuffer {
fn addr(&self) -> usize {
self.base.addr() + self.offset
}
fn size(&self) -> usize {
self.size
}
fn storage_kind(&self) -> StorageKind {
self.base.storage_kind()
}
fn as_any(&self) -> &dyn Any {
self
}
fn nixl_descriptor(&self) -> Option<NixlDescriptor> {
let mut descriptor = self.base.nixl_descriptor()?;
descriptor.addr = self.addr() as u64;
descriptor.size = self.size();
Some(descriptor)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SystemStorage;
fn create_test_buffer(size: usize) -> Buffer {
Buffer::new(SystemStorage::new(size).expect("allocation failed"))
}
#[test]
fn test_offset_buffer_new_valid() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
assert_eq!(offset_buf.offset(), 100);
assert_eq!(offset_buf.size(), 200);
}
#[test]
fn test_offset_buffer_new_zero_offset() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base.clone(), 0, 1024).expect("should succeed");
assert_eq!(offset_buf.offset(), 0);
assert_eq!(offset_buf.size(), 1024);
assert_eq!(offset_buf.addr(), base.addr());
}
#[test]
fn test_offset_buffer_new_at_end() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 1024, 0).expect("should succeed");
assert_eq!(offset_buf.offset(), 1024);
assert_eq!(offset_buf.size(), 0);
}
#[test]
fn test_offset_buffer_new_invalid_offset() {
let base = create_test_buffer(1024);
let result = OffsetBuffer::new(base, 1025, 0);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_new_invalid_size() {
let base = create_test_buffer(1024);
let result = OffsetBuffer::new(base, 100, 1000);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_new_size_overflow() {
let base = create_test_buffer(1024);
let result = OffsetBuffer::new(base, usize::MAX, 1);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_from_inner_address_valid() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let offset_buf =
OffsetBuffer::from_inner_address(base, base_addr + 100, 200).expect("should succeed");
assert_eq!(offset_buf.offset(), 100);
assert_eq!(offset_buf.size(), 200);
}
#[test]
fn test_offset_buffer_from_inner_address_at_start() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let offset_buf = OffsetBuffer::from_inner_address(base.clone(), base_addr, 1024)
.expect("should succeed");
assert_eq!(offset_buf.offset(), 0);
assert_eq!(offset_buf.addr(), base.addr());
}
#[test]
fn test_offset_buffer_from_inner_address_overflow() {
let base = create_test_buffer(1024);
let result = OffsetBuffer::from_inner_address(base, usize::MAX, 1);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_from_inner_address_out_of_bounds_before() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let result = OffsetBuffer::from_inner_address(base, base_addr.saturating_sub(1), 100);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_from_inner_address_out_of_bounds_after() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let result = OffsetBuffer::from_inner_address(base, base_addr + 900, 200);
assert!(result.is_err());
}
#[test]
fn test_offset_buffer_accessors() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let offset_buf = OffsetBuffer::new(base, 256, 512).expect("should succeed");
assert_eq!(offset_buf.offset(), 256);
assert_eq!(offset_buf.base().addr(), base_addr);
assert_eq!(offset_buf.base().size(), 1024);
}
#[test]
fn test_offset_buffer_memory_descriptor_addr() {
let base = create_test_buffer(1024);
let base_addr = base.addr();
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
assert_eq!(offset_buf.addr(), base_addr + 100);
}
#[test]
fn test_offset_buffer_memory_descriptor_size() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
assert_eq!(offset_buf.size(), 200);
}
#[test]
fn test_offset_buffer_memory_descriptor_storage_kind() {
let base = create_test_buffer(1024);
let base_kind = base.storage_kind();
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
assert_eq!(offset_buf.storage_kind(), base_kind);
}
#[test]
fn test_offset_buffer_as_any() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
let any_ref = offset_buf.as_any();
assert!(any_ref.downcast_ref::<OffsetBuffer>().is_some());
}
#[test]
fn test_offset_buffer_clone() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
let cloned = offset_buf.clone();
assert_eq!(offset_buf.addr(), cloned.addr());
assert_eq!(offset_buf.size(), cloned.size());
assert_eq!(offset_buf.offset(), cloned.offset());
}
#[test]
fn test_offset_buffer_debug() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
let debug_str = format!("{:?}", offset_buf);
assert!(debug_str.contains("OffsetBuffer"));
assert!(debug_str.contains("offset"));
assert!(debug_str.contains("size"));
}
#[test]
fn test_offset_buffer_nixl_descriptor_none() {
let base = create_test_buffer(1024);
let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
assert!(offset_buf.nixl_descriptor().is_none());
}
}