1#![cfg(any(not(Py_LIMITED_API), Py_3_11))]
2use crate::Bound;
22#[cfg(feature = "gil-refs")]
23use crate::PyNativeType;
24use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
25use std::marker::PhantomData;
26use std::os::raw;
27use std::pin::Pin;
28use std::{cell, mem, ptr, slice};
29use std::{ffi::CStr, fmt::Debug};
30
31#[repr(transparent)]
34pub struct PyBuffer<T>(Pin<Box<ffi::Py_buffer>>, PhantomData<T>);
35
36unsafe impl<T> Send for PyBuffer<T> {}
39unsafe impl<T> Sync for PyBuffer<T> {}
40
41impl<T> Debug for PyBuffer<T> {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("PyBuffer")
44 .field("buf", &self.0.buf)
45 .field("obj", &self.0.obj)
46 .field("len", &self.0.len)
47 .field("itemsize", &self.0.itemsize)
48 .field("readonly", &self.0.readonly)
49 .field("ndim", &self.0.ndim)
50 .field("format", &self.0.format)
51 .field("shape", &self.0.shape)
52 .field("strides", &self.0.strides)
53 .field("suboffsets", &self.0.suboffsets)
54 .field("internal", &self.0.internal)
55 .finish()
56 }
57}
58
59#[derive(Copy, Clone, Debug, Eq, PartialEq)]
61pub enum ElementType {
62 SignedInteger {
64 bytes: usize,
66 },
67 UnsignedInteger {
69 bytes: usize,
71 },
72 Bool,
74 Float {
76 bytes: usize,
78 },
79 Unknown,
81}
82
83impl ElementType {
84 pub fn from_format(format: &CStr) -> ElementType {
89 match format.to_bytes() {
90 [size] | [b'@', size] => native_element_type_from_type_char(*size),
91 [b'=' | b'<' | b'>' | b'!', size] => standard_element_type_from_type_char(*size),
92 _ => ElementType::Unknown,
93 }
94 }
95}
96
97fn native_element_type_from_type_char(type_char: u8) -> ElementType {
98 use self::ElementType::*;
99 match type_char {
100 b'c' => UnsignedInteger {
101 bytes: mem::size_of::<raw::c_char>(),
102 },
103 b'b' => SignedInteger {
104 bytes: mem::size_of::<raw::c_schar>(),
105 },
106 b'B' => UnsignedInteger {
107 bytes: mem::size_of::<raw::c_uchar>(),
108 },
109 b'?' => Bool,
110 b'h' => SignedInteger {
111 bytes: mem::size_of::<raw::c_short>(),
112 },
113 b'H' => UnsignedInteger {
114 bytes: mem::size_of::<raw::c_ushort>(),
115 },
116 b'i' => SignedInteger {
117 bytes: mem::size_of::<raw::c_int>(),
118 },
119 b'I' => UnsignedInteger {
120 bytes: mem::size_of::<raw::c_uint>(),
121 },
122 b'l' => SignedInteger {
123 bytes: mem::size_of::<raw::c_long>(),
124 },
125 b'L' => UnsignedInteger {
126 bytes: mem::size_of::<raw::c_ulong>(),
127 },
128 b'q' => SignedInteger {
129 bytes: mem::size_of::<raw::c_longlong>(),
130 },
131 b'Q' => UnsignedInteger {
132 bytes: mem::size_of::<raw::c_ulonglong>(),
133 },
134 b'n' => SignedInteger {
135 bytes: mem::size_of::<libc::ssize_t>(),
136 },
137 b'N' => UnsignedInteger {
138 bytes: mem::size_of::<libc::size_t>(),
139 },
140 b'e' => Float { bytes: 2 },
141 b'f' => Float { bytes: 4 },
142 b'd' => Float { bytes: 8 },
143 _ => Unknown,
144 }
145}
146
147fn standard_element_type_from_type_char(type_char: u8) -> ElementType {
148 use self::ElementType::*;
149 match type_char {
150 b'c' | b'B' => UnsignedInteger { bytes: 1 },
151 b'b' => SignedInteger { bytes: 1 },
152 b'?' => Bool,
153 b'h' => SignedInteger { bytes: 2 },
154 b'H' => UnsignedInteger { bytes: 2 },
155 b'i' | b'l' => SignedInteger { bytes: 4 },
156 b'I' | b'L' => UnsignedInteger { bytes: 4 },
157 b'q' => SignedInteger { bytes: 8 },
158 b'Q' => UnsignedInteger { bytes: 8 },
159 b'e' => Float { bytes: 2 },
160 b'f' => Float { bytes: 4 },
161 b'd' => Float { bytes: 8 },
162 _ => Unknown,
163 }
164}
165
166#[cfg(target_endian = "little")]
167fn is_matching_endian(c: u8) -> bool {
168 c == b'@' || c == b'=' || c == b'>'
169}
170
171#[cfg(target_endian = "big")]
172fn is_matching_endian(c: u8) -> bool {
173 c == b'@' || c == b'=' || c == b'>' || c == b'!'
174}
175
176pub unsafe trait Element: Copy {
182 fn is_compatible_format(format: &CStr) -> bool;
185}
186
187impl<'py, T: Element> FromPyObject<'py> for PyBuffer<T> {
188 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
189 Self::get_bound(obj)
190 }
191}
192
193impl<T: Element> PyBuffer<T> {
194 #[cfg(feature = "gil-refs")]
196 #[deprecated(
197 since = "0.21.0",
198 note = "`PyBuffer::get` will be replaced by `PyBuffer::get_bound` in a future PyO3 version"
199 )]
200 pub fn get(obj: &PyAny) -> PyResult<PyBuffer<T>> {
201 Self::get_bound(&obj.as_borrowed())
202 }
203
204 pub fn get_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
206 let mut buf = Box::new(mem::MaybeUninit::uninit());
208 let buf: Box<ffi::Py_buffer> = {
209 err::error_on_minusone(obj.py(), unsafe {
210 ffi::PyObject_GetBuffer(obj.as_ptr(), buf.as_mut_ptr(), ffi::PyBUF_FULL_RO)
211 })?;
212 unsafe { mem::transmute(buf) }
215 };
216 let buf = PyBuffer(Pin::from(buf), PhantomData);
219
220 if buf.0.shape.is_null() {
221 Err(PyBufferError::new_err("shape is null"))
222 } else if buf.0.strides.is_null() {
223 Err(PyBufferError::new_err("strides is null"))
224 } else if mem::size_of::<T>() != buf.item_size() || !T::is_compatible_format(buf.format()) {
225 Err(PyBufferError::new_err(format!(
226 "buffer contents are not compatible with {}",
227 std::any::type_name::<T>()
228 )))
229 } else if buf.0.buf.align_offset(mem::align_of::<T>()) != 0 {
230 Err(PyBufferError::new_err(format!(
231 "buffer contents are insufficiently aligned for {}",
232 std::any::type_name::<T>()
233 )))
234 } else {
235 Ok(buf)
236 }
237 }
238
239 #[inline]
244 pub fn buf_ptr(&self) -> *mut raw::c_void {
245 self.0.buf
246 }
247
248 pub fn get_ptr(&self, indices: &[usize]) -> *mut raw::c_void {
252 let shape = &self.shape()[..indices.len()];
253 for i in 0..indices.len() {
254 assert!(indices[i] < shape[i]);
255 }
256 unsafe {
257 ffi::PyBuffer_GetPointer(
258 #[cfg(Py_3_11)]
259 &*self.0,
260 #[cfg(not(Py_3_11))]
261 {
262 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
263 },
264 #[cfg(Py_3_11)]
265 {
266 indices.as_ptr().cast()
267 },
268 #[cfg(not(Py_3_11))]
269 {
270 indices.as_ptr() as *mut ffi::Py_ssize_t
271 },
272 )
273 }
274 }
275
276 #[inline]
278 pub fn readonly(&self) -> bool {
279 self.0.readonly != 0
280 }
281
282 #[inline]
285 pub fn item_size(&self) -> usize {
286 self.0.itemsize as usize
287 }
288
289 #[inline]
291 pub fn item_count(&self) -> usize {
292 (self.0.len as usize) / (self.0.itemsize as usize)
293 }
294
295 #[inline]
299 pub fn len_bytes(&self) -> usize {
300 self.0.len as usize
301 }
302
303 #[inline]
307 pub fn dimensions(&self) -> usize {
308 self.0.ndim as usize
309 }
310
311 #[inline]
319 pub fn shape(&self) -> &[usize] {
320 unsafe { slice::from_raw_parts(self.0.shape.cast(), self.0.ndim as usize) }
321 }
322
323 #[inline]
328 pub fn strides(&self) -> &[isize] {
329 unsafe { slice::from_raw_parts(self.0.strides, self.0.ndim as usize) }
330 }
331
332 #[inline]
338 pub fn suboffsets(&self) -> Option<&[isize]> {
339 unsafe {
340 if self.0.suboffsets.is_null() {
341 None
342 } else {
343 Some(slice::from_raw_parts(
344 self.0.suboffsets,
345 self.0.ndim as usize,
346 ))
347 }
348 }
349 }
350
351 #[inline]
353 pub fn format(&self) -> &CStr {
354 if self.0.format.is_null() {
355 ffi::c_str!("B")
356 } else {
357 unsafe { CStr::from_ptr(self.0.format) }
358 }
359 }
360
361 #[inline]
363 pub fn is_c_contiguous(&self) -> bool {
364 unsafe { ffi::PyBuffer_IsContiguous(&*self.0, b'C' as std::os::raw::c_char) != 0 }
365 }
366
367 #[inline]
369 pub fn is_fortran_contiguous(&self) -> bool {
370 unsafe { ffi::PyBuffer_IsContiguous(&*self.0, b'F' as std::os::raw::c_char) != 0 }
371 }
372
373 pub fn as_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
383 if self.is_c_contiguous() {
384 unsafe {
385 Some(slice::from_raw_parts(
386 self.0.buf as *mut ReadOnlyCell<T>,
387 self.item_count(),
388 ))
389 }
390 } else {
391 None
392 }
393 }
394
395 pub fn as_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
406 if !self.readonly() && self.is_c_contiguous() {
407 unsafe {
408 Some(slice::from_raw_parts(
409 self.0.buf as *mut cell::Cell<T>,
410 self.item_count(),
411 ))
412 }
413 } else {
414 None
415 }
416 }
417
418 pub fn as_fortran_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
428 if mem::size_of::<T>() == self.item_size() && self.is_fortran_contiguous() {
429 unsafe {
430 Some(slice::from_raw_parts(
431 self.0.buf as *mut ReadOnlyCell<T>,
432 self.item_count(),
433 ))
434 }
435 } else {
436 None
437 }
438 }
439
440 pub fn as_fortran_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
451 if !self.readonly() && self.is_fortran_contiguous() {
452 unsafe {
453 Some(slice::from_raw_parts(
454 self.0.buf as *mut cell::Cell<T>,
455 self.item_count(),
456 ))
457 }
458 } else {
459 None
460 }
461 }
462
463 pub fn copy_to_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
473 self._copy_to_slice(py, target, b'C')
474 }
475
476 pub fn copy_to_fortran_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
486 self._copy_to_slice(py, target, b'F')
487 }
488
489 fn _copy_to_slice(&self, py: Python<'_>, target: &mut [T], fort: u8) -> PyResult<()> {
490 if mem::size_of_val(target) != self.len_bytes() {
491 return Err(PyBufferError::new_err(format!(
492 "slice to copy to (of length {}) does not match buffer length of {}",
493 target.len(),
494 self.item_count()
495 )));
496 }
497
498 err::error_on_minusone(py, unsafe {
499 ffi::PyBuffer_ToContiguous(
500 target.as_mut_ptr().cast(),
501 #[cfg(Py_3_11)]
502 &*self.0,
503 #[cfg(not(Py_3_11))]
504 {
505 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
506 },
507 self.0.len,
508 fort as std::os::raw::c_char,
509 )
510 })
511 }
512
513 pub fn to_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
518 self._to_vec(py, b'C')
519 }
520
521 pub fn to_fortran_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
526 self._to_vec(py, b'F')
527 }
528
529 fn _to_vec(&self, py: Python<'_>, fort: u8) -> PyResult<Vec<T>> {
530 let item_count = self.item_count();
531 let mut vec: Vec<T> = Vec::with_capacity(item_count);
532
533 err::error_on_minusone(py, unsafe {
536 ffi::PyBuffer_ToContiguous(
537 vec.as_ptr() as *mut raw::c_void,
538 #[cfg(Py_3_11)]
539 &*self.0,
540 #[cfg(not(Py_3_11))]
541 {
542 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
543 },
544 self.0.len,
545 fort as std::os::raw::c_char,
546 )
547 })?;
548 unsafe { vec.set_len(item_count) };
550 Ok(vec)
551 }
552
553 pub fn copy_from_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
564 self._copy_from_slice(py, source, b'C')
565 }
566
567 pub fn copy_from_fortran_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
578 self._copy_from_slice(py, source, b'F')
579 }
580
581 fn _copy_from_slice(&self, py: Python<'_>, source: &[T], fort: u8) -> PyResult<()> {
582 if self.readonly() {
583 return Err(PyBufferError::new_err("cannot write to read-only buffer"));
584 } else if mem::size_of_val(source) != self.len_bytes() {
585 return Err(PyBufferError::new_err(format!(
586 "slice to copy from (of length {}) does not match buffer length of {}",
587 source.len(),
588 self.item_count()
589 )));
590 }
591
592 err::error_on_minusone(py, unsafe {
593 ffi::PyBuffer_FromContiguous(
594 #[cfg(Py_3_11)]
595 &*self.0,
596 #[cfg(not(Py_3_11))]
597 {
598 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
599 },
600 #[cfg(Py_3_11)]
601 {
602 source.as_ptr().cast()
603 },
604 #[cfg(not(Py_3_11))]
605 {
606 source.as_ptr() as *mut raw::c_void
607 },
608 self.0.len,
609 fort as std::os::raw::c_char,
610 )
611 })
612 }
613
614 pub fn release(self, _py: Python<'_>) {
619 let mut mdself = mem::ManuallyDrop::new(self);
623 unsafe {
624 ffi::PyBuffer_Release(&mut *mdself.0);
626
627 let inner: *mut Pin<Box<ffi::Py_buffer>> = &mut mdself.0;
630 ptr::drop_in_place(inner);
631 }
632 }
633}
634
635impl<T> Drop for PyBuffer<T> {
636 fn drop(&mut self) {
637 Python::with_gil(|_| unsafe { ffi::PyBuffer_Release(&mut *self.0) });
638 }
639}
640
641#[repr(transparent)]
647pub struct ReadOnlyCell<T: Element>(cell::UnsafeCell<T>);
648
649impl<T: Element> ReadOnlyCell<T> {
650 #[inline]
652 pub fn get(&self) -> T {
653 unsafe { *self.0.get() }
654 }
655
656 #[inline]
658 pub fn as_ptr(&self) -> *const T {
659 self.0.get()
660 }
661}
662
663macro_rules! impl_element(
664 ($t:ty, $f:ident) => {
665 unsafe impl Element for $t {
666 fn is_compatible_format(format: &CStr) -> bool {
667 let slice = format.to_bytes();
668 if slice.len() > 1 && !is_matching_endian(slice[0]) {
669 return false;
670 }
671 ElementType::from_format(format) == ElementType::$f { bytes: mem::size_of::<$t>() }
672 }
673 }
674 }
675);
676
677impl_element!(u8, UnsignedInteger);
678impl_element!(u16, UnsignedInteger);
679impl_element!(u32, UnsignedInteger);
680impl_element!(u64, UnsignedInteger);
681impl_element!(usize, UnsignedInteger);
682impl_element!(i8, SignedInteger);
683impl_element!(i16, SignedInteger);
684impl_element!(i32, SignedInteger);
685impl_element!(i64, SignedInteger);
686impl_element!(isize, SignedInteger);
687impl_element!(f32, Float);
688impl_element!(f64, Float);
689
690#[cfg(test)]
691mod tests {
692 use super::PyBuffer;
693 use crate::ffi;
694 use crate::types::any::PyAnyMethods;
695 use crate::Python;
696
697 #[test]
698 fn test_debug() {
699 Python::with_gil(|py| {
700 let bytes = py.eval_bound("b'abcde'", None, None).unwrap();
701 let buffer: PyBuffer<u8> = PyBuffer::get_bound(&bytes).unwrap();
702 let expected = format!(
703 concat!(
704 "PyBuffer {{ buf: {:?}, obj: {:?}, ",
705 "len: 5, itemsize: 1, readonly: 1, ",
706 "ndim: 1, format: {:?}, shape: {:?}, ",
707 "strides: {:?}, suboffsets: {:?}, internal: {:?} }}",
708 ),
709 buffer.0.buf,
710 buffer.0.obj,
711 buffer.0.format,
712 buffer.0.shape,
713 buffer.0.strides,
714 buffer.0.suboffsets,
715 buffer.0.internal
716 );
717 let debug_repr = format!("{:?}", buffer);
718 assert_eq!(debug_repr, expected);
719 });
720 }
721
722 #[test]
723 fn test_element_type_from_format() {
724 use super::ElementType;
725 use super::ElementType::*;
726 use std::mem::size_of;
727 use std::os::raw;
728
729 for (cstr, expected) in [
730 (
732 ffi::c_str!("@b"),
733 SignedInteger {
734 bytes: size_of::<raw::c_schar>(),
735 },
736 ),
737 (
738 ffi::c_str!("@c"),
739 UnsignedInteger {
740 bytes: size_of::<raw::c_char>(),
741 },
742 ),
743 (
744 ffi::c_str!("@b"),
745 SignedInteger {
746 bytes: size_of::<raw::c_schar>(),
747 },
748 ),
749 (
750 ffi::c_str!("@B"),
751 UnsignedInteger {
752 bytes: size_of::<raw::c_uchar>(),
753 },
754 ),
755 (ffi::c_str!("@?"), Bool),
756 (
757 ffi::c_str!("@h"),
758 SignedInteger {
759 bytes: size_of::<raw::c_short>(),
760 },
761 ),
762 (
763 ffi::c_str!("@H"),
764 UnsignedInteger {
765 bytes: size_of::<raw::c_ushort>(),
766 },
767 ),
768 (
769 ffi::c_str!("@i"),
770 SignedInteger {
771 bytes: size_of::<raw::c_int>(),
772 },
773 ),
774 (
775 ffi::c_str!("@I"),
776 UnsignedInteger {
777 bytes: size_of::<raw::c_uint>(),
778 },
779 ),
780 (
781 ffi::c_str!("@l"),
782 SignedInteger {
783 bytes: size_of::<raw::c_long>(),
784 },
785 ),
786 (
787 ffi::c_str!("@L"),
788 UnsignedInteger {
789 bytes: size_of::<raw::c_ulong>(),
790 },
791 ),
792 (
793 ffi::c_str!("@q"),
794 SignedInteger {
795 bytes: size_of::<raw::c_longlong>(),
796 },
797 ),
798 (
799 ffi::c_str!("@Q"),
800 UnsignedInteger {
801 bytes: size_of::<raw::c_ulonglong>(),
802 },
803 ),
804 (
805 ffi::c_str!("@n"),
806 SignedInteger {
807 bytes: size_of::<libc::ssize_t>(),
808 },
809 ),
810 (
811 ffi::c_str!("@N"),
812 UnsignedInteger {
813 bytes: size_of::<libc::size_t>(),
814 },
815 ),
816 (ffi::c_str!("@e"), Float { bytes: 2 }),
817 (ffi::c_str!("@f"), Float { bytes: 4 }),
818 (ffi::c_str!("@d"), Float { bytes: 8 }),
819 (ffi::c_str!("@z"), Unknown),
820 (ffi::c_str!("=b"), SignedInteger { bytes: 1 }),
822 (ffi::c_str!("=c"), UnsignedInteger { bytes: 1 }),
823 (ffi::c_str!("=B"), UnsignedInteger { bytes: 1 }),
824 (ffi::c_str!("=?"), Bool),
825 (ffi::c_str!("=h"), SignedInteger { bytes: 2 }),
826 (ffi::c_str!("=H"), UnsignedInteger { bytes: 2 }),
827 (ffi::c_str!("=l"), SignedInteger { bytes: 4 }),
828 (ffi::c_str!("=l"), SignedInteger { bytes: 4 }),
829 (ffi::c_str!("=I"), UnsignedInteger { bytes: 4 }),
830 (ffi::c_str!("=L"), UnsignedInteger { bytes: 4 }),
831 (ffi::c_str!("=q"), SignedInteger { bytes: 8 }),
832 (ffi::c_str!("=Q"), UnsignedInteger { bytes: 8 }),
833 (ffi::c_str!("=e"), Float { bytes: 2 }),
834 (ffi::c_str!("=f"), Float { bytes: 4 }),
835 (ffi::c_str!("=d"), Float { bytes: 8 }),
836 (ffi::c_str!("=z"), Unknown),
837 (ffi::c_str!("=0"), Unknown),
838 (ffi::c_str!(":b"), Unknown),
840 ] {
841 assert_eq!(
842 ElementType::from_format(cstr),
843 expected,
844 "element from format &Cstr: {:?}",
845 cstr,
846 );
847 }
848 }
849
850 #[test]
851 fn test_compatible_size() {
852 assert_eq!(
854 std::mem::size_of::<ffi::Py_ssize_t>(),
855 std::mem::size_of::<usize>()
856 );
857 }
858
859 #[test]
860 fn test_bytes_buffer() {
861 Python::with_gil(|py| {
862 let bytes = py.eval_bound("b'abcde'", None, None).unwrap();
863 let buffer = PyBuffer::get_bound(&bytes).unwrap();
864 assert_eq!(buffer.dimensions(), 1);
865 assert_eq!(buffer.item_count(), 5);
866 assert_eq!(buffer.format().to_str().unwrap(), "B");
867 assert_eq!(buffer.shape(), [5]);
868 assert!(buffer.is_c_contiguous());
870 assert!(buffer.is_fortran_contiguous());
871
872 let slice = buffer.as_slice(py).unwrap();
873 assert_eq!(slice.len(), 5);
874 assert_eq!(slice[0].get(), b'a');
875 assert_eq!(slice[2].get(), b'c');
876
877 assert_eq!(unsafe { *(buffer.get_ptr(&[1]) as *mut u8) }, b'b');
878
879 assert!(buffer.as_mut_slice(py).is_none());
880
881 assert!(buffer.copy_to_slice(py, &mut [0u8]).is_err());
882 let mut arr = [0; 5];
883 buffer.copy_to_slice(py, &mut arr).unwrap();
884 assert_eq!(arr, b"abcde" as &[u8]);
885
886 assert!(buffer.copy_from_slice(py, &[0u8; 5]).is_err());
887 assert_eq!(buffer.to_vec(py).unwrap(), b"abcde");
888 });
889 }
890
891 #[test]
892 fn test_array_buffer() {
893 Python::with_gil(|py| {
894 let array = py
895 .import_bound("array")
896 .unwrap()
897 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
898 .unwrap();
899 let buffer = PyBuffer::get_bound(&array).unwrap();
900 assert_eq!(buffer.dimensions(), 1);
901 assert_eq!(buffer.item_count(), 4);
902 assert_eq!(buffer.format().to_str().unwrap(), "f");
903 assert_eq!(buffer.shape(), [4]);
904
905 let slice = buffer.as_slice(py).unwrap();
911 assert_eq!(slice.len(), 4);
912 assert_eq!(slice[0].get(), 1.0);
913 assert_eq!(slice[3].get(), 2.5);
914
915 let mut_slice = buffer.as_mut_slice(py).unwrap();
916 assert_eq!(mut_slice.len(), 4);
917 assert_eq!(mut_slice[0].get(), 1.0);
918 mut_slice[3].set(2.75);
919 assert_eq!(slice[3].get(), 2.75);
920
921 buffer
922 .copy_from_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
923 .unwrap();
924 assert_eq!(slice[2].get(), 12.0);
925
926 assert_eq!(buffer.to_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
927
928 let buffer = PyBuffer::get_bound(&array).unwrap();
930 let slice = buffer.as_fortran_slice(py).unwrap();
931 assert_eq!(slice.len(), 4);
932 assert_eq!(slice[1].get(), 11.0);
933
934 let mut_slice = buffer.as_fortran_mut_slice(py).unwrap();
935 assert_eq!(mut_slice.len(), 4);
936 assert_eq!(mut_slice[2].get(), 12.0);
937 mut_slice[3].set(2.75);
938 assert_eq!(slice[3].get(), 2.75);
939
940 buffer
941 .copy_from_fortran_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
942 .unwrap();
943 assert_eq!(slice[2].get(), 12.0);
944
945 assert_eq!(buffer.to_fortran_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
946 });
947 }
948}