1#![cfg(any(not(Py_LIMITED_API), Py_3_11))]
2use crate::Bound;
22use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
23use std::ffi::{
24 c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
25 c_ushort, c_void,
26};
27use std::marker::PhantomData;
28use std::pin::Pin;
29use std::{cell, mem, ptr, slice};
30use std::{ffi::CStr, fmt::Debug};
31
32#[repr(transparent)]
35pub struct PyBuffer<T>(Pin<Box<ffi::Py_buffer>>, PhantomData<T>);
36
37unsafe impl<T> Send for PyBuffer<T> {}
40unsafe impl<T> Sync for PyBuffer<T> {}
41
42impl<T> Debug for PyBuffer<T> {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.debug_struct("PyBuffer")
45 .field("buf", &self.0.buf)
46 .field("obj", &self.0.obj)
47 .field("len", &self.0.len)
48 .field("itemsize", &self.0.itemsize)
49 .field("readonly", &self.0.readonly)
50 .field("ndim", &self.0.ndim)
51 .field("format", &self.0.format)
52 .field("shape", &self.0.shape)
53 .field("strides", &self.0.strides)
54 .field("suboffsets", &self.0.suboffsets)
55 .field("internal", &self.0.internal)
56 .finish()
57 }
58}
59
60#[derive(Copy, Clone, Debug, Eq, PartialEq)]
62pub enum ElementType {
63 SignedInteger {
65 bytes: usize,
67 },
68 UnsignedInteger {
70 bytes: usize,
72 },
73 Bool,
75 Float {
77 bytes: usize,
79 },
80 Unknown,
82}
83
84impl ElementType {
85 pub fn from_format(format: &CStr) -> ElementType {
90 match format.to_bytes() {
91 [size] | [b'@', size] => native_element_type_from_type_char(*size),
92 [b'=' | b'<' | b'>' | b'!', size] => standard_element_type_from_type_char(*size),
93 _ => ElementType::Unknown,
94 }
95 }
96}
97
98fn native_element_type_from_type_char(type_char: u8) -> ElementType {
99 use self::ElementType::*;
100 match type_char {
101 b'c' => UnsignedInteger {
102 bytes: mem::size_of::<c_char>(),
103 },
104 b'b' => SignedInteger {
105 bytes: mem::size_of::<c_schar>(),
106 },
107 b'B' => UnsignedInteger {
108 bytes: mem::size_of::<c_uchar>(),
109 },
110 b'?' => Bool,
111 b'h' => SignedInteger {
112 bytes: mem::size_of::<c_short>(),
113 },
114 b'H' => UnsignedInteger {
115 bytes: mem::size_of::<c_ushort>(),
116 },
117 b'i' => SignedInteger {
118 bytes: mem::size_of::<c_int>(),
119 },
120 b'I' => UnsignedInteger {
121 bytes: mem::size_of::<c_uint>(),
122 },
123 b'l' => SignedInteger {
124 bytes: mem::size_of::<c_long>(),
125 },
126 b'L' => UnsignedInteger {
127 bytes: mem::size_of::<c_ulong>(),
128 },
129 b'q' => SignedInteger {
130 bytes: mem::size_of::<c_longlong>(),
131 },
132 b'Q' => UnsignedInteger {
133 bytes: mem::size_of::<c_ulonglong>(),
134 },
135 b'n' => SignedInteger {
136 bytes: mem::size_of::<libc::ssize_t>(),
137 },
138 b'N' => UnsignedInteger {
139 bytes: mem::size_of::<libc::size_t>(),
140 },
141 b'e' => Float { bytes: 2 },
142 b'f' => Float { bytes: 4 },
143 b'd' => Float { bytes: 8 },
144 _ => Unknown,
145 }
146}
147
148fn standard_element_type_from_type_char(type_char: u8) -> ElementType {
149 use self::ElementType::*;
150 match type_char {
151 b'c' | b'B' => UnsignedInteger { bytes: 1 },
152 b'b' => SignedInteger { bytes: 1 },
153 b'?' => Bool,
154 b'h' => SignedInteger { bytes: 2 },
155 b'H' => UnsignedInteger { bytes: 2 },
156 b'i' | b'l' => SignedInteger { bytes: 4 },
157 b'I' | b'L' => UnsignedInteger { bytes: 4 },
158 b'q' => SignedInteger { bytes: 8 },
159 b'Q' => UnsignedInteger { bytes: 8 },
160 b'e' => Float { bytes: 2 },
161 b'f' => Float { bytes: 4 },
162 b'd' => Float { bytes: 8 },
163 _ => Unknown,
164 }
165}
166
167#[cfg(target_endian = "little")]
168fn is_matching_endian(c: u8) -> bool {
169 c == b'@' || c == b'=' || c == b'>'
170}
171
172#[cfg(target_endian = "big")]
173fn is_matching_endian(c: u8) -> bool {
174 c == b'@' || c == b'=' || c == b'>' || c == b'!'
175}
176
177pub unsafe trait Element: Copy {
183 fn is_compatible_format(format: &CStr) -> bool;
186}
187
188impl<T: Element> FromPyObject<'_> for PyBuffer<T> {
189 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
190 Self::get(obj)
191 }
192}
193
194impl<T: Element> PyBuffer<T> {
195 pub fn get(obj: &Bound<'_, PyAny>) -> PyResult<PyBuffer<T>> {
197 let mut buf = Box::new(mem::MaybeUninit::uninit());
199 let buf: Box<ffi::Py_buffer> = {
200 err::error_on_minusone(obj.py(), unsafe {
201 ffi::PyObject_GetBuffer(obj.as_ptr(), buf.as_mut_ptr(), ffi::PyBUF_FULL_RO)
202 })?;
203 unsafe { mem::transmute(buf) }
206 };
207 let buf = PyBuffer(Pin::from(buf), PhantomData);
210
211 if buf.0.shape.is_null() {
212 Err(PyBufferError::new_err("shape is null"))
213 } else if buf.0.strides.is_null() {
214 Err(PyBufferError::new_err("strides is null"))
215 } else if mem::size_of::<T>() != buf.item_size() || !T::is_compatible_format(buf.format()) {
216 Err(PyBufferError::new_err(format!(
217 "buffer contents are not compatible with {}",
218 std::any::type_name::<T>()
219 )))
220 } else if buf.0.buf.align_offset(mem::align_of::<T>()) != 0 {
221 Err(PyBufferError::new_err(format!(
222 "buffer contents are insufficiently aligned for {}",
223 std::any::type_name::<T>()
224 )))
225 } else {
226 Ok(buf)
227 }
228 }
229
230 #[inline]
240 pub fn buf_ptr(&self) -> *mut c_void {
241 self.0.buf
242 }
243
244 pub fn get_ptr(&self, indices: &[usize]) -> *mut c_void {
248 let shape = &self.shape()[..indices.len()];
249 for i in 0..indices.len() {
250 assert!(indices[i] < shape[i]);
251 }
252 unsafe {
253 ffi::PyBuffer_GetPointer(
254 #[cfg(Py_3_11)]
255 &*self.0,
256 #[cfg(not(Py_3_11))]
257 {
258 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
259 },
260 #[cfg(Py_3_11)]
261 {
262 indices.as_ptr().cast()
263 },
264 #[cfg(not(Py_3_11))]
265 {
266 indices.as_ptr() as *mut ffi::Py_ssize_t
267 },
268 )
269 }
270 }
271
272 #[inline]
274 pub fn readonly(&self) -> bool {
275 self.0.readonly != 0
276 }
277
278 #[inline]
281 pub fn item_size(&self) -> usize {
282 self.0.itemsize as usize
283 }
284
285 #[inline]
287 pub fn item_count(&self) -> usize {
288 (self.0.len as usize) / (self.0.itemsize as usize)
289 }
290
291 #[inline]
295 pub fn len_bytes(&self) -> usize {
296 self.0.len as usize
297 }
298
299 #[inline]
303 pub fn dimensions(&self) -> usize {
304 self.0.ndim as usize
305 }
306
307 #[inline]
315 pub fn shape(&self) -> &[usize] {
316 unsafe { slice::from_raw_parts(self.0.shape.cast(), self.0.ndim as usize) }
317 }
318
319 #[inline]
324 pub fn strides(&self) -> &[isize] {
325 unsafe { slice::from_raw_parts(self.0.strides, self.0.ndim as usize) }
326 }
327
328 #[inline]
334 pub fn suboffsets(&self) -> Option<&[isize]> {
335 unsafe {
336 if self.0.suboffsets.is_null() {
337 None
338 } else {
339 Some(slice::from_raw_parts(
340 self.0.suboffsets,
341 self.0.ndim as usize,
342 ))
343 }
344 }
345 }
346
347 #[inline]
349 pub fn format(&self) -> &CStr {
350 if self.0.format.is_null() {
351 ffi::c_str!("B")
352 } else {
353 unsafe { CStr::from_ptr(self.0.format) }
354 }
355 }
356
357 #[inline]
359 pub fn is_c_contiguous(&self) -> bool {
360 unsafe { ffi::PyBuffer_IsContiguous(&*self.0, b'C' as std::ffi::c_char) != 0 }
361 }
362
363 #[inline]
365 pub fn is_fortran_contiguous(&self) -> bool {
366 unsafe { ffi::PyBuffer_IsContiguous(&*self.0, b'F' as std::ffi::c_char) != 0 }
367 }
368
369 pub fn as_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
379 if self.is_c_contiguous() {
380 unsafe {
381 Some(slice::from_raw_parts(
382 self.0.buf as *mut ReadOnlyCell<T>,
383 self.item_count(),
384 ))
385 }
386 } else {
387 None
388 }
389 }
390
391 pub fn as_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
402 if !self.readonly() && self.is_c_contiguous() {
403 unsafe {
404 Some(slice::from_raw_parts(
405 self.0.buf as *mut cell::Cell<T>,
406 self.item_count(),
407 ))
408 }
409 } else {
410 None
411 }
412 }
413
414 pub fn as_fortran_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
424 if mem::size_of::<T>() == self.item_size() && self.is_fortran_contiguous() {
425 unsafe {
426 Some(slice::from_raw_parts(
427 self.0.buf as *mut ReadOnlyCell<T>,
428 self.item_count(),
429 ))
430 }
431 } else {
432 None
433 }
434 }
435
436 pub fn as_fortran_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
447 if !self.readonly() && self.is_fortran_contiguous() {
448 unsafe {
449 Some(slice::from_raw_parts(
450 self.0.buf as *mut cell::Cell<T>,
451 self.item_count(),
452 ))
453 }
454 } else {
455 None
456 }
457 }
458
459 pub fn copy_to_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
469 self._copy_to_slice(py, target, b'C')
470 }
471
472 pub fn copy_to_fortran_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
482 self._copy_to_slice(py, target, b'F')
483 }
484
485 fn _copy_to_slice(&self, py: Python<'_>, target: &mut [T], fort: u8) -> PyResult<()> {
486 if mem::size_of_val(target) != self.len_bytes() {
487 return Err(PyBufferError::new_err(format!(
488 "slice to copy to (of length {}) does not match buffer length of {}",
489 target.len(),
490 self.item_count()
491 )));
492 }
493
494 err::error_on_minusone(py, unsafe {
495 ffi::PyBuffer_ToContiguous(
496 target.as_mut_ptr().cast(),
497 #[cfg(Py_3_11)]
498 &*self.0,
499 #[cfg(not(Py_3_11))]
500 {
501 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
502 },
503 self.0.len,
504 fort as std::ffi::c_char,
505 )
506 })
507 }
508
509 pub fn to_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
514 self._to_vec(py, b'C')
515 }
516
517 pub fn to_fortran_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
522 self._to_vec(py, b'F')
523 }
524
525 fn _to_vec(&self, py: Python<'_>, fort: u8) -> PyResult<Vec<T>> {
526 let item_count = self.item_count();
527 let mut vec: Vec<T> = Vec::with_capacity(item_count);
528
529 err::error_on_minusone(py, unsafe {
532 ffi::PyBuffer_ToContiguous(
533 vec.as_ptr() as *mut c_void,
534 #[cfg(Py_3_11)]
535 &*self.0,
536 #[cfg(not(Py_3_11))]
537 {
538 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
539 },
540 self.0.len,
541 fort as std::ffi::c_char,
542 )
543 })?;
544 unsafe { vec.set_len(item_count) };
546 Ok(vec)
547 }
548
549 pub fn copy_from_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
560 self._copy_from_slice(py, source, b'C')
561 }
562
563 pub fn copy_from_fortran_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
574 self._copy_from_slice(py, source, b'F')
575 }
576
577 fn _copy_from_slice(&self, py: Python<'_>, source: &[T], fort: u8) -> PyResult<()> {
578 if self.readonly() {
579 return Err(PyBufferError::new_err("cannot write to read-only buffer"));
580 } else if mem::size_of_val(source) != self.len_bytes() {
581 return Err(PyBufferError::new_err(format!(
582 "slice to copy from (of length {}) does not match buffer length of {}",
583 source.len(),
584 self.item_count()
585 )));
586 }
587
588 err::error_on_minusone(py, unsafe {
589 ffi::PyBuffer_FromContiguous(
590 #[cfg(Py_3_11)]
591 &*self.0,
592 #[cfg(not(Py_3_11))]
593 {
594 &*self.0 as *const ffi::Py_buffer as *mut ffi::Py_buffer
595 },
596 #[cfg(Py_3_11)]
597 {
598 source.as_ptr().cast()
599 },
600 #[cfg(not(Py_3_11))]
601 {
602 source.as_ptr() as *mut c_void
603 },
604 self.0.len,
605 fort as std::ffi::c_char,
606 )
607 })
608 }
609
610 pub fn release(self, _py: Python<'_>) {
615 let mut mdself = mem::ManuallyDrop::new(self);
619 unsafe {
620 ffi::PyBuffer_Release(&mut *mdself.0);
622
623 let inner: *mut Pin<Box<ffi::Py_buffer>> = &mut mdself.0;
626 ptr::drop_in_place(inner);
627 }
628 }
629}
630
631impl<T> Drop for PyBuffer<T> {
632 fn drop(&mut self) {
633 fn inner(buf: &mut Pin<Box<ffi::Py_buffer>>) {
634 if Python::try_attach(|_| unsafe { ffi::PyBuffer_Release(&mut **buf) }).is_none()
635 && crate::internal::state::is_in_gc_traversal()
636 {
637 eprintln!("Warning: PyBuffer dropped while in GC traversal, this is a bug and will leak memory.");
638 }
639 }
644
645 inner(&mut self.0);
646 }
647}
648
649#[repr(transparent)]
655pub struct ReadOnlyCell<T: Element>(cell::UnsafeCell<T>);
656
657impl<T: Element> ReadOnlyCell<T> {
658 #[inline]
660 pub fn get(&self) -> T {
661 unsafe { *self.0.get() }
662 }
663
664 #[inline]
666 pub fn as_ptr(&self) -> *const T {
667 self.0.get()
668 }
669}
670
671macro_rules! impl_element(
672 ($t:ty, $f:ident) => {
673 unsafe impl Element for $t {
674 fn is_compatible_format(format: &CStr) -> bool {
675 let slice = format.to_bytes();
676 if slice.len() > 1 && !is_matching_endian(slice[0]) {
677 return false;
678 }
679 ElementType::from_format(format) == ElementType::$f { bytes: mem::size_of::<$t>() }
680 }
681 }
682 }
683);
684
685impl_element!(u8, UnsignedInteger);
686impl_element!(u16, UnsignedInteger);
687impl_element!(u32, UnsignedInteger);
688impl_element!(u64, UnsignedInteger);
689impl_element!(usize, UnsignedInteger);
690impl_element!(i8, SignedInteger);
691impl_element!(i16, SignedInteger);
692impl_element!(i32, SignedInteger);
693impl_element!(i64, SignedInteger);
694impl_element!(isize, SignedInteger);
695impl_element!(f32, Float);
696impl_element!(f64, Float);
697
698#[cfg(test)]
699mod tests {
700 use super::*;
701
702 use crate::ffi;
703 use crate::types::any::PyAnyMethods;
704 use crate::Python;
705
706 #[test]
707 fn test_debug() {
708 Python::attach(|py| {
709 let bytes = py.eval(ffi::c_str!("b'abcde'"), None, None).unwrap();
710 let buffer: PyBuffer<u8> = PyBuffer::get(&bytes).unwrap();
711 let expected = format!(
712 concat!(
713 "PyBuffer {{ buf: {:?}, obj: {:?}, ",
714 "len: 5, itemsize: 1, readonly: 1, ",
715 "ndim: 1, format: {:?}, shape: {:?}, ",
716 "strides: {:?}, suboffsets: {:?}, internal: {:?} }}",
717 ),
718 buffer.0.buf,
719 buffer.0.obj,
720 buffer.0.format,
721 buffer.0.shape,
722 buffer.0.strides,
723 buffer.0.suboffsets,
724 buffer.0.internal
725 );
726 let debug_repr = format!("{buffer:?}");
727 assert_eq!(debug_repr, expected);
728 });
729 }
730
731 #[test]
732 fn test_element_type_from_format() {
733 use super::ElementType::*;
734 use std::mem::size_of;
735
736 for (cstr, expected) in [
737 (
739 ffi::c_str!("@b"),
740 SignedInteger {
741 bytes: size_of::<c_schar>(),
742 },
743 ),
744 (
745 ffi::c_str!("@c"),
746 UnsignedInteger {
747 bytes: size_of::<c_char>(),
748 },
749 ),
750 (
751 ffi::c_str!("@b"),
752 SignedInteger {
753 bytes: size_of::<c_schar>(),
754 },
755 ),
756 (
757 ffi::c_str!("@B"),
758 UnsignedInteger {
759 bytes: size_of::<c_uchar>(),
760 },
761 ),
762 (ffi::c_str!("@?"), Bool),
763 (
764 ffi::c_str!("@h"),
765 SignedInteger {
766 bytes: size_of::<c_short>(),
767 },
768 ),
769 (
770 ffi::c_str!("@H"),
771 UnsignedInteger {
772 bytes: size_of::<c_ushort>(),
773 },
774 ),
775 (
776 ffi::c_str!("@i"),
777 SignedInteger {
778 bytes: size_of::<c_int>(),
779 },
780 ),
781 (
782 ffi::c_str!("@I"),
783 UnsignedInteger {
784 bytes: size_of::<c_uint>(),
785 },
786 ),
787 (
788 ffi::c_str!("@l"),
789 SignedInteger {
790 bytes: size_of::<c_long>(),
791 },
792 ),
793 (
794 ffi::c_str!("@L"),
795 UnsignedInteger {
796 bytes: size_of::<c_ulong>(),
797 },
798 ),
799 (
800 ffi::c_str!("@q"),
801 SignedInteger {
802 bytes: size_of::<c_longlong>(),
803 },
804 ),
805 (
806 ffi::c_str!("@Q"),
807 UnsignedInteger {
808 bytes: size_of::<c_ulonglong>(),
809 },
810 ),
811 (
812 ffi::c_str!("@n"),
813 SignedInteger {
814 bytes: size_of::<libc::ssize_t>(),
815 },
816 ),
817 (
818 ffi::c_str!("@N"),
819 UnsignedInteger {
820 bytes: size_of::<libc::size_t>(),
821 },
822 ),
823 (ffi::c_str!("@e"), Float { bytes: 2 }),
824 (ffi::c_str!("@f"), Float { bytes: 4 }),
825 (ffi::c_str!("@d"), Float { bytes: 8 }),
826 (ffi::c_str!("@z"), Unknown),
827 (ffi::c_str!("=b"), SignedInteger { bytes: 1 }),
829 (ffi::c_str!("=c"), UnsignedInteger { bytes: 1 }),
830 (ffi::c_str!("=B"), UnsignedInteger { bytes: 1 }),
831 (ffi::c_str!("=?"), Bool),
832 (ffi::c_str!("=h"), SignedInteger { bytes: 2 }),
833 (ffi::c_str!("=H"), UnsignedInteger { bytes: 2 }),
834 (ffi::c_str!("=l"), SignedInteger { bytes: 4 }),
835 (ffi::c_str!("=l"), SignedInteger { bytes: 4 }),
836 (ffi::c_str!("=I"), UnsignedInteger { bytes: 4 }),
837 (ffi::c_str!("=L"), UnsignedInteger { bytes: 4 }),
838 (ffi::c_str!("=q"), SignedInteger { bytes: 8 }),
839 (ffi::c_str!("=Q"), UnsignedInteger { bytes: 8 }),
840 (ffi::c_str!("=e"), Float { bytes: 2 }),
841 (ffi::c_str!("=f"), Float { bytes: 4 }),
842 (ffi::c_str!("=d"), Float { bytes: 8 }),
843 (ffi::c_str!("=z"), Unknown),
844 (ffi::c_str!("=0"), Unknown),
845 (ffi::c_str!(":b"), Unknown),
847 ] {
848 assert_eq!(
849 ElementType::from_format(cstr),
850 expected,
851 "element from format &Cstr: {cstr:?}",
852 );
853 }
854 }
855
856 #[test]
857 fn test_compatible_size() {
858 assert_eq!(
860 std::mem::size_of::<ffi::Py_ssize_t>(),
861 std::mem::size_of::<usize>()
862 );
863 }
864
865 #[test]
866 fn test_bytes_buffer() {
867 Python::attach(|py| {
868 let bytes = py.eval(ffi::c_str!("b'abcde'"), None, None).unwrap();
869 let buffer = PyBuffer::get(&bytes).unwrap();
870 assert_eq!(buffer.dimensions(), 1);
871 assert_eq!(buffer.item_count(), 5);
872 assert_eq!(buffer.format().to_str().unwrap(), "B");
873 assert_eq!(buffer.shape(), [5]);
874 assert!(buffer.is_c_contiguous());
876 assert!(buffer.is_fortran_contiguous());
877
878 let slice = buffer.as_slice(py).unwrap();
879 assert_eq!(slice.len(), 5);
880 assert_eq!(slice[0].get(), b'a');
881 assert_eq!(slice[2].get(), b'c');
882
883 assert_eq!(unsafe { *(buffer.get_ptr(&[1]) as *mut u8) }, b'b');
884
885 assert!(buffer.as_mut_slice(py).is_none());
886
887 assert!(buffer.copy_to_slice(py, &mut [0u8]).is_err());
888 let mut arr = [0; 5];
889 buffer.copy_to_slice(py, &mut arr).unwrap();
890 assert_eq!(arr, b"abcde" as &[u8]);
891
892 assert!(buffer.copy_from_slice(py, &[0u8; 5]).is_err());
893 assert_eq!(buffer.to_vec(py).unwrap(), b"abcde");
894 });
895 }
896
897 #[test]
898 fn test_array_buffer() {
899 Python::attach(|py| {
900 let array = py
901 .import("array")
902 .unwrap()
903 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
904 .unwrap();
905 let buffer = PyBuffer::get(&array).unwrap();
906 assert_eq!(buffer.dimensions(), 1);
907 assert_eq!(buffer.item_count(), 4);
908 assert_eq!(buffer.format().to_str().unwrap(), "f");
909 assert_eq!(buffer.shape(), [4]);
910
911 let slice = buffer.as_slice(py).unwrap();
917 assert_eq!(slice.len(), 4);
918 assert_eq!(slice[0].get(), 1.0);
919 assert_eq!(slice[3].get(), 2.5);
920
921 let mut_slice = buffer.as_mut_slice(py).unwrap();
922 assert_eq!(mut_slice.len(), 4);
923 assert_eq!(mut_slice[0].get(), 1.0);
924 mut_slice[3].set(2.75);
925 assert_eq!(slice[3].get(), 2.75);
926
927 buffer
928 .copy_from_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
929 .unwrap();
930 assert_eq!(slice[2].get(), 12.0);
931
932 assert_eq!(buffer.to_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
933
934 let buffer = PyBuffer::get(&array).unwrap();
936 let slice = buffer.as_fortran_slice(py).unwrap();
937 assert_eq!(slice.len(), 4);
938 assert_eq!(slice[1].get(), 11.0);
939
940 let mut_slice = buffer.as_fortran_mut_slice(py).unwrap();
941 assert_eq!(mut_slice.len(), 4);
942 assert_eq!(mut_slice[2].get(), 12.0);
943 mut_slice[3].set(2.75);
944 assert_eq!(slice[3].get(), 2.75);
945
946 buffer
947 .copy_from_fortran_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
948 .unwrap();
949 assert_eq!(slice[2].get(), 12.0);
950
951 assert_eq!(buffer.to_fortran_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
952 });
953 }
954}