ibverbs_rs/ibverbs/memory/
remote_memory_region.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
32pub struct RemoteMemoryRegion {
33 addr: u64,
34 length: usize,
35 rkey: u32,
36}
37
38impl RemoteMemoryRegion {
39 pub fn new(addr: u64, length: usize, rkey: u32) -> Self {
44 Self { addr, length, rkey }
45 }
46
47 pub fn address(&self) -> u64 {
49 self.addr
50 }
51
52 pub fn length(&self) -> usize {
57 self.length
58 }
59
60 pub fn rkey(&self) -> u32 {
62 self.rkey
63 }
64
65 pub fn sub_region(&self, offset: usize) -> Option<RemoteMemoryRegion> {
75 if offset > self.length {
76 return None;
77 }
78
79 Some(RemoteMemoryRegion {
80 addr: self.addr.checked_add(offset.try_into().ok()?)?,
81 length: self.length - offset,
82 rkey: self.rkey,
83 })
84 }
85
86 pub fn sub_region_unchecked(&self, offset: usize) -> RemoteMemoryRegion {
94 RemoteMemoryRegion {
95 addr: self.addr + offset as u64,
96 length: self.length - offset,
97 rkey: self.rkey,
98 }
99 }
100}
101
102#[macro_export]
128macro_rules! remote_array_field {
129 ($mr:expr, $T:ty, $index:expr) => {{
130 let type_size = std::mem::size_of::<$T>();
131 match ($index).checked_mul(type_size) {
132 Some(offset) => $mr.sub_region(offset),
133 None => None,
134 }
135 }};
136}
137
138#[macro_export]
143macro_rules! remote_array_field_unchecked {
144 ($mr:expr, $T:ty, $index:expr) => {{
145 let type_size = std::mem::size_of::<$T>();
146 let offset = $index * type_size;
147 $mr.sub_region_unchecked(offset)
148 }};
149}
150
151#[macro_export]
182macro_rules! remote_struct_field {
183 ($mr:expr, $Struct:ident :: $field:ident) => {{
184 let offset = std::mem::offset_of!($Struct, $field);
185 $mr.sub_region(offset)
186 }};
187}
188
189#[macro_export]
194macro_rules! remote_struct_field_unchecked {
195 ($mr:expr, $Struct:ident :: $field:ident) => {{
196 let offset = std::mem::offset_of!($Struct, $field);
197 $mr.sub_region_unchecked(offset)
198 }};
199}
200
201#[macro_export]
231macro_rules! remote_struct_array_field {
232 ($mr:expr, $Struct:ident, $index:expr, $field:ident) => {{
233 let struct_size = std::mem::size_of::<$Struct>();
234 let field_offset = std::mem::offset_of!($Struct, $field);
235 match ($index)
236 .checked_mul(struct_size)
237 .and_then(|o| o.checked_add(field_offset))
238 {
239 Some(total_offset) => $mr.sub_region(total_offset),
240 None => None,
241 }
242 }};
243}
244
245#[macro_export]
250macro_rules! remote_struct_array_field_unchecked {
251 ($mr:expr, $Struct:ident, $index:expr, $field:ident) => {{
252 let struct_size = std::mem::size_of::<$Struct>();
253 let field_offset = std::mem::offset_of!($Struct, $field);
254 let total_offset = ($index * struct_size) + field_offset;
255 $mr.sub_region_unchecked(total_offset)
256 }};
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn new_stores_fields() {
265 let rmr = RemoteMemoryRegion::new(0x1000, 4096, 0xABCD);
266 assert_eq!(rmr.address(), 0x1000);
267 assert_eq!(rmr.length(), 4096);
268 assert_eq!(rmr.rkey(), 0xABCD);
269 }
270
271 #[test]
272 fn zero_length_region() {
273 let rmr = RemoteMemoryRegion::new(0x2000, 0, 1);
274 assert_eq!(rmr.length(), 0);
275 }
276
277 #[test]
278 fn sub_region_at_zero_offset() {
279 let rmr = RemoteMemoryRegion::new(0x1000, 100, 42);
280 let sub = rmr.sub_region(0).unwrap();
281 assert_eq!(sub.address(), 0x1000);
282 assert_eq!(sub.length(), 100);
283 assert_eq!(sub.rkey(), 42);
284 }
285
286 #[test]
287 fn sub_region_at_middle() {
288 let rmr = RemoteMemoryRegion::new(0x1000, 100, 42);
289 let sub = rmr.sub_region(40).unwrap();
290 assert_eq!(sub.address(), 0x1028);
291 assert_eq!(sub.length(), 60);
292 }
293
294 #[test]
295 fn sub_region_at_exact_end() {
296 let rmr = RemoteMemoryRegion::new(0x1000, 100, 42);
297 let sub = rmr.sub_region(100).unwrap();
298 assert_eq!(sub.address(), 0x1064);
299 assert_eq!(sub.length(), 0);
300 }
301
302 #[test]
303 fn sub_region_beyond_end_returns_none() {
304 let rmr = RemoteMemoryRegion::new(0x1000, 100, 42);
305 assert!(rmr.sub_region(101).is_none());
306 }
307
308 #[test]
309 fn sub_region_address_overflow_returns_none() {
310 let rmr = RemoteMemoryRegion::new(u64::MAX, 100, 42);
311 assert!(rmr.sub_region(1).is_none());
313 }
314
315 #[test]
316 fn sub_region_large_offset_returns_none() {
317 let rmr = RemoteMemoryRegion::new(0x1000, usize::MAX, 42);
318 assert!(rmr.sub_region(usize::MAX).is_none());
320 }
321
322 #[test]
323 fn sub_region_unchecked_at_middle() {
324 let rmr = RemoteMemoryRegion::new(0x1000, 100, 42);
325 let sub = rmr.sub_region_unchecked(40);
326 assert_eq!(sub.address(), 0x1028);
327 assert_eq!(sub.length(), 60);
328 assert_eq!(sub.rkey(), 42);
329 }
330
331 #[test]
332 fn remote_array_field_index_zero() {
333 let rmr = RemoteMemoryRegion::new(0x1000, 80, 0xABCD);
334 let elem = remote_array_field!(rmr, u64, 0_usize).unwrap();
335 assert_eq!(elem.address(), 0x1000);
336 assert_eq!(elem.length(), 80);
337 }
338
339 #[test]
340 fn remote_array_field_index_mid() {
341 let rmr = RemoteMemoryRegion::new(0x1000, 80, 0xABCD);
342 let elem = remote_array_field!(rmr, u64, 4_usize).unwrap();
343 assert_eq!(elem.address(), 0x1020); assert_eq!(elem.length(), 80 - 32);
345 }
346
347 #[test]
348 fn remote_array_field_out_of_bounds() {
349 let rmr = RemoteMemoryRegion::new(0x1000, 16, 0xABCD);
350 assert!(remote_array_field!(rmr, u64, 3_usize).is_none());
352 }
353
354 #[test]
355 fn remote_array_field_index_overflow() {
356 let rmr = RemoteMemoryRegion::new(0x1000, 1024, 0xABCD);
357 assert!(remote_array_field!(rmr, u64, usize::MAX).is_none());
359 }
360
361 #[test]
362 fn remote_array_field_unchecked_index_mid() {
363 let rmr = RemoteMemoryRegion::new(0x1000, 80, 0xABCD);
364 let elem = remote_array_field_unchecked!(rmr, u64, 4_usize);
365 assert_eq!(elem.address(), 0x1020);
366 }
367
368 #[repr(C)]
369 struct TestPacket {
370 header: u32,
371 payload: [u8; 1024],
372 }
373
374 #[test]
375 fn remote_struct_field_header() {
376 let rmr = RemoteMemoryRegion::new(0x1000, 1028, 0xABCD);
377 let field = remote_struct_field!(rmr, TestPacket::header).unwrap();
378 assert_eq!(field.address(), 0x1000);
379 }
380
381 #[test]
382 fn remote_struct_field_payload() {
383 let rmr = RemoteMemoryRegion::new(0x1000, 1028, 0xABCD);
384 let field = remote_struct_field!(rmr, TestPacket::payload).unwrap();
385 assert_eq!(field.address(), 0x1004);
386 }
387
388 #[test]
389 fn remote_struct_field_out_of_bounds() {
390 let rmr = RemoteMemoryRegion::new(0x1000, 2, 0xABCD);
392 assert!(remote_struct_field!(rmr, TestPacket::payload).is_none());
393 }
394
395 #[test]
396 fn remote_struct_field_unchecked_payload() {
397 let rmr = RemoteMemoryRegion::new(0x1000, 1028, 0xABCD);
398 let field = remote_struct_field_unchecked!(rmr, TestPacket::payload);
399 assert_eq!(field.address(), 0x1004);
400 }
401
402 #[repr(C)]
403 struct TestNode {
404 id: u32,
405 data: u64,
406 }
407
408 #[test]
409 fn remote_struct_array_field_first_element() {
410 let rmr = RemoteMemoryRegion::new(0x1000, 240, 0xABCD);
411 let field = remote_struct_array_field!(rmr, TestNode, 0_usize, data).unwrap();
412 let expected_offset = std::mem::offset_of!(TestNode, data);
413 assert_eq!(field.address(), 0x1000 + expected_offset as u64);
414 }
415
416 #[test]
417 fn remote_struct_array_field_nth_element() {
418 let rmr = RemoteMemoryRegion::new(0x1000, 240, 0xABCD);
419 let field = remote_struct_array_field!(rmr, TestNode, 2_usize, data).unwrap();
420 let node_size = std::mem::size_of::<TestNode>();
421 let field_offset = std::mem::offset_of!(TestNode, data);
422 let expected = 0x1000u64 + (2 * node_size + field_offset) as u64;
423 assert_eq!(field.address(), expected);
424 }
425
426 #[test]
427 fn remote_struct_array_field_overflow() {
428 let rmr = RemoteMemoryRegion::new(0x1000, 240, 0xABCD);
429 assert!(remote_struct_array_field!(rmr, TestNode, usize::MAX, data).is_none());
430 }
431
432 #[test]
433 fn remote_struct_array_field_unchecked_nth_element() {
434 let rmr = RemoteMemoryRegion::new(0x1000, 240, 0xABCD);
435 let field = remote_struct_array_field_unchecked!(rmr, TestNode, 2_usize, data);
436 let node_size = std::mem::size_of::<TestNode>();
437 let field_offset = std::mem::offset_of!(TestNode, data);
438 let expected = 0x1000u64 + (2 * node_size + field_offset) as u64;
439 assert_eq!(field.address(), expected);
440 }
441
442 #[test]
443 fn serde_round_trip() {
444 let rmr = RemoteMemoryRegion::new(0xDEAD_BEEF, 9999, 0x42);
445 let json = serde_json::to_string(&rmr).unwrap();
446 let restored: RemoteMemoryRegion = serde_json::from_str(&json).unwrap();
447 assert_eq!(restored.address(), rmr.address());
448 assert_eq!(restored.length(), rmr.length());
449 assert_eq!(restored.rkey(), rmr.rkey());
450 }
451}