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(&self) -> Result<TensorMap<T>> {
247 self.map_internal(None)
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(&self, byte_size: usize) -> Result<TensorMap<T>> {
308 self.map_internal(Some(byte_size))
309 }
310
311 fn map_internal(&self, byte_size_override: Option<usize>) -> Result<TensorMap<T>> {
312 if self.handle.mapped.swap(true, Ordering::AcqRel) {
313 return Err(Error::PboMapped);
314 }
315 match self
318 .handle
319 .ops
320 .map_buffer(self.handle.buffer_id, self.handle.size)
321 {
322 Ok(mapping) => {
323 let pbo_ptr = PboPtr(
324 NonNull::new(mapping.ptr as *mut c_void)
325 .ok_or(Error::InvalidSize(self.handle.size))?,
326 );
327 Ok(TensorMap::Pbo(PboMap {
328 ptr: Arc::new(Mutex::new(pbo_ptr)),
329 shape: self.shape.clone(),
330 handle: Arc::clone(&self.handle),
331 byte_size_override,
332 view_offset: self.view_offset,
333 _marker: PhantomData,
334 }))
335 }
336 Err(e) => {
337 self.handle.mapped.store(false, Ordering::Release);
338 Err(e)
339 }
340 }
341 }
342}
343
344#[derive(Debug)]
347struct PboPtr(NonNull<c_void>);
348
349impl Deref for PboPtr {
350 type Target = NonNull<c_void>;
351 fn deref(&self) -> &Self::Target {
352 &self.0
353 }
354}
355
356unsafe impl Send for PboPtr {}
357
358pub struct PboMap<T>
359where
360 T: Num + Clone + fmt::Debug,
361{
362 ptr: Arc<Mutex<PboPtr>>,
363 shape: Vec<usize>,
364 handle: Arc<PboHandle>,
365 byte_size_override: Option<usize>,
371 view_offset: usize,
374 _marker: PhantomData<T>,
375}
376
377impl<T> fmt::Debug for PboMap<T>
378where
379 T: Num + Clone + fmt::Debug,
380{
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 f.debug_struct("PboMap")
383 .field("shape", &self.shape)
384 .field("buffer_id", &self.handle.buffer_id)
385 .finish()
386 }
387}
388
389impl<T> TensorMapTrait<T> for PboMap<T>
390where
391 T: Num + Clone + fmt::Debug,
392{
393 fn shape(&self) -> &[usize] {
394 &self.shape
395 }
396
397 fn unmap(&mut self) {
398 trace!("Unmapping PboMap buffer_id={}", self.handle.buffer_id);
399 if let Err(e) = self.handle.ops.unmap_buffer(self.handle.buffer_id) {
400 log::warn!("Failed to unmap PBO buffer {}: {e}", self.handle.buffer_id);
401 }
402 self.handle.mapped.store(false, Ordering::Release);
403 }
404
405 fn as_slice(&self) -> &[T] {
406 let ptr = self.ptr.lock().expect("Failed to lock PboMap pointer");
407 let base = unsafe { (ptr.as_ptr() as *const u8).add(self.view_offset) as *const T };
408 unsafe { std::slice::from_raw_parts(base, self.slice_len_elems()) }
409 }
410
411 fn as_mut_slice(&mut self) -> &mut [T] {
412 let ptr = self.ptr.lock().expect("Failed to lock PboMap pointer");
413 let base = unsafe { (ptr.as_ptr() as *mut u8).add(self.view_offset) as *mut T };
414 unsafe { std::slice::from_raw_parts_mut(base, self.slice_len_elems()) }
415 }
416}
417
418impl<T> PboMap<T>
419where
420 T: Num + Clone + fmt::Debug,
421{
422 fn slice_len_elems(&self) -> usize {
426 match self.byte_size_override {
427 Some(bytes) => bytes / std::mem::size_of::<T>(),
428 None => self.shape.iter().product(),
429 }
430 }
431}
432
433impl<T> Deref for PboMap<T>
434where
435 T: Num + Clone + fmt::Debug,
436{
437 type Target = [T];
438 fn deref(&self) -> &[T] {
439 self.as_slice()
440 }
441}
442
443impl<T> DerefMut for PboMap<T>
444where
445 T: Num + Clone + fmt::Debug,
446{
447 fn deref_mut(&mut self) -> &mut [T] {
448 self.as_mut_slice()
449 }
450}
451
452impl<T> Drop for PboMap<T>
453where
454 T: Num + Clone + fmt::Debug,
455{
456 fn drop(&mut self) {
457 self.unmap();
458 }
459}
460
461impl<T> Clone for PboTensor<T>
462where
463 T: Num + Clone + fmt::Debug + Send + Sync,
464{
465 fn clone(&self) -> Self {
466 Self {
467 name: self.name.clone(),
468 shape: self.shape.clone(),
469 handle: Arc::clone(&self.handle),
470 identity: self.identity.clone(),
471 view_offset: self.view_offset,
472 _marker: PhantomData,
473 }
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 struct MockPboOps {
483 storage: Mutex<Vec<u8>>,
484 }
485
486 impl MockPboOps {
487 fn new(size: usize) -> Arc<Self> {
488 Arc::new(Self {
489 storage: Mutex::new(vec![0u8; size]),
490 })
491 }
492 }
493
494 unsafe impl PboOps for MockPboOps {
497 fn map_buffer(&self, _buffer_id: u32, size: usize) -> Result<PboMapping> {
498 let storage = self.storage.lock().expect("lock");
499 assert_eq!(storage.len(), size);
500 Ok(PboMapping {
501 ptr: storage.as_ptr() as *mut u8,
502 size,
503 })
504 }
505
506 fn unmap_buffer(&self, _buffer_id: u32) -> Result<()> {
507 Ok(())
508 }
509
510 fn delete_buffer(&self, _buffer_id: u32) {}
511 }
512
513 #[test]
514 fn test_pbo_tensor_create_and_metadata() {
515 let ops = MockPboOps::new(24);
516 let tensor = PboTensor::<u8>::from_pbo(42, 24, &[2, 3, 4], Some("test_pbo"), ops).unwrap();
517 assert_eq!(tensor.memory(), TensorMemory::Pbo);
518 assert_eq!(tensor.name(), "test_pbo");
519 assert_eq!(tensor.shape(), &[2, 3, 4]);
520 assert_eq!(tensor.buffer_id(), 42);
521 assert!(!tensor.is_mapped());
522 }
523
524 #[test]
525 fn test_pbo_tensor_map_write_read() {
526 let ops = MockPboOps::new(12);
527 let tensor = PboTensor::<u8>::from_pbo(1, 12, &[3, 4], Some("rw_test"), ops).unwrap();
528 {
529 let mut map = tensor.map().expect("map should succeed");
530 assert_eq!(map.shape(), &[3, 4]);
531 assert!(tensor.is_mapped());
532 map.as_mut_slice().fill(0xAB);
533 assert!(map.as_slice().iter().all(|&b| b == 0xAB));
534 }
535 assert!(!tensor.is_mapped());
536 }
537
538 #[test]
539 fn test_pbo_tensor_double_map_fails() {
540 let ops = MockPboOps::new(8);
541 let tensor = PboTensor::<u8>::from_pbo(2, 8, &[8], None, ops).unwrap();
542 let _map1 = tensor.map().expect("first map should succeed");
543 assert!(tensor.is_mapped());
544 let result = tensor.map();
545 assert!(result.is_err(), "second map while mapped should fail");
546 }
547
548 #[test]
549 fn test_pbo_tensor_reshape() {
550 let ops = MockPboOps::new(24);
551 let mut tensor = PboTensor::<u8>::from_pbo(3, 24, &[2, 3, 4], None, ops).unwrap();
552 tensor
553 .reshape(&[4, 6])
554 .expect("compatible reshape should succeed");
555 assert_eq!(tensor.shape(), &[4, 6]);
556 let result = tensor.reshape(&[100]);
557 assert!(result.is_err(), "incompatible reshape should fail");
558 }
559
560 #[test]
561 fn test_pbo_tensor_set_logical_shape_capacity_based() {
562 let ops = MockPboOps::new(24);
566 let mut tensor = PboTensor::<u8>::from_pbo(7, 24, &[24], None, ops).unwrap();
567 tensor
569 .set_logical_shape(&[4, 5])
570 .expect("shape within capacity should succeed");
571 assert_eq!(tensor.shape(), &[4, 5]);
572 tensor.set_logical_shape(&[24]).unwrap();
574 assert!(
576 tensor.set_logical_shape(&[5, 5]).is_err(),
577 "shape exceeding PBO capacity must be rejected"
578 );
579 }
580
581 #[test]
582 fn test_pbo_tensor_buffer_identity() {
583 let ops1 = MockPboOps::new(8);
584 let ops2 = MockPboOps::new(8);
585 let t1 = PboTensor::<u8>::from_pbo(1, 8, &[8], None, ops1).unwrap();
586 let t2 = PboTensor::<u8>::from_pbo(2, 8, &[8], None, ops2).unwrap();
587 assert_ne!(t1.buffer_identity().id(), t2.buffer_identity().id());
588 }
589
590 #[test]
591 fn test_pbo_tensor_new_returns_error() {
592 let result = PboTensor::<u8>::new(&[8], None);
593 assert!(result.is_err(), "PboTensor::new() should fail");
594 }
595
596 #[cfg(unix)]
597 #[test]
598 fn test_pbo_tensor_fd_ops_return_error() {
599 let ops = MockPboOps::new(8);
600 let tensor = PboTensor::<u8>::from_pbo(1, 8, &[8], None, ops).unwrap();
601 assert!(tensor.clone_fd().is_err());
602 }
603
604 #[test]
605 fn test_pbo_tensor_from_pbo_size_mismatch() {
606 let ops = MockPboOps::new(24);
607 let result = PboTensor::<u8>::from_pbo(1, 24, &[2, 3, 5], None, ops);
608 assert!(result.is_err(), "mismatched size/shape should fail");
609 }
610
611 #[test]
612 fn test_pbo_tensor_from_pbo_zero_size() {
613 let ops = MockPboOps::new(0);
614 let result = PboTensor::<u8>::from_pbo(1, 0, &[0], None, ops);
615 assert!(result.is_err(), "zero size should fail");
616 }
617
618 #[test]
619 fn test_pbo_via_tensor_enum() {
620 let ops = MockPboOps::new(12);
621 let pbo = PboTensor::<u8>::from_pbo(10, 12, &[3, 4], Some("enum_test"), ops).unwrap();
622 let tensor = crate::Tensor::wrap(crate::TensorStorage::Pbo(pbo));
623 assert_eq!(tensor.memory(), TensorMemory::Pbo);
624 assert_eq!(tensor.name(), "enum_test");
625 assert_eq!(tensor.shape(), &[3, 4]);
626 let mut map = tensor.map().expect("map via enum");
627 map.as_mut_slice().fill(42);
628 assert!(map.as_slice().iter().all(|&b| b == 42));
629 }
630}