Skip to main content

dynamo_memory/
offset.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use super::{
5    Any, Buffer, MemoryDescriptor, Result, StorageError, StorageKind, nixl::NixlDescriptor,
6};
7
8/// An [`OffsetBuffer`] is a new [`Buffer`]-like object that represents a sub-region (still contiguous)
9/// within an existing [`Buffer`].
10#[derive(Clone)]
11pub struct OffsetBuffer {
12    base: Buffer,
13    offset: usize,
14    size: usize,
15}
16
17impl std::fmt::Debug for OffsetBuffer {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.debug_struct("OffsetBuffer")
20            .field("base", &self.base)
21            .field("offset", &self.offset)
22            .field("size", &self.size)
23            .finish()
24    }
25}
26
27impl OffsetBuffer {
28    /// Create a new offset view into an existing memory region.
29    ///
30    /// Returns an error if the offset and length exceed the bounds of the base region.
31    pub fn new(base: Buffer, offset: usize, size: usize) -> Result<Self> {
32        let end = offset
33            .checked_add(size)
34            .ok_or_else(|| StorageError::Unsupported("offset overflow".into()))?;
35        if end > base.size() {
36            return Err(StorageError::Unsupported(
37                "offset region exceeds base allocation bounds".into(),
38            ));
39        }
40        Ok(Self { base, offset, size })
41    }
42
43    /// Creates an offset buffer from an absolute address within the base region.
44    pub fn from_inner_address(base: Buffer, address: usize, size: usize) -> Result<Self> {
45        // Use checked arithmetic to prevent overflow
46        let end = address
47            .checked_add(size)
48            .ok_or_else(|| StorageError::Unsupported("address + size overflow".into()))?;
49        let base_end = base
50            .addr()
51            .checked_add(base.size())
52            .ok_or_else(|| StorageError::Unsupported("base address + size overflow".into()))?;
53
54        // Verify address is within the base region
55        if address < base.addr() || end > base_end {
56            return Err(StorageError::Unsupported("address out of bounds".into()));
57        }
58
59        let offset = address - base.addr();
60        Self::new(base, offset, size)
61    }
62
63    /// Get the offset relative to the base mapping.
64    pub fn offset(&self) -> usize {
65        self.offset
66    }
67
68    /// Access the underlying base region.
69    pub fn base(&self) -> &Buffer {
70        &self.base
71    }
72}
73
74impl MemoryDescriptor for OffsetBuffer {
75    fn addr(&self) -> usize {
76        self.base.addr() + self.offset
77    }
78
79    fn size(&self) -> usize {
80        self.size
81    }
82
83    fn storage_kind(&self) -> StorageKind {
84        self.base.storage_kind()
85    }
86
87    fn as_any(&self) -> &dyn Any {
88        self
89    }
90
91    fn nixl_descriptor(&self) -> Option<NixlDescriptor> {
92        let mut descriptor = self.base.nixl_descriptor()?;
93        descriptor.addr = self.addr() as u64;
94        descriptor.size = self.size();
95        Some(descriptor)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::SystemStorage;
103
104    fn create_test_buffer(size: usize) -> Buffer {
105        Buffer::new(SystemStorage::new(size).expect("allocation failed"))
106    }
107
108    #[test]
109    fn test_offset_buffer_new_valid() {
110        let base = create_test_buffer(1024);
111        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
112        assert_eq!(offset_buf.offset(), 100);
113        assert_eq!(offset_buf.size(), 200);
114    }
115
116    #[test]
117    fn test_offset_buffer_new_zero_offset() {
118        let base = create_test_buffer(1024);
119        let offset_buf = OffsetBuffer::new(base.clone(), 0, 1024).expect("should succeed");
120        assert_eq!(offset_buf.offset(), 0);
121        assert_eq!(offset_buf.size(), 1024);
122        assert_eq!(offset_buf.addr(), base.addr());
123    }
124
125    #[test]
126    fn test_offset_buffer_new_at_end() {
127        let base = create_test_buffer(1024);
128        // Offset at exact end with zero size should succeed
129        let offset_buf = OffsetBuffer::new(base, 1024, 0).expect("should succeed");
130        assert_eq!(offset_buf.offset(), 1024);
131        assert_eq!(offset_buf.size(), 0);
132    }
133
134    #[test]
135    fn test_offset_buffer_new_invalid_offset() {
136        let base = create_test_buffer(1024);
137        // Offset beyond bounds
138        let result = OffsetBuffer::new(base, 1025, 0);
139        assert!(result.is_err());
140    }
141
142    #[test]
143    fn test_offset_buffer_new_invalid_size() {
144        let base = create_test_buffer(1024);
145        // Size exceeds remaining space
146        let result = OffsetBuffer::new(base, 100, 1000);
147        assert!(result.is_err());
148    }
149
150    #[test]
151    fn test_offset_buffer_new_size_overflow() {
152        let base = create_test_buffer(1024);
153        // offset + size would overflow usize
154        let result = OffsetBuffer::new(base, usize::MAX, 1);
155        assert!(result.is_err());
156    }
157
158    #[test]
159    fn test_offset_buffer_from_inner_address_valid() {
160        let base = create_test_buffer(1024);
161        let base_addr = base.addr();
162        let offset_buf =
163            OffsetBuffer::from_inner_address(base, base_addr + 100, 200).expect("should succeed");
164        assert_eq!(offset_buf.offset(), 100);
165        assert_eq!(offset_buf.size(), 200);
166    }
167
168    #[test]
169    fn test_offset_buffer_from_inner_address_at_start() {
170        let base = create_test_buffer(1024);
171        let base_addr = base.addr();
172        let offset_buf = OffsetBuffer::from_inner_address(base.clone(), base_addr, 1024)
173            .expect("should succeed");
174        assert_eq!(offset_buf.offset(), 0);
175        assert_eq!(offset_buf.addr(), base.addr());
176    }
177
178    #[test]
179    fn test_offset_buffer_from_inner_address_overflow() {
180        let base = create_test_buffer(1024);
181        // address + size would overflow
182        let result = OffsetBuffer::from_inner_address(base, usize::MAX, 1);
183        assert!(result.is_err());
184    }
185
186    #[test]
187    fn test_offset_buffer_from_inner_address_out_of_bounds_before() {
188        let base = create_test_buffer(1024);
189        let base_addr = base.addr();
190        // Address before base region
191        let result = OffsetBuffer::from_inner_address(base, base_addr.saturating_sub(1), 100);
192        assert!(result.is_err());
193    }
194
195    #[test]
196    fn test_offset_buffer_from_inner_address_out_of_bounds_after() {
197        let base = create_test_buffer(1024);
198        let base_addr = base.addr();
199        // End address beyond base region
200        let result = OffsetBuffer::from_inner_address(base, base_addr + 900, 200);
201        assert!(result.is_err());
202    }
203
204    #[test]
205    fn test_offset_buffer_accessors() {
206        let base = create_test_buffer(1024);
207        let base_addr = base.addr();
208        let offset_buf = OffsetBuffer::new(base, 256, 512).expect("should succeed");
209
210        assert_eq!(offset_buf.offset(), 256);
211        assert_eq!(offset_buf.base().addr(), base_addr);
212        assert_eq!(offset_buf.base().size(), 1024);
213    }
214
215    #[test]
216    fn test_offset_buffer_memory_descriptor_addr() {
217        let base = create_test_buffer(1024);
218        let base_addr = base.addr();
219        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
220
221        // addr() should return base_addr + offset
222        assert_eq!(offset_buf.addr(), base_addr + 100);
223    }
224
225    #[test]
226    fn test_offset_buffer_memory_descriptor_size() {
227        let base = create_test_buffer(1024);
228        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
229        assert_eq!(offset_buf.size(), 200);
230    }
231
232    #[test]
233    fn test_offset_buffer_memory_descriptor_storage_kind() {
234        let base = create_test_buffer(1024);
235        let base_kind = base.storage_kind();
236        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
237
238        // storage_kind should match the base
239        assert_eq!(offset_buf.storage_kind(), base_kind);
240    }
241
242    #[test]
243    fn test_offset_buffer_as_any() {
244        let base = create_test_buffer(1024);
245        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
246
247        // Should be able to downcast to OffsetBuffer
248        let any_ref = offset_buf.as_any();
249        assert!(any_ref.downcast_ref::<OffsetBuffer>().is_some());
250    }
251
252    #[test]
253    fn test_offset_buffer_clone() {
254        let base = create_test_buffer(1024);
255        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
256        let cloned = offset_buf.clone();
257
258        assert_eq!(offset_buf.addr(), cloned.addr());
259        assert_eq!(offset_buf.size(), cloned.size());
260        assert_eq!(offset_buf.offset(), cloned.offset());
261    }
262
263    #[test]
264    fn test_offset_buffer_debug() {
265        let base = create_test_buffer(1024);
266        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
267        let debug_str = format!("{:?}", offset_buf);
268
269        assert!(debug_str.contains("OffsetBuffer"));
270        assert!(debug_str.contains("offset"));
271        assert!(debug_str.contains("size"));
272    }
273
274    #[test]
275    fn test_offset_buffer_nixl_descriptor_none() {
276        // SystemStorage doesn't have a NIXL descriptor
277        let base = create_test_buffer(1024);
278        let offset_buf = OffsetBuffer::new(base, 100, 200).expect("should succeed");
279
280        // Should return None since base has no NIXL descriptor
281        assert!(offset_buf.nixl_descriptor().is_none());
282    }
283}