custos/
buffer.rs

1use core::{ffi::c_void, mem::ManuallyDrop};
2
3#[cfg(feature = "cpu")]
4use crate::cpu::{CPUPtr, CPU};
5
6#[cfg(not(feature = "cpu"))]
7use crate::CPU;
8
9use crate::{
10    flag::AllocFlag, shape::Shape, Alloc, ClearBuf, CloneBuf, CommonPtrs, Device, DevicelessAble,
11    Ident, IsShapeIndep, MainMemory, PtrType, Read, ShallowCopy, WriteBuf,
12};
13
14pub use self::num::Num;
15pub use impl_from_const::*;
16
17mod impl_from;
18mod impl_from_const;
19mod num;
20
21/// The underlying non-growable array structure of `custos`. A `Buffer` may be encapsulated in other data structures.
22/// By default, the `Buffer` is a f32 CPU Buffer with no statically known shape.
23/// # Example
24#[cfg_attr(feature = "cpu", doc = "```")]
25#[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
26/// use custos::prelude::*;
27///
28/// fn buffer_f32_cpu(buf: &Buffer) {}
29/// fn buffer_generic<T, D: Device>(buf: &Buffer<T, D>) {}
30///
31/// let device = CPU::new();
32/// let buf = Buffer::from((&device, [0.5, 1.3, 3.2, 2.43]));
33///
34/// buffer_f32_cpu(&buf);
35/// buffer_generic(&buf);
36/// ```
37pub struct Buffer<'a, T = f32, D: Device = CPU, S: Shape = ()> {
38    /// the type of pointer
39    pub ptr: D::Ptr<T, S>,
40    /// A reference to the corresponding device. Mainly used for operations without a device parameter.
41    pub device: Option<&'a D>,
42    /// Used as a cache and autograd identifier.
43    #[cfg(not(feature = "no-std"))]
44    pub ident: Option<Ident>,
45}
46
47unsafe impl<'a, T, D: Device, S: Shape> Send for Buffer<'a, T, D, S> {}
48
49unsafe impl<'a, T, D: Device, S: Shape> Sync for Buffer<'a, T, D, S> {}
50
51impl<'a, T, D: Device, S: Shape> Buffer<'a, T, D, S> {
52    /// Creates a zeroed (or values set to default) `Buffer` with the given length on the specified device.
53    /// This `Buffer` can't outlive the device specified as a parameter.
54    #[cfg_attr(feature = "cpu", doc = "```")]
55    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
56    /// use custos::{CPU, Buffer};
57    ///
58    /// let device = CPU::new();
59    /// let mut buffer = Buffer::<i32>::new(&device, 6);
60    ///
61    /// // this only works with CPU or unified memory buffers (this creates a slice with the host pointer)
62    /// for value in &mut buffer {
63    ///     *value = 2;
64    /// }
65    ///
66    /// assert_eq!(buffer.as_slice(), &[2; 6]);
67    ///
68    /// ```
69    #[inline]
70    pub fn new(device: &'a D, len: usize) -> Buffer<'a, T, D, S>
71    where
72        D: Alloc<'a, T, S>, /*+ GraphReturn*/
73    {
74        let ptr = device.alloc(len, AllocFlag::None);
75        
76        #[cfg(not(feature = "no-std"))]
77        let ident = device.add_to_cache(&ptr);
78
79        Buffer {
80            ptr,
81            device: Some(device),
82            // TODO: enable, if leafs get more important
83            //node: device.graph().add_leaf(len),
84            #[cfg(not(feature = "no-std"))]
85            ident,
86        }
87    }
88
89    /// Buffers created with this method can outlive the device used to create this `Buffer`.<br>
90    /// No operations can be performed on this `Buffer` without a device parameter.
91    /// # Examples
92    #[cfg_attr(feature = "cpu", doc = "```")]
93    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
94    /// use custos::{CPU, Buffer};
95    ///
96    /// let mut buf = {
97    ///     let device = CPU::new();
98    ///     Buffer::<u8>::deviceless(&device, 5)
99    /// };
100    /// // buf.read(); // panics
101    /// for (idx, element) in buf.iter_mut().enumerate() {
102    ///     *element = idx as u8;
103    /// }
104    /// assert_eq!(buf.as_slice(), &[0, 1, 2, 3, 4]);
105    /// ```
106    #[inline]
107    pub fn deviceless<'b>(device: &'b D, len: usize) -> Buffer<'a, T, D, S>
108    where
109        D: DevicelessAble<'b, T, S>,
110    {
111        Buffer {
112            ptr: device.alloc(len, AllocFlag::None),
113            #[cfg(not(feature = "no-std"))]
114            ident: None,
115            device: None,
116        }
117    }
118
119    /// Returns the device of the `Buffer`.
120    /// Panic if the `Buffer` is deviceless.
121    pub fn device(&self) -> &'a D {
122        self.device
123            .expect("Called device() on a deviceless buffer.")
124    }
125
126    /// Reads the contents of the `Buffer`.
127    #[inline]
128    pub fn read(&'a self) -> D::Read<'a>
129    where
130        T: Clone + Default,
131        D: Read<T, S>,
132    {
133        self.device().read(self)
134    }
135
136    /// Reads the contents of the `Buffer` and writes them into a vector.
137    /// `.read` is more efficient, if the device uses host memory.
138    /// # Example
139    #[cfg_attr(feature = "cpu", doc = "```")]
140    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
141    /// use custos::{CPU, Buffer};
142    ///
143    /// let device = CPU::new();
144    /// let buf = Buffer::from((&device, [1, 2, 3, 4]));
145    ///
146    /// assert_eq!(buf.read_to_vec(), vec![1, 2, 3, 4]);
147    /// ```
148    #[inline]
149    #[cfg(not(feature = "no-std"))]
150    pub fn read_to_vec(&self) -> Vec<T>
151    where
152        D: Read<T, S>,
153        T: Default + Clone,
154    {
155        self.device().read_to_vec(self)
156    }
157
158    /// Writes a slice to the `Buffer`.
159    /// With a CPU buffer, the slice is just copied to the slice of the buffer.
160    ///
161    /// # Example
162    #[cfg_attr(feature = "cpu", doc = "```")]
163    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
164    /// use custos::{CPU, Buffer};
165    ///
166    /// let device = CPU::new();
167    /// let mut buf = Buffer::<i32>::new(&device, 6);
168    /// buf.write(&[4, 2, 3, 4, 5, 3]);
169    ///
170    /// assert_eq!(&*buf, [4, 2, 3, 4, 5, 3]);
171    /// ```
172    #[inline]
173    pub fn write(&mut self, data: &[T])
174    where
175        D: WriteBuf<T, S, D>,
176    {
177        self.device().write(self, data)
178    }
179
180    /// Writes the contents of the source buffer to self.
181    #[inline]
182    pub fn write_buf(&mut self, src: &Buffer<T, D, S>)
183    where
184        T: Clone,
185        D: WriteBuf<T, S, D>,
186    {
187        self.device().write_buf(self, src)
188    }
189
190    /// Returns the number of elements contained in `Buffer`.
191    /// # Example
192    #[cfg_attr(feature = "cpu", doc = "```")]
193    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
194    /// use custos::{CPU, Buffer};
195    ///
196    /// let device = CPU::new();
197    /// let a = Buffer::<i32, _>::new(&device, 10);
198    /// assert_eq!(a.len(), 10)
199    /// ```
200    #[inline]
201    pub fn len(&self) -> usize {
202        self.ptr.size()
203    }
204
205    /// Creates a shallow copy of &self.
206    ///
207    /// # Safety
208    /// Itself, this function does not need to be unsafe.
209    /// However, declaring this function as unsafe highlights the violation of creating two or more owners for one resource.
210    /// Furthermore, the resulting `Buffer` can outlive `self`.
211    #[inline]
212    pub unsafe fn shallow(&self) -> Buffer<'a, T, D, S>
213    where
214        <D as Device>::Ptr<T, S>: ShallowCopy,
215    {
216        Buffer {
217            ptr: self.ptr.shallow(),
218            device: self.device,
219            #[cfg(not(feature = "no-std"))]
220            ident: self.ident,
221        }
222    }
223
224    /// Returns a shallow copy of &self, if the `realloc` feature is deactivated.
225    /// If the `realloc` feature is activated, it returns a deep copy / clone.
226    ///
227    /// # Safety
228    /// Itself, this function does not need to be unsafe.
229    /// However, declaring this function as unsafe highlights the violation of possibly creating two or more owners for one resource.
230    /// Furthermore, the resulting `Buffer` can outlive `self`.
231    pub unsafe fn shallow_or_clone(&self) -> Buffer<'a, T, D, S>
232    where
233        <D as Device>::Ptr<T, S>: ShallowCopy,
234        T: Clone,
235        D: CloneBuf<'a, T, S>,
236    {
237        {
238            #[cfg(not(feature = "realloc"))]
239            self.shallow()
240        }
241
242        #[cfg(feature = "realloc")]
243        self.clone()
244    }
245
246    /// Returns the [`Ident`] of a `Buffer`.
247    /// A `Buffer` receives an id, if it is useable for caching, graph optimization or autograd.
248    /// Panics, if `Buffer` hasn't an id.
249    #[inline]
250    pub fn id(&self) -> Ident {
251        #[cfg(feature = "no-std")]
252        {
253            unimplemented!("This buffer has no trackable id. Who?: e.g. 'Stack' Buffer, Buffers created via Buffer::from_raw_host..(..), `Num` (scalar) Buffer")
254        }
255
256        #[cfg(not(feature = "no-std"))]
257        self.ident.expect("This buffer has no trackable id. Who?: e.g. 'Stack' Buffer, Buffers created via Buffer::from_raw_host..(..), `Num` (scalar) Buffer")
258    }
259
260    /// Sets all elements in `Buffer` to the default value.
261    pub fn clear(&mut self)
262    where
263        D: ClearBuf<T, S, D>,
264    {
265        self.device().clear(self)
266    }
267}
268
269impl<'a, T, D: Device, S: Shape> Drop for Buffer<'a, T, D, S> {
270    #[inline]
271    fn drop(&mut self) {
272        if self.ptr.flag() != AllocFlag::None {
273            return;
274        }
275
276        #[cfg(not(feature = "no-std"))]
277        if let Some(device) = self.device {
278            if let Some(ident) = self.ident {
279                device.remove(ident)
280            }
281        }
282    }
283}
284
285// TODO better solution for the to_dims stack problem?
286impl<'a, T, D: Device, S: Shape> Buffer<'a, T, D, S> {
287    /// Converts a non stack allocated `Buffer` with shape `S` to a `Buffer` with shape `O`.
288    /// # Example
289    #[cfg_attr(feature = "cpu", doc = "```")]
290    #[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
291    /// use custos::{CPU, Buffer, Shape, Dim1, Dim2};
292    ///
293    /// let device = CPU::new();
294    /// let a = Buffer::<i32, CPU, Dim1<10>>::new(&device, 10);
295    /// let _b = a.to_dims::<Dim2<5, 2>>();
296    ///
297    /// ```
298    #[inline]
299    pub fn to_dims<O: Shape>(self) -> Buffer<'a, T, D, O>
300    where
301        D: crate::ToDim<T, S, O>,
302        D::Ptr<T, S>: ShallowCopy,
303    {
304        let buf = ManuallyDrop::new(self);
305
306        let ptr = buf.device().to_dim(unsafe { buf.ptr.shallow() });
307
308        Buffer {
309            ptr,
310            device: buf.device,
311            #[cfg(not(feature = "no-std"))]
312            ident: buf.ident,
313        }
314    }
315}
316
317impl<'a, T, D: IsShapeIndep, S: Shape> Buffer<'a, T, D, S> {
318    /// Returns a reference of the same buffer, but with a different shape.
319    /// The Buffer is shape independet, so it can be converted to any shape.
320    #[inline]
321    pub fn as_dims<'b, O: Shape>(&self) -> &Buffer<'b, T, D, O> {
322        // Safety: shape independent buffers
323        // -> all dims have a size of 0
324        // -> all other buffer types do not depend on any features of the shape (S::ARR).
325        unsafe { &*(self as *const Self).cast() }
326    }
327
328    /// Returns a mutable reference of the same buffer, but with a different shape.
329    /// The Buffer is shape independet, so it can be converted to any shape.
330    #[inline]
331    pub fn as_dims_mut<'b, O: Shape>(&mut self) -> &mut Buffer<'b, T, D, O> {
332        unsafe { &mut *(self as *mut Self).cast() }
333    }
334}
335
336impl<'a, T, D: Device, S: Shape> Buffer<'a, T, D, S>
337where
338    D::Ptr<T, S>: CommonPtrs<T>,
339{
340    #[inline]
341    /// Returns all types of pointers. (host, OpenCL, CUDA)
342    pub fn ptrs(&self) -> (*const T, *mut c_void, u64) {
343        self.ptr.ptrs()
344    }
345
346    #[inline]
347    /// Returns all types of pointers. (host, OpenCL, CUDA)
348    pub fn ptrs_mut(&mut self) -> (*mut T, *mut c_void, u64) {
349        self.ptr.ptrs_mut()
350    }
351}
352
353impl<'a, T, D: Device> Buffer<'a, T, D> {
354    /// Returns `true` if `Buffer` is created without a slice.
355    /// # Example
356    /// ```
357    /// use custos::{CPU, Buffer};
358    ///
359    /// let a = Buffer::<i32, ()>::from(5);
360    /// assert!(a.is_empty())
361    /// ```
362    #[inline]
363    pub fn is_empty(&self) -> bool {
364        self.len() == 0
365    }
366}
367
368impl<'a, T, D: Device, S: Shape> Buffer<'a, T, D, S> {
369    /// Creates a new `Buffer` from a slice (&[T]).
370    /// The pointer of the allocation may be added to the cache of the device.
371    /// Usually, this pointer / `Buffer` is then returned by a `device.get_existing_buf(..)` (accesses the cache) call.
372    #[inline]
373    pub fn from_slice(device: &'a D, slice: &[T]) -> Self
374    where
375        T: Clone,
376        D: Alloc<'a, T, S>,
377    {
378        let ptr = device.with_slice(slice);
379
380        #[cfg(not(feature = "no-std"))]
381        let ident = device.add_to_cache(&ptr);
382
383        Buffer {
384            ptr,
385            #[cfg(not(feature = "no-std"))]
386            ident,
387            device: Some(device),
388        }
389    }
390
391    /// Creates a new `Buffer` from a `Vec`.
392    /// The pointer of the allocation may be added to the cache of the device.
393    /// Usually, this pointer / `Buffer` is then returned by a `device.get_existing_buf(..)` call.
394    #[cfg(not(feature = "no-std"))]
395    #[inline]
396    pub fn from_vec(device: &'a D, data: Vec<T>) -> Self
397    where
398        T: Clone,
399        D: Alloc<'a, T, S>,
400    {
401        let ptr = device.alloc_with_vec(data);
402        let ident = device.add_to_cache(&ptr);
403
404        Buffer {
405            ptr,
406            ident,
407            device: Some(device),
408        }
409    }
410
411    /// Creates a new `Buffer` from an nd-array.
412    /// The dimension is defined by the [`Shape`].
413    /// The pointer of the allocation may be added to the cache of the device.
414    /// Usually, this pointer / `Buffer` is then returned by a `device.get_existing_buf(..)` call.
415    #[inline]
416    pub fn from_array(device: &'a D, array: S::ARR<T>) -> Buffer<T, D, S>
417    where
418        T: Clone,
419        D: Alloc<'a, T, S>,
420    {
421        let ptr = device.with_array(array);
422        
423        #[cfg(not(feature = "no-std"))]
424        let ident = device.add_to_cache(&ptr);
425
426        Buffer {
427            ptr,
428            #[cfg(not(feature = "no-std"))]
429            ident,
430            device: Some(device),
431        }
432    }
433}
434
435#[cfg(feature = "cpu")]
436impl<'a, T, S: Shape> Buffer<'a, T, CPU, S> {
437    /// Constructs a deviceless `Buffer` out of a host pointer and a length.
438    /// # Example
439    /// ```
440    /// use custos::{Buffer, Alloc, CPU, Read, flag::AllocFlag};
441    /// use std::ffi::c_void;
442    ///
443    /// let device = CPU::new();
444    /// let mut ptr = Alloc::<f32>::alloc(&device, 10, AllocFlag::None);
445    /// let mut buf = unsafe {
446    ///     Buffer::<_, _, ()>::from_raw_host(ptr.ptr, 10)
447    /// };
448    /// for (idx, value) in buf.iter_mut().enumerate() {
449    ///     *value += idx as f32;
450    /// }
451    ///
452    /// assert_eq!(buf.as_slice(), &[0., 1., 2., 3., 4., 5., 6., 7., 8., 9.,]);
453    ///
454    /// ```
455    /// # Safety
456    /// The pointer must be valid.
457    /// The `Buffer` does not manage deallocation of the allocated memory.
458    #[inline]
459    pub unsafe fn from_raw_host(ptr: *mut T, len: usize) -> Buffer<'a, T, CPU, S> {
460        Buffer {
461            ptr: CPUPtr::from_ptr(ptr, len, AllocFlag::Wrapper),
462            device: None,
463            ident: None,
464        }
465    }
466
467    /// Constructs a `Buffer` out of a host pointer and a length.
468    /// The provided device can be used to shorten operation calls.
469    ///
470    /// # Safety
471    /// The pointer must be valid.
472    /// The `Buffer` does not manage deallocation of the allocated memory.
473    #[inline]
474    pub unsafe fn from_raw_host_device(
475        device: &'a CPU,
476        ptr: *mut T,
477        len: usize,
478    ) -> Buffer<'a, T, CPU, S> {
479        Buffer {
480            ptr: CPUPtr::from_ptr(ptr, len, AllocFlag::Wrapper),
481            device: Some(device),
482            ident: None,
483        }
484    }
485}
486
487#[cfg(feature = "opencl")]
488impl<'a, T, S: Shape> Buffer<'a, T, crate::OpenCL, S> {
489    /// Returns the OpenCL pointer of the `Buffer`.
490    #[inline]
491    pub fn cl_ptr(&self) -> *mut c_void {
492        assert!(
493            !self.ptr.ptr.is_null(),
494            "called cl_ptr() on an invalid OpenCL buffer"
495        );
496        self.ptrs().1
497    }
498}
499
500#[cfg(feature = "cuda")]
501impl<'a, T> Buffer<'a, T, crate::CUDA> {
502    // TODO: replace buf.ptr.2 with this fn, do the same with cl, cpu
503    /// Returns a non null CUDA pointer
504    #[inline]
505    pub fn cu_ptr(&self) -> u64 {
506        assert!(
507            self.ptrs().2 != 0,
508            "called cu_ptr() on an invalid CUDA buffer"
509        );
510        self.ptr.ptr
511    }
512}
513
514impl<'a, T, D: MainMemory, S: Shape> Buffer<'a, T, D, S> {
515    /// Returns a CPU slice. This does not work with CUDA or raw OpenCL buffers.
516    #[inline(always)]
517    pub fn as_slice(&self) -> &[T] {
518        self
519    }
520
521    /// Returns a mutable CPU slice.
522    #[inline(always)]
523    pub fn as_mut_slice(&mut self) -> &mut [T] {
524        self
525    }
526}
527
528impl<'a, T, D: MainMemory, S: Shape> Buffer<'a, T, D, S>
529where
530    D::Ptr<T, S>: CommonPtrs<T>,
531{
532    /// Returns a non null host pointer
533    #[inline]
534    pub fn host_ptr(&self) -> *const T {
535        assert!(
536            !self.ptrs().0.is_null(),
537            "called host_ptr() on an invalid CPU buffer (this would dereference a null pointer)"
538        );
539        self.ptrs().0
540    }
541
542    /// Returns a non null host pointer
543    #[inline]
544    pub fn host_ptr_mut(&mut self) -> *mut T {
545        assert!(
546            !self.ptrs().0.is_null(),
547            "called host_ptr_mut() on an invalid CPU buffer (this would dereference a null pointer)"
548        );
549        self.ptrs_mut().0
550    }
551}
552
553impl<'a, T, D, S> Clone for Buffer<'a, T, D, S>
554where
555    T: Clone,
556    D: CloneBuf<'a, T, S> + Device,
557    S: Shape,
558{
559    fn clone(&self) -> Self {
560        self.device().clone_buf(self)
561    }
562}
563
564/*#[cfg(feature = "safe")]
565unsafe impl<T> Send for Buffer<'a, T> {}
566#[cfg(feature = "safe")]
567unsafe impl<T> Sync for Buffer<'a, T> {}*/
568
569impl<'a, T, D: Device, S: Shape> Default for Buffer<'a, T, D, S>
570where
571    D::Ptr<T, S>: Default,
572{
573    fn default() -> Self {
574        Self {
575            ptr: D::Ptr::<T, S>::default(),
576            device: None,
577            #[cfg(not(feature = "no-std"))]
578            ident: None,
579        }
580    }
581}
582
583impl<T, D: MainMemory> AsRef<[T]> for Buffer<'_, T, D> {
584    #[inline]
585    fn as_ref(&self) -> &[T] {
586        self
587    }
588}
589
590impl<T, D: MainMemory> AsMut<[T]> for Buffer<'_, T, D> {
591    #[inline]
592    fn as_mut(&mut self) -> &mut [T] {
593        self
594    }
595}
596
597/// A `Buffer` dereferences into a slice.
598///
599/// # Examples
600///
601#[cfg_attr(feature = "cpu", doc = "```")]
602#[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
603/// use custos::{Buffer, CPU};
604///
605/// let device = CPU::new();
606///
607/// let a = Buffer::from((&device, [1., 2., 3., 4.,]));
608/// let b = Buffer::from((&device, [2., 3., 4., 5.,]));
609///
610/// let mut c = Buffer::from((&device, [0.; 4]));
611///
612/// let slice_add = |a: &[f64], b: &[f64], c: &mut [f64]| {
613///     for i in 0..c.len() {
614///         c[i] = a[i] + b[i];
615///     }
616/// };
617///
618/// slice_add(&a, &b, &mut c);
619/// assert_eq!(c.as_slice(), &[3., 5., 7., 9.,]);
620/// ```
621impl<T, D: MainMemory, S: Shape> core::ops::Deref for Buffer<'_, T, D, S> {
622    type Target = [T];
623
624    #[inline]
625    fn deref(&self) -> &Self::Target {
626        unsafe { core::slice::from_raw_parts(D::as_ptr(&self.ptr), self.len()) }
627    }
628}
629
630/// A `Buffer` dereferences into a slice.
631///
632/// # Examples
633///
634#[cfg_attr(feature = "cpu", doc = "```")]
635#[cfg_attr(not(feature = "cpu"), doc = "```ignore")]
636/// use custos::{Buffer, CPU};
637///  
638/// let device = CPU::new();
639///
640/// let a = Buffer::from((&device, [4., 2., 3., 4.,]));
641/// let b = Buffer::from((&device, [2., 3., 6., 5.,]));
642/// let mut c = Buffer::from((&device, [0.; 4]));
643///
644/// let slice_add = |a: &[f64], b: &[f64], c: &mut [f64]| {
645///     for i in 0..c.len() {
646///         c[i] = a[i] + b[i];
647///     }
648/// };
649/// slice_add(&a, &b, &mut c);
650/// assert_eq!(c.as_slice(), &[6., 5., 9., 9.,]);
651/// ```
652impl<T, D: MainMemory, S: Shape> core::ops::DerefMut for Buffer<'_, T, D, S> {
653    #[inline]
654    fn deref_mut(&mut self) -> &mut Self::Target {
655        unsafe { core::slice::from_raw_parts_mut(D::as_ptr_mut(&mut self.ptr), self.len()) }
656    }
657}
658
659#[cfg(not(feature = "no-std"))]
660use core::fmt::Debug;
661
662#[cfg(not(feature = "no-std"))]
663impl<'a, T, D> Debug for Buffer<'a, T, D>
664where
665    T: Debug + Default + Clone + 'a,
666    D: Read<T> + Device + 'a,
667    for<'b> <D as Read<T>>::Read<'b>: Debug,
668    D::Ptr<T, ()>: CommonPtrs<T>,
669{
670    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
671        f.debug_struct("Buffer")
672            .field("ptr (CPU, CL, CU)", &self.ptrs())
673            .field("len", &self.len());
674        writeln!(f, ",")?;
675
676        if !self.ptrs().0.is_null() {
677            let slice = unsafe { std::slice::from_raw_parts(self.ptrs().0, self.len()) };
678            writeln!(f, "CPU:    {slice:?}")?;
679        }
680
681        #[cfg(feature = "opencl")]
682        if !self.ptrs().1.is_null() {
683            write!(f, "OpenCL: {:?}, ", self.read())?;
684        }
685
686        #[cfg(feature = "cuda")]
687        if self.ptrs().2 != 0 {
688            write!(f, "CUDA: {:?}, ", self.read())?;
689        }
690
691        write!(
692            f,
693            "datatype={}, device={device} }}",
694            core::any::type_name::<T>(),
695            device = core::any::type_name::<D>()
696        )
697    }
698}
699
700impl<'a, T, D: MainMemory, S: Shape> core::iter::IntoIterator for &'a Buffer<'_, T, D, S> {
701    type Item = &'a T;
702
703    type IntoIter = core::slice::Iter<'a, T>;
704
705    #[inline]
706    fn into_iter(self) -> Self::IntoIter {
707        self.iter()
708    }
709}
710
711impl<'a, T, D: MainMemory, S: Shape> core::iter::IntoIterator for &'a mut Buffer<'_, T, D, S> {
712    type Item = &'a mut T;
713
714    type IntoIter = core::slice::IterMut<'a, T>;
715
716    #[inline]
717    fn into_iter(self) -> Self::IntoIter {
718        self.iter_mut()
719    }
720}
721
722#[cfg(test)]
723mod tests {
724    use crate::Buffer;
725
726    #[cfg(feature = "cpu")]
727    #[test]
728    fn test_deref() {
729        let device = crate::CPU::new();
730        let buf: Buffer<i32> = Buffer::from((&device, [1, 2, 3, 4]));
731        let slice = &*buf;
732        assert_eq!(slice, &[1, 2, 3, 4]);
733    }
734
735    #[cfg(feature = "opencl")]
736    #[cfg(unified_cl)]
737    #[test]
738    fn test_deref_cl() -> crate::Result<()> {
739        use crate::OpenCL;
740
741        let device = OpenCL::new(0)?;
742        let buf = Buffer::from((&device, [1, 2, 3, 4]));
743        let slice = &*buf;
744        assert_eq!(slice, &[1, 2, 3, 4]);
745
746        Ok(())
747    }
748
749    #[cfg(feature = "stack")]
750    #[test]
751    fn test_deref_stack() -> crate::Result<()> {
752        use crate::{shape::Dim1, stack::Stack};
753
754        //TODO
755        let buf = Buffer::<i32, _, Dim1<4>>::from((Stack, [1i32, 2, 3, 4]));
756        let slice = &*buf;
757        assert_eq!(slice, &[1, 2, 3, 4]);
758
759        Ok(())
760    }
761
762    #[cfg(feature = "cpu")]
763    #[test]
764    fn test_debug_print() {
765        let device = crate::CPU::new();
766        let buf = Buffer::from((&device, [1, 2, 3, 4, 5, 6]));
767
768        println!("{buf:?}",);
769    }
770
771    #[cfg(feature = "cpu")]
772    #[test]
773    fn test_to_dims() {
774        use crate::Dim2;
775
776        let device = crate::CPU::new();
777        let buf = Buffer::from((&device, [1, 2, 3, 4, 5, 6]));
778        let buf_dim2 = buf.to_dims::<Dim2<3, 2>>();
779
780        buf_dim2.to_dims::<()>();
781    }
782
783    #[cfg(feature = "cpu")]
784    #[test]
785    fn test_id_cpu() {
786        use crate::{Ident, CPU};
787
788        let device = CPU::new();
789
790        let buf = Buffer::from((&device, [1, 2, 3, 4]));
791        assert_eq!(buf.id(), Ident { idx: 0, len: 4 })
792    }
793
794    #[cfg(feature = "stack")]
795    #[cfg(not(feature = "no-std"))]
796    #[should_panic]
797    #[test]
798    fn test_id_stack() {
799        use crate::{Stack, WithShape};
800
801        let device = Stack;
802
803        let buf = Buffer::with(&device, [1, 2, 3, 4]);
804        buf.id();
805    }
806}