1use crate::{
5 error::{Error, Result},
6 BufferIdentity, TensorMap, TensorMapTrait, TensorMemory, TensorTrait,
7};
8use log::trace;
9use num_traits::Num;
10use std::{
11 ffi::c_void,
12 fmt,
13 marker::PhantomData,
14 ops::{Deref, DerefMut},
15 ptr::NonNull,
16 sync::{
17 atomic::{AtomicBool, Ordering},
18 Arc, Mutex,
19 },
20};
21
22pub struct PboMapping {
25 pub ptr: *mut u8,
26 pub size: usize,
27}
28
29unsafe impl Send for PboMapping {}
34
35pub unsafe trait PboOps: Send + Sync {
49 fn map_buffer(&self, buffer_id: u32, size: usize) -> Result<PboMapping>;
52
53 fn unmap_buffer(&self, buffer_id: u32) -> Result<()>;
56
57 fn delete_buffer(&self, buffer_id: u32);
60}
61
62struct PboHandle {
64 ops: Arc<dyn PboOps>,
65 buffer_id: u32,
66 size: usize,
67 mapped: AtomicBool,
68}
69
70impl Drop for PboHandle {
71 fn drop(&mut self) {
72 self.ops.delete_buffer(self.buffer_id);
73 }
74}
75
76pub struct PboTensor<T>
78where
79 T: Num + Clone + fmt::Debug + Send + Sync,
80{
81 pub name: String,
82 pub shape: Vec<usize>,
83 handle: Arc<PboHandle>,
84 identity: BufferIdentity,
85 pub(crate) view_offset: usize,
90 _marker: PhantomData<T>,
91}
92
93impl<T> fmt::Debug for PboTensor<T>
94where
95 T: Num + Clone + fmt::Debug + Send + Sync,
96{
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.debug_struct("PboTensor")
99 .field("name", &self.name)
100 .field("shape", &self.shape)
101 .field("buffer_id", &self.handle.buffer_id)
102 .field("size", &self.handle.size)
103 .finish()
104 }
105}
106
107unsafe impl<T> Send for PboTensor<T> where T: Num + Clone + fmt::Debug + Send + Sync {}
108unsafe impl<T> Sync for PboTensor<T> where T: Num + Clone + fmt::Debug + Send + Sync {}
109
110impl<T> PboTensor<T>
111where
112 T: Num + Clone + fmt::Debug + Send + Sync,
113{
114 pub fn from_pbo(
125 buffer_id: u32,
126 size: usize,
127 shape: &[usize],
128 name: Option<&str>,
129 ops: Arc<dyn PboOps>,
130 ) -> Result<Self> {
131 if size == 0 {
132 return Err(Error::InvalidSize(0));
133 }
134 let expected = shape.iter().product::<usize>() * std::mem::size_of::<T>();
135 if size < expected {
139 return Err(Error::ShapeMismatch(format!(
140 "PBO size {size} is smaller than shape {shape:?} * sizeof({}) = {expected}",
141 std::any::type_name::<T>(),
142 )));
143 }
144 let name = name.unwrap_or("pbo_tensor").to_owned();
145 Ok(Self {
146 name,
147 shape: shape.to_vec(),
148 handle: Arc::new(PboHandle {
149 ops,
150 buffer_id,
151 size,
152 mapped: AtomicBool::new(false),
153 }),
154 identity: BufferIdentity::new(),
155 view_offset: 0,
156 _marker: PhantomData,
157 })
158 }
159
160 pub fn buffer_id(&self) -> u32 {
162 self.handle.buffer_id
163 }
164
165 pub fn is_mapped(&self) -> bool {
167 self.handle.mapped.load(Ordering::Acquire)
168 }
169}
170
171impl<T> TensorTrait<T> for PboTensor<T>
172where
173 T: Num + Clone + fmt::Debug + Send + Sync,
174{
175 fn new(_shape: &[usize], _name: Option<&str>) -> Result<Self> {
176 Err(Error::NotImplemented(
177 "PboTensor cannot be created directly — use ImageProcessor::create_image()".to_owned(),
178 ))
179 }
180
181 #[cfg(unix)]
182 fn from_fd(_fd: std::os::fd::OwnedFd, _shape: &[usize], _name: Option<&str>) -> Result<Self> {
183 Err(Error::NotImplemented(
184 "PboTensor does not support from_fd".to_owned(),
185 ))
186 }
187
188 #[cfg(unix)]
189 fn clone_fd(&self) -> Result<std::os::fd::OwnedFd> {
190 Err(Error::NotImplemented(
191 "PboTensor does not support clone_fd".to_owned(),
192 ))
193 }
194
195 fn memory(&self) -> TensorMemory {
196 TensorMemory::Pbo
197 }
198
199 fn name(&self) -> String {
200 self.name.clone()
201 }
202
203 fn shape(&self) -> &[usize] {
204 &self.shape
205 }
206
207 fn reshape(&mut self, shape: &[usize]) -> Result<()> {
208 if shape.is_empty() {
209 return Err(Error::InvalidSize(0));
210 }
211 let new_size = shape.iter().product::<usize>() * std::mem::size_of::<T>();
212 if new_size != self.handle.size {
213 return Err(Error::ShapeMismatch(format!(
214 "Cannot reshape incompatible shape: {:?} to {:?}",
215 self.shape, shape
216 )));
217 }
218 self.shape = shape.to_vec();
219 self.view_offset = 0;
220 Ok(())
221 }
222
223 fn capacity_bytes(&self) -> usize {
224 self.handle.size
225 }
226
227 fn set_logical_shape(&mut self, shape: &[usize]) -> Result<()> {
232 if shape.is_empty() {
233 return Err(Error::InvalidSize(0));
234 }
235 let needed = shape.iter().product::<usize>() * std::mem::size_of::<T>();
236 if needed > self.handle.size {
237 return Err(Error::InsufficientCapacity {
238 needed,
239 capacity: self.handle.size,
240 });
241 }
242 self.shape = shape.to_vec();
243 Ok(())
244 }
245
246 fn map_with(&self, access: crate::CpuAccess) -> Result<TensorMap<T>> {
247 self.map_internal(None, access)
248 }
249
250 fn buffer_identity(&self) -> &BufferIdentity {
251 &self.identity
252 }
253
254 fn view(&self, offset_bytes: usize, shape: &[usize]) -> Result<Self> {
266 if !offset_bytes.is_multiple_of(std::mem::align_of::<T>()) {
267 return Err(Error::InvalidOperation(format!(
268 "PboTensor::view: offset {offset_bytes} not aligned to align_of::<T>()={}",
269 std::mem::align_of::<T>()
270 )));
271 }
272 let abs_offset = self
273 .view_offset
274 .checked_add(offset_bytes)
275 .ok_or(Error::InvalidSize(offset_bytes))?;
276 let logical = shape.iter().product::<usize>() * std::mem::size_of::<T>();
277 let needed = abs_offset
278 .checked_add(logical)
279 .ok_or(Error::InvalidSize(logical))?;
280 if needed > self.handle.size {
281 return Err(Error::InsufficientCapacity {
282 needed,
283 capacity: self.handle.size,
284 });
285 }
286 Ok(Self {
287 name: self.name.clone(),
288 shape: shape.to_vec(),
289 handle: Arc::clone(&self.handle),
290 identity: self.identity.clone(),
291 view_offset: abs_offset,
292 _marker: PhantomData,
293 })
294 }
295}
296
297impl<T> PboTensor<T>
298where
299 T: Num + Clone + fmt::Debug + Send + Sync,
300{
301 pub(crate) fn map_with_byte_size(
308 &self,
309 byte_size: usize,
310 access: crate::CpuAccess,
311 ) -> Result<TensorMap<T>> {
312 self.map_internal(Some(byte_size), access)
313 }
314
315 fn map_internal(
316 &self,
317 byte_size_override: Option<usize>,
318 access: crate::CpuAccess,
319 ) -> Result<TensorMap<T>> {
320 if self.handle.mapped.swap(true, Ordering::AcqRel) {
321 return Err(Error::PboMapped);
322 }
323 match self
326 .handle
327 .ops
328 .map_buffer(self.handle.buffer_id, self.handle.size)
329 {
330 Ok(mapping) => {
331 let pbo_ptr = PboPtr(
332 NonNull::new(mapping.ptr as *mut c_void)
333 .ok_or(Error::InvalidSize(self.handle.size))?,
334 );
335 Ok(TensorMap::Pbo(PboMap {
336 ptr: Arc::new(Mutex::new(pbo_ptr)),
337 shape: self.shape.clone(),
338 handle: Arc::clone(&self.handle),
339 byte_size_override,
340 view_offset: self.view_offset,
341 writable: access.writes(),
342 _marker: PhantomData,
343 }))
344 }
345 Err(e) => {
346 self.handle.mapped.store(false, Ordering::Release);
347 Err(e)
348 }
349 }
350 }
351}
352
353#[derive(Debug)]
356struct PboPtr(NonNull<c_void>);
357
358impl Deref for PboPtr {
359 type Target = NonNull<c_void>;
360 fn deref(&self) -> &Self::Target {
361 &self.0
362 }
363}
364
365unsafe impl Send for PboPtr {}
366
367pub struct PboMap<T>
368where
369 T: Num + Clone + fmt::Debug,
370{
371 ptr: Arc<Mutex<PboPtr>>,
372 shape: Vec<usize>,
373 handle: Arc<PboHandle>,
374 byte_size_override: Option<usize>,
380 view_offset: usize,
383 writable: bool,
387 _marker: PhantomData<T>,
388}
389
390impl<T> fmt::Debug for PboMap<T>
391where
392 T: Num + Clone + fmt::Debug,
393{
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 f.debug_struct("PboMap")
396 .field("shape", &self.shape)
397 .field("buffer_id", &self.handle.buffer_id)
398 .finish()
399 }
400}
401
402impl<T> TensorMapTrait<T> for PboMap<T>
403where
404 T: Num + Clone + fmt::Debug,
405{
406 fn shape(&self) -> &[usize] {
407 &self.shape
408 }
409
410 fn unmap(&mut self) {
411 trace!("Unmapping PboMap buffer_id={}", self.handle.buffer_id);
412 if let Err(e) = self.handle.ops.unmap_buffer(self.handle.buffer_id) {
413 log::warn!("Failed to unmap PBO buffer {}: {e}", self.handle.buffer_id);
414 }
415 self.handle.mapped.store(false, Ordering::Release);
416 }
417
418 fn as_slice(&self) -> &[T] {
419 let ptr = self.ptr.lock().expect("Failed to lock PboMap pointer");
420 let base = unsafe { (ptr.as_ptr() as *const u8).add(self.view_offset) as *const T };
421 unsafe { std::slice::from_raw_parts(base, self.slice_len_elems()) }
422 }
423
424 fn as_mut_slice(&mut self) -> &mut [T] {
425 crate::assert_map_writable(self.writable, "Pbo");
426 let ptr = self.ptr.lock().expect("Failed to lock PboMap pointer");
427 let base = unsafe { (ptr.as_ptr() as *mut u8).add(self.view_offset) as *mut T };
428 unsafe { std::slice::from_raw_parts_mut(base, self.slice_len_elems()) }
429 }
430}
431
432impl<T> PboMap<T>
433where
434 T: Num + Clone + fmt::Debug,
435{
436 fn slice_len_elems(&self) -> usize {
440 match self.byte_size_override {
441 Some(bytes) => bytes / std::mem::size_of::<T>(),
442 None => self.shape.iter().product(),
443 }
444 }
445}
446
447impl<T> Deref for PboMap<T>
448where
449 T: Num + Clone + fmt::Debug,
450{
451 type Target = [T];
452 fn deref(&self) -> &[T] {
453 self.as_slice()
454 }
455}
456
457impl<T> DerefMut for PboMap<T>
458where
459 T: Num + Clone + fmt::Debug,
460{
461 fn deref_mut(&mut self) -> &mut [T] {
462 self.as_mut_slice()
463 }
464}
465
466impl<T> Drop for PboMap<T>
467where
468 T: Num + Clone + fmt::Debug,
469{
470 fn drop(&mut self) {
471 self.unmap();
472 }
473}
474
475impl<T> Clone for PboTensor<T>
476where
477 T: Num + Clone + fmt::Debug + Send + Sync,
478{
479 fn clone(&self) -> Self {
480 Self {
481 name: self.name.clone(),
482 shape: self.shape.clone(),
483 handle: Arc::clone(&self.handle),
484 identity: self.identity.clone(),
485 view_offset: self.view_offset,
486 _marker: PhantomData,
487 }
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 struct MockPboOps {
497 storage: Mutex<Vec<u8>>,
498 }
499
500 impl MockPboOps {
501 fn new(size: usize) -> Arc<Self> {
502 Arc::new(Self {
503 storage: Mutex::new(vec![0u8; size]),
504 })
505 }
506 }
507
508 unsafe impl PboOps for MockPboOps {
511 fn map_buffer(&self, _buffer_id: u32, size: usize) -> Result<PboMapping> {
512 let storage = self.storage.lock().expect("lock");
513 assert_eq!(storage.len(), size);
514 Ok(PboMapping {
515 ptr: storage.as_ptr() as *mut u8,
516 size,
517 })
518 }
519
520 fn unmap_buffer(&self, _buffer_id: u32) -> Result<()> {
521 Ok(())
522 }
523
524 fn delete_buffer(&self, _buffer_id: u32) {}
525 }
526
527 #[test]
528 fn test_pbo_tensor_create_and_metadata() {
529 let ops = MockPboOps::new(24);
530 let tensor = PboTensor::<u8>::from_pbo(42, 24, &[2, 3, 4], Some("test_pbo"), ops).unwrap();
531 assert_eq!(tensor.memory(), TensorMemory::Pbo);
532 assert_eq!(tensor.name(), "test_pbo");
533 assert_eq!(tensor.shape(), &[2, 3, 4]);
534 assert_eq!(tensor.buffer_id(), 42);
535 assert!(!tensor.is_mapped());
536 }
537
538 #[test]
539 fn test_pbo_tensor_map_write_read() {
540 let ops = MockPboOps::new(12);
541 let tensor = PboTensor::<u8>::from_pbo(1, 12, &[3, 4], Some("rw_test"), ops).unwrap();
542 {
543 let mut map = tensor.map().expect("map should succeed");
544 assert_eq!(map.shape(), &[3, 4]);
545 assert!(tensor.is_mapped());
546 map.as_mut_slice().fill(0xAB);
547 assert!(map.as_slice().iter().all(|&b| b == 0xAB));
548 }
549 assert!(!tensor.is_mapped());
550 }
551
552 #[test]
553 fn test_pbo_tensor_double_map_fails() {
554 let ops = MockPboOps::new(8);
555 let tensor = PboTensor::<u8>::from_pbo(2, 8, &[8], None, ops).unwrap();
556 let _map1 = tensor.map().expect("first map should succeed");
557 assert!(tensor.is_mapped());
558 let result = tensor.map();
559 assert!(result.is_err(), "second map while mapped should fail");
560 }
561
562 #[test]
563 fn test_pbo_tensor_reshape() {
564 let ops = MockPboOps::new(24);
565 let mut tensor = PboTensor::<u8>::from_pbo(3, 24, &[2, 3, 4], None, ops).unwrap();
566 tensor
567 .reshape(&[4, 6])
568 .expect("compatible reshape should succeed");
569 assert_eq!(tensor.shape(), &[4, 6]);
570 let result = tensor.reshape(&[100]);
571 assert!(result.is_err(), "incompatible reshape should fail");
572 }
573
574 #[test]
575 fn test_pbo_tensor_set_logical_shape_capacity_based() {
576 let ops = MockPboOps::new(24);
580 let mut tensor = PboTensor::<u8>::from_pbo(7, 24, &[24], None, ops).unwrap();
581 tensor
583 .set_logical_shape(&[4, 5])
584 .expect("shape within capacity should succeed");
585 assert_eq!(tensor.shape(), &[4, 5]);
586 tensor.set_logical_shape(&[24]).unwrap();
588 assert!(
590 tensor.set_logical_shape(&[5, 5]).is_err(),
591 "shape exceeding PBO capacity must be rejected"
592 );
593 }
594
595 #[test]
596 fn test_pbo_tensor_buffer_identity() {
597 let ops1 = MockPboOps::new(8);
598 let ops2 = MockPboOps::new(8);
599 let t1 = PboTensor::<u8>::from_pbo(1, 8, &[8], None, ops1).unwrap();
600 let t2 = PboTensor::<u8>::from_pbo(2, 8, &[8], None, ops2).unwrap();
601 assert_ne!(t1.buffer_identity().id(), t2.buffer_identity().id());
602 }
603
604 #[test]
605 fn test_pbo_tensor_new_returns_error() {
606 let result = PboTensor::<u8>::new(&[8], None);
607 assert!(result.is_err(), "PboTensor::new() should fail");
608 }
609
610 #[cfg(unix)]
611 #[test]
612 fn test_pbo_tensor_fd_ops_return_error() {
613 let ops = MockPboOps::new(8);
614 let tensor = PboTensor::<u8>::from_pbo(1, 8, &[8], None, ops).unwrap();
615 assert!(tensor.clone_fd().is_err());
616 }
617
618 #[test]
619 fn test_pbo_tensor_from_pbo_size_mismatch() {
620 let ops = MockPboOps::new(24);
621 let result = PboTensor::<u8>::from_pbo(1, 24, &[2, 3, 5], None, ops);
622 assert!(result.is_err(), "mismatched size/shape should fail");
623 }
624
625 #[test]
626 fn test_pbo_tensor_from_pbo_zero_size() {
627 let ops = MockPboOps::new(0);
628 let result = PboTensor::<u8>::from_pbo(1, 0, &[0], None, ops);
629 assert!(result.is_err(), "zero size should fail");
630 }
631
632 #[test]
633 fn test_pbo_via_tensor_enum() {
634 let ops = MockPboOps::new(12);
635 let pbo = PboTensor::<u8>::from_pbo(10, 12, &[3, 4], Some("enum_test"), ops).unwrap();
636 let tensor = crate::Tensor::wrap(crate::TensorStorage::Pbo(pbo));
637 assert_eq!(tensor.memory(), TensorMemory::Pbo);
638 assert_eq!(tensor.name(), "enum_test");
639 assert_eq!(tensor.shape(), &[3, 4]);
640 let mut map = tensor.map().expect("map via enum");
641 map.as_mut_slice().fill(42);
642 assert!(map.as_slice().iter().all(|&b| b == 42));
643 }
644}