1#![cfg(any(not(Py_LIMITED_API), Py_3_11))]
2#[cfg(feature = "experimental-inspect")]
22use crate::inspect::{type_hint_identifier, PyStaticExpr};
23use crate::{err, exceptions::PyBufferError, ffi, FromPyObject, PyAny, PyResult, Python};
24use crate::{Borrowed, Bound, PyErr};
25use std::ffi::{
26 c_char, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong, c_ulonglong,
27 c_ushort, c_void,
28};
29use std::marker::{PhantomData, PhantomPinned};
30use std::pin::Pin;
31use std::ptr::NonNull;
32use std::{cell, mem, ptr, slice};
33use std::{ffi::CStr, fmt::Debug};
34
35#[repr(transparent)]
37pub struct PyBuffer<T>(PyUntypedBuffer, PhantomData<[T]>);
38
39#[repr(transparent)]
41pub struct PyUntypedBuffer(
42 Pin<Box<RawBuffer>>,
48);
49
50#[repr(transparent)]
52struct RawBuffer(ffi::Py_buffer, PhantomPinned);
53
54unsafe impl Send for PyUntypedBuffer {}
56unsafe impl Sync for PyUntypedBuffer {}
57
58impl<T> Debug for PyBuffer<T> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 debug_buffer("PyBuffer", &self.0, f)
61 }
62}
63
64impl Debug for PyUntypedBuffer {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 debug_buffer("PyUntypedBuffer", self, f)
67 }
68}
69
70fn debug_buffer(
71 name: &str,
72 b: &PyUntypedBuffer,
73 f: &mut std::fmt::Formatter<'_>,
74) -> std::fmt::Result {
75 let raw = b.raw();
76 f.debug_struct(name)
77 .field("buf", &raw.buf)
78 .field("obj", &raw.obj)
79 .field("len", &raw.len)
80 .field("itemsize", &raw.itemsize)
81 .field("readonly", &raw.readonly)
82 .field("ndim", &raw.ndim)
83 .field("format", &b.format())
84 .field("shape", &b.shape())
85 .field("strides", &b.strides())
86 .field("suboffsets", &b.suboffsets())
87 .field("internal", &raw.internal)
88 .finish()
89}
90
91#[derive(Copy, Clone, Debug, Eq, PartialEq)]
93pub enum ElementType {
94 SignedInteger {
96 bytes: usize,
98 },
99 UnsignedInteger {
101 bytes: usize,
103 },
104 Bool,
106 Float {
108 bytes: usize,
110 },
111 Unknown,
113}
114
115impl ElementType {
116 pub fn from_format(format: &CStr) -> ElementType {
121 match format.to_bytes() {
122 [size] | [b'@', size] => native_element_type_from_type_char(*size),
123 [b'=' | b'<' | b'>' | b'!', size] => standard_element_type_from_type_char(*size),
124 _ => ElementType::Unknown,
125 }
126 }
127}
128
129fn native_element_type_from_type_char(type_char: u8) -> ElementType {
130 use self::ElementType::*;
131 match type_char {
132 b'c' => UnsignedInteger {
133 bytes: mem::size_of::<c_char>(),
134 },
135 b'b' => SignedInteger {
136 bytes: mem::size_of::<c_schar>(),
137 },
138 b'B' => UnsignedInteger {
139 bytes: mem::size_of::<c_uchar>(),
140 },
141 b'?' => Bool,
142 b'h' => SignedInteger {
143 bytes: mem::size_of::<c_short>(),
144 },
145 b'H' => UnsignedInteger {
146 bytes: mem::size_of::<c_ushort>(),
147 },
148 b'i' => SignedInteger {
149 bytes: mem::size_of::<c_int>(),
150 },
151 b'I' => UnsignedInteger {
152 bytes: mem::size_of::<c_uint>(),
153 },
154 b'l' => SignedInteger {
155 bytes: mem::size_of::<c_long>(),
156 },
157 b'L' => UnsignedInteger {
158 bytes: mem::size_of::<c_ulong>(),
159 },
160 b'q' => SignedInteger {
161 bytes: mem::size_of::<c_longlong>(),
162 },
163 b'Q' => UnsignedInteger {
164 bytes: mem::size_of::<c_ulonglong>(),
165 },
166 b'n' => SignedInteger {
167 bytes: mem::size_of::<libc::ssize_t>(),
168 },
169 b'N' => UnsignedInteger {
170 bytes: mem::size_of::<libc::size_t>(),
171 },
172 b'e' => Float { bytes: 2 },
173 b'f' => Float { bytes: 4 },
174 b'd' => Float { bytes: 8 },
175 _ => Unknown,
176 }
177}
178
179fn standard_element_type_from_type_char(type_char: u8) -> ElementType {
180 use self::ElementType::*;
181 match type_char {
182 b'c' | b'B' => UnsignedInteger { bytes: 1 },
183 b'b' => SignedInteger { bytes: 1 },
184 b'?' => Bool,
185 b'h' => SignedInteger { bytes: 2 },
186 b'H' => UnsignedInteger { bytes: 2 },
187 b'i' | b'l' => SignedInteger { bytes: 4 },
188 b'I' | b'L' => UnsignedInteger { bytes: 4 },
189 b'q' => SignedInteger { bytes: 8 },
190 b'Q' => UnsignedInteger { bytes: 8 },
191 b'e' => Float { bytes: 2 },
192 b'f' => Float { bytes: 4 },
193 b'd' => Float { bytes: 8 },
194 _ => Unknown,
195 }
196}
197
198#[cfg(target_endian = "little")]
199fn is_matching_endian(c: u8) -> bool {
200 c == b'@' || c == b'=' || c == b'>'
201}
202
203#[cfg(target_endian = "big")]
204fn is_matching_endian(c: u8) -> bool {
205 c == b'@' || c == b'=' || c == b'>' || c == b'!'
206}
207
208pub unsafe trait Element: Copy {
214 fn is_compatible_format(format: &CStr) -> bool;
217}
218
219impl<T: Element> FromPyObject<'_, '_> for PyBuffer<T> {
220 type Error = PyErr;
221
222 #[cfg(feature = "experimental-inspect")]
223 const INPUT_TYPE: PyStaticExpr = type_hint_identifier!("collections.abc", "Buffer");
224
225 fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<PyBuffer<T>, Self::Error> {
226 Self::get(&obj)
227 }
228}
229
230impl<T: Element> PyBuffer<T> {
231 pub fn get(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
233 PyUntypedBuffer::get(obj)?.into_typed()
234 }
235
236 pub fn as_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
246 if self.is_c_contiguous() {
247 unsafe {
248 Some(slice::from_raw_parts(
249 self.raw().buf.cast(),
250 self.item_count(),
251 ))
252 }
253 } else {
254 None
255 }
256 }
257
258 pub fn as_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
269 if !self.readonly() && self.is_c_contiguous() {
270 unsafe {
271 Some(slice::from_raw_parts(
272 self.raw().buf.cast(),
273 self.item_count(),
274 ))
275 }
276 } else {
277 None
278 }
279 }
280
281 pub fn as_fortran_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
291 if mem::size_of::<T>() == self.item_size() && self.is_fortran_contiguous() {
292 unsafe {
293 Some(slice::from_raw_parts(
294 self.raw().buf.cast(),
295 self.item_count(),
296 ))
297 }
298 } else {
299 None
300 }
301 }
302
303 pub fn as_fortran_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
314 if !self.readonly() && self.is_fortran_contiguous() {
315 unsafe {
316 Some(slice::from_raw_parts(
317 self.raw().buf.cast(),
318 self.item_count(),
319 ))
320 }
321 } else {
322 None
323 }
324 }
325
326 pub fn copy_to_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
336 self._copy_to_slice(py, target, b'C')
337 }
338
339 pub fn copy_to_fortran_slice(&self, py: Python<'_>, target: &mut [T]) -> PyResult<()> {
349 self._copy_to_slice(py, target, b'F')
350 }
351
352 fn _copy_to_slice(&self, py: Python<'_>, target: &mut [T], fort: u8) -> PyResult<()> {
353 if mem::size_of_val(target) != self.len_bytes() {
354 return Err(PyBufferError::new_err(format!(
355 "slice to copy to (of length {}) does not match buffer length of {}",
356 target.len(),
357 self.item_count()
358 )));
359 }
360
361 err::error_on_minusone(py, unsafe {
362 ffi::PyBuffer_ToContiguous(
363 target.as_mut_ptr().cast(),
364 #[cfg(Py_3_11)]
365 self.raw(),
366 #[cfg(not(Py_3_11))]
367 ptr::from_ref(self.raw()).cast_mut(),
368 self.raw().len,
369 fort as std::ffi::c_char,
370 )
371 })
372 }
373
374 pub fn to_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
379 self._to_vec(py, b'C')
380 }
381
382 pub fn to_fortran_vec(&self, py: Python<'_>) -> PyResult<Vec<T>> {
387 self._to_vec(py, b'F')
388 }
389
390 fn _to_vec(&self, py: Python<'_>, fort: u8) -> PyResult<Vec<T>> {
391 let item_count = self.item_count();
392 let mut vec: Vec<T> = Vec::with_capacity(item_count);
393
394 err::error_on_minusone(py, unsafe {
397 ffi::PyBuffer_ToContiguous(
398 vec.as_mut_ptr().cast(),
399 #[cfg(Py_3_11)]
400 self.raw(),
401 #[cfg(not(Py_3_11))]
402 ptr::from_ref(self.raw()).cast_mut(),
403 self.raw().len,
404 fort as std::ffi::c_char,
405 )
406 })?;
407 unsafe { vec.set_len(item_count) };
409 Ok(vec)
410 }
411
412 pub fn copy_from_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
423 self._copy_from_slice(py, source, b'C')
424 }
425
426 pub fn copy_from_fortran_slice(&self, py: Python<'_>, source: &[T]) -> PyResult<()> {
437 self._copy_from_slice(py, source, b'F')
438 }
439
440 fn _copy_from_slice(&self, py: Python<'_>, source: &[T], fort: u8) -> PyResult<()> {
441 if self.readonly() {
442 return Err(PyBufferError::new_err("cannot write to read-only buffer"));
443 } else if mem::size_of_val(source) != self.len_bytes() {
444 return Err(PyBufferError::new_err(format!(
445 "slice to copy from (of length {}) does not match buffer length of {}",
446 source.len(),
447 self.item_count()
448 )));
449 }
450
451 err::error_on_minusone(py, unsafe {
452 ffi::PyBuffer_FromContiguous(
453 #[cfg(Py_3_11)]
454 self.raw(),
455 #[cfg(not(Py_3_11))]
456 ptr::from_ref(self.raw()).cast_mut(),
457 #[cfg(Py_3_11)]
458 {
459 source.as_ptr().cast()
460 },
461 #[cfg(not(Py_3_11))]
462 {
463 source.as_ptr().cast::<c_void>().cast_mut()
464 },
465 self.raw().len,
466 fort as std::ffi::c_char,
467 )
468 })
469 }
470}
471
472impl<T> std::ops::Deref for PyBuffer<T> {
473 type Target = PyUntypedBuffer;
474
475 fn deref(&self) -> &Self::Target {
476 &self.0
477 }
478}
479
480impl PyUntypedBuffer {
481 pub fn get(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
483 let buf = {
484 let mut buf = Box::<RawBuffer>::new_uninit();
485 err::error_on_minusone(obj.py(), unsafe {
487 ffi::PyObject_GetBuffer(
488 obj.as_ptr(),
489 buf.as_mut_ptr().cast::<ffi::Py_buffer>(),
490 ffi::PyBUF_FULL_RO,
491 )
492 })?;
493 unsafe { buf.assume_init() }
495 };
496 let buf = Self(Pin::from(buf));
499 let raw = buf.raw();
500
501 if raw.shape.is_null() {
502 Err(PyBufferError::new_err("shape is null"))
503 } else if raw.strides.is_null() {
504 Err(PyBufferError::new_err("strides is null"))
505 } else {
506 Ok(buf)
507 }
508 }
509
510 pub fn into_typed<T: Element>(self) -> PyResult<PyBuffer<T>> {
512 self.ensure_compatible_with::<T>()?;
513 Ok(PyBuffer(self, PhantomData))
514 }
515
516 pub fn as_typed<T: Element>(&self) -> PyResult<&PyBuffer<T>> {
518 self.ensure_compatible_with::<T>()?;
519 Ok(unsafe { NonNull::from(self).cast::<PyBuffer<T>>().as_ref() })
521 }
522
523 fn ensure_compatible_with<T: Element>(&self) -> PyResult<()> {
524 if mem::size_of::<T>() != self.item_size() || !T::is_compatible_format(self.format()) {
525 Err(PyBufferError::new_err(format!(
526 "buffer contents are not compatible with {}",
527 std::any::type_name::<T>()
528 )))
529 } else if self.raw().buf.align_offset(mem::align_of::<T>()) != 0 {
530 Err(PyBufferError::new_err(format!(
531 "buffer contents are insufficiently aligned for {}",
532 std::any::type_name::<T>()
533 )))
534 } else {
535 Ok(())
536 }
537 }
538
539 pub fn release(self, _py: Python<'_>) {
544 let mut mdself = mem::ManuallyDrop::new(self);
548 unsafe {
549 mdself.0.release();
552
553 ptr::drop_in_place::<Pin<Box<RawBuffer>>>(&mut mdself.0);
556 }
557 }
558
559 #[inline]
569 pub fn buf_ptr(&self) -> *mut c_void {
570 self.raw().buf
571 }
572
573 #[inline]
580 pub fn obj<'py>(&self, py: Python<'py>) -> Option<&Bound<'py, PyAny>> {
581 unsafe { Bound::ref_from_ptr_or_opt(py, &self.raw().obj).as_ref() }
582 }
583
584 pub fn get_ptr(&self, indices: &[usize]) -> *mut c_void {
588 let shape = &self.shape()[..indices.len()];
589 for i in 0..indices.len() {
590 assert!(indices[i] < shape[i]);
591 }
592 unsafe {
593 ffi::PyBuffer_GetPointer(
594 #[cfg(Py_3_11)]
595 self.raw(),
596 #[cfg(not(Py_3_11))]
597 ptr::from_ref(self.raw()).cast_mut(),
598 #[cfg(Py_3_11)]
599 indices.as_ptr().cast(),
600 #[cfg(not(Py_3_11))]
601 indices.as_ptr().cast_mut().cast(),
602 )
603 }
604 }
605
606 #[inline]
608 pub fn readonly(&self) -> bool {
609 self.raw().readonly != 0
610 }
611
612 #[inline]
615 pub fn item_size(&self) -> usize {
616 self.raw().itemsize as usize
617 }
618
619 #[inline]
621 pub fn item_count(&self) -> usize {
622 (self.raw().len as usize) / (self.raw().itemsize as usize)
623 }
624
625 #[inline]
629 pub fn len_bytes(&self) -> usize {
630 self.raw().len as usize
631 }
632
633 #[inline]
637 pub fn dimensions(&self) -> usize {
638 self.raw().ndim as usize
639 }
640
641 #[inline]
649 pub fn shape(&self) -> &[usize] {
650 unsafe { slice::from_raw_parts(self.raw().shape.cast(), self.raw().ndim as usize) }
651 }
652
653 #[inline]
658 pub fn strides(&self) -> &[isize] {
659 unsafe { slice::from_raw_parts(self.raw().strides, self.raw().ndim as usize) }
660 }
661
662 #[inline]
668 pub fn suboffsets(&self) -> Option<&[isize]> {
669 unsafe {
670 if self.raw().suboffsets.is_null() {
671 None
672 } else {
673 Some(slice::from_raw_parts(
674 self.raw().suboffsets,
675 self.raw().ndim as usize,
676 ))
677 }
678 }
679 }
680
681 #[inline]
683 pub fn format(&self) -> &CStr {
684 if self.raw().format.is_null() {
685 ffi::c_str!("B")
686 } else {
687 unsafe { CStr::from_ptr(self.raw().format) }
688 }
689 }
690
691 #[inline]
693 pub fn is_c_contiguous(&self) -> bool {
694 unsafe { ffi::PyBuffer_IsContiguous(self.raw(), b'C' as std::ffi::c_char) != 0 }
695 }
696
697 #[inline]
699 pub fn is_fortran_contiguous(&self) -> bool {
700 unsafe { ffi::PyBuffer_IsContiguous(self.raw(), b'F' as std::ffi::c_char) != 0 }
701 }
702
703 fn raw(&self) -> &ffi::Py_buffer {
704 &self.0 .0
705 }
706}
707
708impl RawBuffer {
709 unsafe fn release(self: &mut Pin<Box<Self>>) {
718 unsafe {
719 ffi::PyBuffer_Release(&mut Pin::get_unchecked_mut(self.as_mut()).0);
720 }
721 }
722}
723
724impl Drop for PyUntypedBuffer {
725 fn drop(&mut self) {
726 if Python::try_attach(|_| unsafe { self.0.release() }).is_none()
727 && crate::internal::state::is_in_gc_traversal()
728 {
729 eprintln!("Warning: PyBuffer dropped while in GC traversal, this is a bug and will leak memory.");
730 }
731 }
736}
737
738#[repr(transparent)]
744pub struct ReadOnlyCell<T: Element>(cell::UnsafeCell<T>);
745
746impl<T: Element> ReadOnlyCell<T> {
747 #[inline]
749 pub fn get(&self) -> T {
750 unsafe { *self.0.get() }
751 }
752
753 #[inline]
755 pub fn as_ptr(&self) -> *const T {
756 self.0.get()
757 }
758}
759
760macro_rules! impl_element(
761 ($t:ty, $f:ident) => {
762 unsafe impl Element for $t {
763 fn is_compatible_format(format: &CStr) -> bool {
764 let slice = format.to_bytes();
765 if slice.len() > 1 && !is_matching_endian(slice[0]) {
766 return false;
767 }
768 ElementType::from_format(format) == ElementType::$f { bytes: mem::size_of::<$t>() }
769 }
770 }
771 }
772);
773
774impl_element!(u8, UnsignedInteger);
775impl_element!(u16, UnsignedInteger);
776impl_element!(u32, UnsignedInteger);
777impl_element!(u64, UnsignedInteger);
778impl_element!(usize, UnsignedInteger);
779impl_element!(i8, SignedInteger);
780impl_element!(i16, SignedInteger);
781impl_element!(i32, SignedInteger);
782impl_element!(i64, SignedInteger);
783impl_element!(isize, SignedInteger);
784impl_element!(f32, Float);
785impl_element!(f64, Float);
786
787#[repr(u8)]
788enum PyBufferContiguity {
789 Undefined = 0,
790 C = 1,
791 F = 2,
792 Any = 3,
793}
794
795const CONTIGUITY_UNDEFINED: u8 = PyBufferContiguity::Undefined as u8;
796const CONTIGUITY_C: u8 = PyBufferContiguity::C as u8;
797const CONTIGUITY_F: u8 = PyBufferContiguity::F as u8;
798const CONTIGUITY_ANY: u8 = PyBufferContiguity::Any as u8;
799
800pub struct PyBufferFlags<
803 const FORMAT: bool = false,
804 const SHAPE: bool = false,
805 const STRIDE: bool = false,
806 const WRITABLE: bool = false,
807 const CONTIGUITY: u8 = CONTIGUITY_UNDEFINED,
808>(c_int);
809
810mod py_buffer_flags_sealed {
811 pub trait Sealed {}
812 impl<
813 const FORMAT: bool,
814 const SHAPE: bool,
815 const STRIDE: bool,
816 const WRITABLE: bool,
817 const CONTIGUITY: u8,
818 > Sealed for super::PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY>
819 {
820 }
821}
822
823pub trait PyBufferFlagsType: py_buffer_flags_sealed::Sealed {
825 const CONTIGUITY: u8;
827}
828
829impl<
830 const FORMAT: bool,
831 const SHAPE: bool,
832 const STRIDE: bool,
833 const WRITABLE: bool,
834 const CONTIGUITY_REQ: u8,
835 > PyBufferFlagsType for PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY_REQ>
836{
837 const CONTIGUITY: u8 = CONTIGUITY_REQ;
838}
839
840impl<const SHAPE: bool, const STRIDE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
841 PyBufferFlags<false, SHAPE, STRIDE, WRITABLE, CONTIGUITY>
842{
843 pub const fn format(self) -> PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY> {
845 PyBufferFlags(self.0 | ffi::PyBUF_FORMAT)
846 }
847}
848
849impl<const FORMAT: bool, const STRIDE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
850 PyBufferFlags<FORMAT, false, STRIDE, WRITABLE, CONTIGUITY>
851{
852 pub const fn nd(self) -> PyBufferFlags<FORMAT, true, STRIDE, WRITABLE, CONTIGUITY> {
854 PyBufferFlags(self.0 | ffi::PyBUF_ND)
855 }
856}
857
858impl<const FORMAT: bool, const SHAPE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
859 PyBufferFlags<FORMAT, SHAPE, false, WRITABLE, CONTIGUITY>
860{
861 pub const fn strides(self) -> PyBufferFlags<FORMAT, true, true, WRITABLE, CONTIGUITY> {
863 PyBufferFlags(self.0 | ffi::PyBUF_STRIDES)
864 }
865
866 pub const fn indirect(self) -> PyBufferFlags<FORMAT, true, true, WRITABLE, CONTIGUITY> {
868 PyBufferFlags(self.0 | ffi::PyBUF_INDIRECT)
869 }
870}
871
872impl<const FORMAT: bool, const SHAPE: bool, const STRIDE: bool, const CONTIGUITY: u8>
873 PyBufferFlags<FORMAT, SHAPE, STRIDE, false, CONTIGUITY>
874{
875 pub const fn writable(self) -> PyBufferFlags<FORMAT, SHAPE, STRIDE, true, CONTIGUITY> {
877 PyBufferFlags(self.0 | ffi::PyBUF_WRITABLE)
878 }
879}
880
881impl<const FORMAT: bool, const SHAPE: bool, const STRIDE: bool, const WRITABLE: bool>
882 PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY_UNDEFINED>
883{
884 pub const fn c_contiguous(self) -> PyBufferFlags<FORMAT, true, true, WRITABLE, CONTIGUITY_C> {
886 PyBufferFlags(self.0 | ffi::PyBUF_C_CONTIGUOUS)
887 }
888
889 pub const fn f_contiguous(self) -> PyBufferFlags<FORMAT, true, true, WRITABLE, CONTIGUITY_F> {
891 PyBufferFlags(self.0 | ffi::PyBUF_F_CONTIGUOUS)
892 }
893
894 pub const fn any_contiguous(
899 self,
900 ) -> PyBufferFlags<FORMAT, true, true, WRITABLE, CONTIGUITY_ANY> {
901 PyBufferFlags(self.0 | ffi::PyBUF_ANY_CONTIGUOUS)
902 }
903}
904
905impl PyBufferFlags {
906 pub const fn simple() -> PyBufferFlags {
908 PyBufferFlags(ffi::PyBUF_SIMPLE)
909 }
910
911 pub const fn full() -> PyBufferFlags<true, true, true, true> {
913 PyBufferFlags(ffi::PyBUF_FULL)
914 }
915
916 pub const fn full_ro() -> PyBufferFlags<true, true, true> {
918 PyBufferFlags(ffi::PyBUF_FULL_RO)
919 }
920
921 pub const fn records() -> PyBufferFlags<true, true, true, true> {
923 PyBufferFlags(ffi::PyBUF_RECORDS)
924 }
925
926 pub const fn records_ro() -> PyBufferFlags<true, true, true> {
928 PyBufferFlags(ffi::PyBUF_RECORDS_RO)
929 }
930
931 pub const fn strided() -> PyBufferFlags<false, true, true, true> {
933 PyBufferFlags(ffi::PyBUF_STRIDED)
934 }
935
936 pub const fn strided_ro() -> PyBufferFlags<false, true, true> {
938 PyBufferFlags(ffi::PyBUF_STRIDED_RO)
939 }
940
941 pub const fn contig() -> PyBufferFlags<false, true, false, true, CONTIGUITY_C> {
943 PyBufferFlags(ffi::PyBUF_CONTIG)
944 }
945
946 pub const fn contig_ro() -> PyBufferFlags<false, true, false, false, CONTIGUITY_C> {
948 PyBufferFlags(ffi::PyBUF_CONTIG_RO)
949 }
950}
951
952#[repr(transparent)]
955pub struct PyBufferView<T, Flags: PyBufferFlagsType = PyBufferFlags<true, true, true>>(
956 PyUntypedBufferView<Flags>,
957 PhantomData<[T]>,
958);
959
960pub struct PyUntypedBufferView<Flags: PyBufferFlagsType = PyBufferFlags> {
968 raw: ffi::Py_buffer,
969 _flags: PhantomData<Flags>,
970}
971
972impl<Flags: PyBufferFlagsType> PyUntypedBufferView<Flags> {
973 #[inline]
975 pub fn buf_ptr(&self) -> *mut c_void {
976 self.raw.buf
977 }
978
979 #[inline]
981 pub fn obj<'py>(&self, py: Python<'py>) -> Option<&Bound<'py, PyAny>> {
982 unsafe { Bound::ref_from_ptr_or_opt(py, &self.raw.obj).as_ref() }
983 }
984
985 #[inline]
987 pub fn readonly(&self) -> bool {
988 self.raw.readonly != 0
989 }
990
991 #[inline]
993 pub fn item_size(&self) -> usize {
994 self.raw.itemsize as usize
995 }
996
997 #[inline]
999 pub fn item_count(&self) -> usize {
1000 (self.raw.len as usize) / (self.raw.itemsize as usize)
1001 }
1002
1003 #[inline]
1006 pub fn len_bytes(&self) -> usize {
1007 self.raw.len as usize
1008 }
1009
1010 #[inline]
1014 pub fn dimensions(&self) -> usize {
1015 self.raw.ndim as usize
1016 }
1017
1018 #[inline]
1022 pub fn suboffsets(&self) -> Option<&[isize]> {
1023 if self.raw.suboffsets.is_null() {
1024 return None;
1025 }
1026
1027 Some(unsafe { slice::from_raw_parts(self.raw.suboffsets, self.raw.ndim as usize) })
1028 }
1029
1030 #[inline]
1032 pub fn is_c_contiguous(&self) -> bool {
1033 Flags::CONTIGUITY == CONTIGUITY_C
1034 || unsafe { ffi::PyBuffer_IsContiguous(&self.raw, b'C' as std::ffi::c_char) != 0 }
1035 }
1036
1037 #[inline]
1039 pub fn is_fortran_contiguous(&self) -> bool {
1040 Flags::CONTIGUITY == CONTIGUITY_F
1041 || unsafe { ffi::PyBuffer_IsContiguous(&self.raw, b'F' as std::ffi::c_char) != 0 }
1042 }
1043}
1044
1045impl<const SHAPE: bool, const STRIDE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
1046 PyUntypedBufferView<PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY>>
1047{
1048 #[inline]
1051 pub fn format(&self) -> &CStr {
1052 debug_assert!(!self.raw.format.is_null());
1053 unsafe { CStr::from_ptr(self.raw.format) }
1054 }
1055
1056 pub fn as_typed<T: Element>(
1058 &self,
1059 ) -> PyResult<&PyBufferView<T, PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY>>> {
1060 self.ensure_compatible_with::<T>()?;
1061 Ok(unsafe {
1063 NonNull::from(self)
1064 .cast::<PyBufferView<T, PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY>>>()
1065 .as_ref()
1066 })
1067 }
1068
1069 fn ensure_compatible_with<T: Element>(&self) -> PyResult<()> {
1070 check_buffer_compatibility::<T>(self.raw.buf, self.item_size(), self.format())
1071 }
1072}
1073
1074impl<const FORMAT: bool, const STRIDE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
1075 PyUntypedBufferView<PyBufferFlags<FORMAT, true, STRIDE, WRITABLE, CONTIGUITY>>
1076{
1077 #[inline]
1083 pub fn shape(&self) -> &[usize] {
1084 debug_assert!(!self.raw.shape.is_null());
1085 unsafe { slice::from_raw_parts(self.raw.shape.cast(), self.raw.ndim as usize) }
1086 }
1087}
1088
1089impl<const FORMAT: bool, const SHAPE: bool, const WRITABLE: bool, const CONTIGUITY: u8>
1090 PyUntypedBufferView<PyBufferFlags<FORMAT, SHAPE, true, WRITABLE, CONTIGUITY>>
1091{
1092 #[inline]
1097 pub fn strides(&self) -> &[isize] {
1098 debug_assert!(!self.raw.strides.is_null());
1099 unsafe { slice::from_raw_parts(self.raw.strides, self.raw.ndim as usize) }
1100 }
1101}
1102
1103impl<const WRITABLE: bool>
1105 PyUntypedBufferView<PyBufferFlags<false, false, false, WRITABLE, CONTIGUITY_UNDEFINED>>
1106{
1107 #[inline]
1109 pub fn format(&self) -> &CStr {
1110 ffi::c_str!("B")
1111 }
1112}
1113
1114fn check_buffer_compatibility<T: Element>(
1116 buf: *mut c_void,
1117 itemsize: usize,
1118 format: &CStr,
1119) -> PyResult<()> {
1120 let name = std::any::type_name::<T>();
1121
1122 if mem::size_of::<T>() != itemsize || !T::is_compatible_format(format) {
1123 return Err(PyBufferError::new_err(format!(
1124 "buffer contents are not compatible with {name}"
1125 )));
1126 }
1127
1128 if buf.align_offset(mem::align_of::<T>()) != 0 {
1129 return Err(PyBufferError::new_err(format!(
1130 "buffer contents are insufficiently aligned for {name}"
1131 )));
1132 }
1133
1134 Ok(())
1135}
1136
1137impl PyUntypedBufferView {
1138 pub fn with_flags<
1144 const FORMAT: bool,
1145 const SHAPE: bool,
1146 const STRIDE: bool,
1147 const WRITABLE: bool,
1148 const CONTIGUITY: u8,
1149 R,
1150 >(
1151 obj: &Bound<'_, PyAny>,
1152 flags: PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY>,
1153 f: impl FnOnce(
1154 &PyUntypedBufferView<PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY>>,
1155 ) -> R,
1156 ) -> PyResult<R> {
1157 let mut raw = mem::MaybeUninit::<ffi::Py_buffer>::uninit();
1158
1159 err::error_on_minusone(obj.py(), unsafe {
1160 ffi::PyObject_GetBuffer(obj.as_ptr(), raw.as_mut_ptr(), flags.0)
1161 })?;
1162
1163 let view = PyUntypedBufferView {
1164 raw: unsafe { raw.assume_init() },
1165 _flags: PhantomData,
1166 };
1167
1168 Ok(f(&view))
1169 }
1170}
1171
1172fn debug_buffer_view(
1173 name: &str,
1174 raw: &ffi::Py_buffer,
1175 f: &mut std::fmt::Formatter<'_>,
1176) -> std::fmt::Result {
1177 let ndim = raw.ndim as usize;
1178 let format = NonNull::new(raw.format).map(|p| unsafe { CStr::from_ptr(p.as_ptr()) });
1179 let shape = NonNull::new(raw.shape)
1180 .map(|p| unsafe { slice::from_raw_parts(p.as_ptr().cast::<usize>(), ndim) });
1181 let strides =
1182 NonNull::new(raw.strides).map(|p| unsafe { slice::from_raw_parts(p.as_ptr(), ndim) });
1183 let suboffsets =
1184 NonNull::new(raw.suboffsets).map(|p| unsafe { slice::from_raw_parts(p.as_ptr(), ndim) });
1185
1186 f.debug_struct(name)
1187 .field("buf", &raw.buf)
1188 .field("obj", &raw.obj)
1189 .field("len", &raw.len)
1190 .field("itemsize", &raw.itemsize)
1191 .field("readonly", &raw.readonly)
1192 .field("ndim", &raw.ndim)
1193 .field("format", &format)
1194 .field("shape", &shape)
1195 .field("strides", &strides)
1196 .field("suboffsets", &suboffsets)
1197 .field("internal", &raw.internal)
1198 .finish()
1199}
1200
1201impl<Flags: PyBufferFlagsType> Drop for PyUntypedBufferView<Flags> {
1202 fn drop(&mut self) {
1203 unsafe { ffi::PyBuffer_Release(&mut self.raw) }
1204 }
1205}
1206
1207impl<Flags: PyBufferFlagsType> Debug for PyUntypedBufferView<Flags> {
1208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1209 debug_buffer_view("PyUntypedBufferView", &self.raw, f)
1210 }
1211}
1212
1213impl<T: Element> PyBufferView<T> {
1214 pub fn with<R>(obj: &Bound<'_, PyAny>, f: impl FnOnce(&PyBufferView<T>) -> R) -> PyResult<R> {
1217 PyUntypedBufferView::with_flags(obj, PyBufferFlags::full_ro(), |view| {
1218 view.as_typed::<T>().map(f)
1219 })?
1220 }
1221
1222 pub fn with_flags<
1226 const FORMAT: bool,
1227 const SHAPE: bool,
1228 const STRIDE: bool,
1229 const WRITABLE: bool,
1230 const CONTIGUITY: u8,
1231 R,
1232 >(
1233 obj: &Bound<'_, PyAny>,
1234 flags: PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY>,
1235 f: impl FnOnce(&PyBufferView<T, PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY>>) -> R,
1236 ) -> PyResult<R> {
1237 let mut raw = mem::MaybeUninit::<ffi::Py_buffer>::uninit();
1238
1239 err::error_on_minusone(obj.py(), unsafe {
1240 ffi::PyObject_GetBuffer(obj.as_ptr(), raw.as_mut_ptr(), flags.0 | ffi::PyBUF_FORMAT)
1241 })?;
1242
1243 let view = PyUntypedBufferView::<PyBufferFlags<true, SHAPE, STRIDE, WRITABLE, CONTIGUITY>> {
1244 raw: unsafe { raw.assume_init() },
1245 _flags: PhantomData,
1246 };
1247
1248 view.as_typed::<T>().map(f)
1249 }
1250}
1251
1252impl<T: Element, Flags: PyBufferFlagsType> PyBufferView<T, Flags> {
1253 pub fn as_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [ReadOnlyCell<T>]> {
1260 if !self.is_c_contiguous() {
1261 return None;
1262 }
1263
1264 Some(unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) })
1265 }
1266
1267 pub fn as_mut_slice<'a>(&'a self, _py: Python<'a>) -> Option<&'a [cell::Cell<T>]> {
1274 if self.readonly() || !self.is_c_contiguous() {
1275 return None;
1276 }
1277
1278 Some(unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) })
1279 }
1280}
1281
1282impl<
1284 T: Element,
1285 const FORMAT: bool,
1286 const SHAPE: bool,
1287 const STRIDE: bool,
1288 const WRITABLE: bool,
1289 > PyBufferView<T, PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY_C>>
1290{
1291 pub fn as_contiguous_slice<'a>(&'a self, _py: Python<'a>) -> &'a [ReadOnlyCell<T>] {
1293 unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) }
1294 }
1295}
1296
1297impl<T: Element, const FORMAT: bool, const SHAPE: bool, const STRIDE: bool>
1299 PyBufferView<T, PyBufferFlags<FORMAT, SHAPE, STRIDE, true, CONTIGUITY_C>>
1300{
1301 pub fn as_contiguous_mut_slice<'a>(&'a self, _py: Python<'a>) -> &'a [cell::Cell<T>] {
1304 unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) }
1305 }
1306}
1307
1308impl<
1310 T: Element,
1311 const FORMAT: bool,
1312 const SHAPE: bool,
1313 const STRIDE: bool,
1314 const WRITABLE: bool,
1315 > PyBufferView<T, PyBufferFlags<FORMAT, SHAPE, STRIDE, WRITABLE, CONTIGUITY_F>>
1316{
1317 pub fn as_fortran_contiguous_slice<'a>(&'a self, _py: Python<'a>) -> &'a [ReadOnlyCell<T>] {
1319 unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) }
1320 }
1321}
1322
1323impl<T: Element, const FORMAT: bool, const SHAPE: bool, const STRIDE: bool>
1325 PyBufferView<T, PyBufferFlags<FORMAT, SHAPE, STRIDE, true, CONTIGUITY_F>>
1326{
1327 pub fn as_fortran_contiguous_mut_slice<'a>(&'a self, _py: Python<'a>) -> &'a [cell::Cell<T>] {
1330 unsafe { slice::from_raw_parts(self.0.raw.buf.cast(), self.item_count()) }
1331 }
1332}
1333
1334impl<T, Flags: PyBufferFlagsType> std::ops::Deref for PyBufferView<T, Flags> {
1335 type Target = PyUntypedBufferView<Flags>;
1336
1337 fn deref(&self) -> &Self::Target {
1338 &self.0
1339 }
1340}
1341
1342impl<T, Flags: PyBufferFlagsType> Debug for PyBufferView<T, Flags> {
1343 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1344 debug_buffer_view("PyBufferView", &self.0.raw, f)
1345 }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350 use super::*;
1351
1352 use crate::ffi;
1353 use crate::types::any::PyAnyMethods;
1354 use crate::types::PyBytes;
1355 use crate::Python;
1356
1357 #[test]
1358 fn test_debug() {
1359 Python::attach(|py| {
1360 let bytes = PyBytes::new(py, b"abcde");
1361 let buffer: PyBuffer<u8> = PyBuffer::get(&bytes).unwrap();
1362 let expected = format!(
1363 concat!(
1364 "PyBuffer {{ buf: {:?}, obj: {:?}, ",
1365 "len: 5, itemsize: 1, readonly: 1, ",
1366 "ndim: 1, format: \"B\", shape: [5], ",
1367 "strides: [1], suboffsets: None, internal: {:?} }}",
1368 ),
1369 buffer.raw().buf,
1370 buffer.raw().obj,
1371 buffer.raw().internal
1372 );
1373 let debug_repr = format!("{:?}", buffer);
1374 assert_eq!(debug_repr, expected);
1375 });
1376 }
1377
1378 #[test]
1379 fn test_element_type_from_format() {
1380 use super::ElementType::*;
1381 use std::mem::size_of;
1382
1383 for (cstr, expected) in [
1384 (
1386 c"@b",
1387 SignedInteger {
1388 bytes: size_of::<c_schar>(),
1389 },
1390 ),
1391 (
1392 c"@c",
1393 UnsignedInteger {
1394 bytes: size_of::<c_char>(),
1395 },
1396 ),
1397 (
1398 c"@b",
1399 SignedInteger {
1400 bytes: size_of::<c_schar>(),
1401 },
1402 ),
1403 (
1404 c"@B",
1405 UnsignedInteger {
1406 bytes: size_of::<c_uchar>(),
1407 },
1408 ),
1409 (c"@?", Bool),
1410 (
1411 c"@h",
1412 SignedInteger {
1413 bytes: size_of::<c_short>(),
1414 },
1415 ),
1416 (
1417 c"@H",
1418 UnsignedInteger {
1419 bytes: size_of::<c_ushort>(),
1420 },
1421 ),
1422 (
1423 c"@i",
1424 SignedInteger {
1425 bytes: size_of::<c_int>(),
1426 },
1427 ),
1428 (
1429 c"@I",
1430 UnsignedInteger {
1431 bytes: size_of::<c_uint>(),
1432 },
1433 ),
1434 (
1435 c"@l",
1436 SignedInteger {
1437 bytes: size_of::<c_long>(),
1438 },
1439 ),
1440 (
1441 c"@L",
1442 UnsignedInteger {
1443 bytes: size_of::<c_ulong>(),
1444 },
1445 ),
1446 (
1447 c"@q",
1448 SignedInteger {
1449 bytes: size_of::<c_longlong>(),
1450 },
1451 ),
1452 (
1453 c"@Q",
1454 UnsignedInteger {
1455 bytes: size_of::<c_ulonglong>(),
1456 },
1457 ),
1458 (
1459 c"@n",
1460 SignedInteger {
1461 bytes: size_of::<libc::ssize_t>(),
1462 },
1463 ),
1464 (
1465 c"@N",
1466 UnsignedInteger {
1467 bytes: size_of::<libc::size_t>(),
1468 },
1469 ),
1470 (c"@e", Float { bytes: 2 }),
1471 (c"@f", Float { bytes: 4 }),
1472 (c"@d", Float { bytes: 8 }),
1473 (c"@z", Unknown),
1474 (c"=b", SignedInteger { bytes: 1 }),
1476 (c"=c", UnsignedInteger { bytes: 1 }),
1477 (c"=B", UnsignedInteger { bytes: 1 }),
1478 (c"=?", Bool),
1479 (c"=h", SignedInteger { bytes: 2 }),
1480 (c"=H", UnsignedInteger { bytes: 2 }),
1481 (c"=l", SignedInteger { bytes: 4 }),
1482 (c"=l", SignedInteger { bytes: 4 }),
1483 (c"=I", UnsignedInteger { bytes: 4 }),
1484 (c"=L", UnsignedInteger { bytes: 4 }),
1485 (c"=q", SignedInteger { bytes: 8 }),
1486 (c"=Q", UnsignedInteger { bytes: 8 }),
1487 (c"=e", Float { bytes: 2 }),
1488 (c"=f", Float { bytes: 4 }),
1489 (c"=d", Float { bytes: 8 }),
1490 (c"=z", Unknown),
1491 (c"=0", Unknown),
1492 (
1494 c"b",
1495 SignedInteger {
1496 bytes: size_of::<c_schar>(),
1497 },
1498 ),
1499 (
1500 c"B",
1501 UnsignedInteger {
1502 bytes: size_of::<c_uchar>(),
1503 },
1504 ),
1505 (c"?", Bool),
1506 (c"f", Float { bytes: 4 }),
1507 (c"d", Float { bytes: 8 }),
1508 (c"z", Unknown),
1509 (c"<i", SignedInteger { bytes: 4 }),
1511 (c">H", UnsignedInteger { bytes: 2 }),
1512 (c"!q", SignedInteger { bytes: 8 }),
1513 (c":b", Unknown),
1515 ] {
1516 assert_eq!(
1517 ElementType::from_format(cstr),
1518 expected,
1519 "element from format &Cstr: {cstr:?}",
1520 );
1521 }
1522 }
1523
1524 #[test]
1525 fn test_compatible_size() {
1526 assert_eq!(
1528 std::mem::size_of::<ffi::Py_ssize_t>(),
1529 std::mem::size_of::<usize>()
1530 );
1531 }
1532
1533 #[test]
1534 fn test_bytes_buffer() {
1535 Python::attach(|py| {
1536 let bytes = PyBytes::new(py, b"abcde");
1537 let buffer = PyBuffer::get(&bytes).unwrap();
1538 assert_eq!(buffer.dimensions(), 1);
1539 assert_eq!(buffer.item_count(), 5);
1540 assert_eq!(buffer.format().to_str().unwrap(), "B");
1541 assert_eq!(buffer.shape(), [5]);
1542 assert!(buffer.is_c_contiguous());
1544 assert!(buffer.is_fortran_contiguous());
1545
1546 let slice = buffer.as_slice(py).unwrap();
1547 assert_eq!(slice.len(), 5);
1548 assert_eq!(slice[0].get(), b'a');
1549 assert_eq!(slice[2].get(), b'c');
1550 assert_eq!(unsafe { *slice[0].as_ptr() }, b'a');
1551
1552 assert_eq!(unsafe { *(buffer.get_ptr(&[1]).cast::<u8>()) }, b'b');
1553
1554 assert!(buffer.as_mut_slice(py).is_none());
1555
1556 assert!(buffer.copy_to_slice(py, &mut [0u8]).is_err());
1557 let mut arr = [0; 5];
1558 buffer.copy_to_slice(py, &mut arr).unwrap();
1559 assert_eq!(arr, b"abcde" as &[u8]);
1560
1561 assert!(buffer.copy_from_slice(py, &[0u8; 5]).is_err());
1562 assert_eq!(buffer.to_vec(py).unwrap(), b"abcde");
1563 });
1564 }
1565
1566 #[test]
1567 fn test_array_buffer() {
1568 Python::attach(|py| {
1569 let array = py
1570 .import("array")
1571 .unwrap()
1572 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1573 .unwrap();
1574 let buffer = PyBuffer::get(&array).unwrap();
1575 assert_eq!(buffer.dimensions(), 1);
1576 assert_eq!(buffer.item_count(), 4);
1577 assert_eq!(buffer.format().to_str().unwrap(), "f");
1578 assert_eq!(buffer.shape(), [4]);
1579
1580 let slice = buffer.as_slice(py).unwrap();
1586 assert_eq!(slice.len(), 4);
1587 assert_eq!(slice[0].get(), 1.0);
1588 assert_eq!(slice[3].get(), 2.5);
1589
1590 let mut_slice = buffer.as_mut_slice(py).unwrap();
1591 assert_eq!(mut_slice.len(), 4);
1592 assert_eq!(mut_slice[0].get(), 1.0);
1593 mut_slice[3].set(2.75);
1594 assert_eq!(slice[3].get(), 2.75);
1595
1596 buffer
1597 .copy_from_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
1598 .unwrap();
1599 assert_eq!(slice[2].get(), 12.0);
1600
1601 assert_eq!(buffer.to_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
1602
1603 let buffer = PyBuffer::get(&array).unwrap();
1605 let slice = buffer.as_fortran_slice(py).unwrap();
1606 assert_eq!(slice.len(), 4);
1607 assert_eq!(slice[1].get(), 11.0);
1608
1609 let mut_slice = buffer.as_fortran_mut_slice(py).unwrap();
1610 assert_eq!(mut_slice.len(), 4);
1611 assert_eq!(mut_slice[2].get(), 12.0);
1612 mut_slice[3].set(2.75);
1613 assert_eq!(slice[3].get(), 2.75);
1614
1615 buffer
1616 .copy_from_fortran_slice(py, &[10.0f32, 11.0, 12.0, 13.0])
1617 .unwrap();
1618 assert_eq!(slice[2].get(), 12.0);
1619
1620 assert_eq!(buffer.to_fortran_vec(py).unwrap(), [10.0, 11.0, 12.0, 13.0]);
1621 });
1622 }
1623
1624 #[test]
1625 fn test_obj_getter() {
1626 Python::attach(|py| {
1627 let bytes = PyBytes::new(py, b"hello");
1628 let buf = PyUntypedBuffer::get(bytes.as_any()).unwrap();
1629
1630 let owner = buf.obj(py).unwrap();
1632 assert!(owner.is_instance_of::<PyBytes>());
1633 assert!(owner.is(&bytes));
1634
1635 let owner_ref: crate::Py<PyAny> = owner.clone().unbind();
1637 buf.release(py);
1638 drop(bytes);
1639 Python::attach(|py| {
1641 let rebound = owner_ref.bind(py);
1642 assert!(rebound.is_instance_of::<PyBytes>());
1643 });
1644 });
1645 }
1646
1647 #[test]
1648 fn test_copy_to_fortran_slice() {
1649 Python::attach(|py| {
1650 let array = py
1651 .import("array")
1652 .unwrap()
1653 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1654 .unwrap();
1655 let buffer = PyBuffer::get(&array).unwrap();
1656
1657 assert!(buffer.copy_to_fortran_slice(py, &mut [0.0f32]).is_err());
1659 let mut arr = [0.0f32; 4];
1661 buffer.copy_to_fortran_slice(py, &mut arr).unwrap();
1662 assert_eq!(arr, [1.0, 1.5, 2.0, 2.5]);
1663 });
1664 }
1665
1666 #[test]
1667 fn test_copy_from_slice_wrong_length() {
1668 Python::attach(|py| {
1669 let array = py
1670 .import("array")
1671 .unwrap()
1672 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1673 .unwrap();
1674 let buffer = PyBuffer::get(&array).unwrap();
1675 assert!(!buffer.readonly());
1677 assert!(buffer.copy_from_slice(py, &[0.0f32; 2]).is_err());
1678 assert!(buffer.copy_from_fortran_slice(py, &[0.0f32; 2]).is_err());
1679 });
1680 }
1681
1682 #[test]
1683 fn test_untyped_buffer() {
1684 Python::attach(|py| {
1685 let bytes = PyBytes::new(py, b"abcde");
1686 let buffer = PyUntypedBuffer::get(&bytes).unwrap();
1687 assert_eq!(buffer.dimensions(), 1);
1688 assert_eq!(buffer.item_count(), 5);
1689 assert_eq!(buffer.format().to_str().unwrap(), "B");
1690 assert_eq!(buffer.shape(), [5]);
1691 assert!(!buffer.buf_ptr().is_null());
1692 assert_eq!(buffer.strides(), &[1]);
1693 assert_eq!(buffer.len_bytes(), 5);
1694 assert_eq!(buffer.item_size(), 1);
1695 assert!(buffer.readonly());
1696 assert!(buffer.suboffsets().is_none());
1697
1698 assert!(format!("{:?}", buffer).starts_with("PyUntypedBuffer { buf: "));
1699
1700 let typed: &PyBuffer<u8> = buffer.as_typed().unwrap();
1701 assert_eq!(typed.dimensions(), 1);
1702 assert_eq!(typed.item_count(), 5);
1703 assert_eq!(typed.format().to_str().unwrap(), "B");
1704 assert_eq!(typed.shape(), [5]);
1705 });
1706 }
1707
1708 #[test]
1709 fn test_untyped_buffer_view() {
1710 Python::attach(|py| {
1711 let bytes = PyBytes::new(py, b"abcde");
1712 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::full_ro(), |view| {
1713 assert!(!view.buf_ptr().is_null());
1714 assert_eq!(view.len_bytes(), 5);
1715 assert_eq!(view.item_size(), 1);
1716 assert_eq!(view.item_count(), 5);
1717 assert!(view.readonly());
1718 assert_eq!(view.dimensions(), 1);
1719 assert_eq!(view.format().to_str().unwrap(), "B");
1721 assert_eq!(view.shape(), [5]);
1722 assert_eq!(view.strides(), [1]);
1723 assert!(view.suboffsets().is_none());
1724 assert!(view.is_c_contiguous());
1725 assert!(view.is_fortran_contiguous());
1726 assert!(view.obj(py).unwrap().is(&bytes));
1727 })
1728 .unwrap();
1729 });
1730 }
1731
1732 #[test]
1733 fn test_typed_buffer_view() {
1734 Python::attach(|py| {
1735 let bytes = PyBytes::new(py, b"abcde");
1736 PyBufferView::<u8>::with(&bytes, |view| {
1737 assert_eq!(view.dimensions(), 1);
1738 assert_eq!(view.item_count(), 5);
1739 assert_eq!(view.format().to_str().unwrap(), "B");
1741 assert_eq!(view.shape(), [5]);
1742
1743 let slice = view.as_slice(py).unwrap();
1744 assert_eq!(slice.len(), 5);
1745 assert_eq!(slice[0].get(), b'a');
1746 assert_eq!(slice[4].get(), b'e');
1747
1748 assert!(view.as_mut_slice(py).is_none());
1750 })
1751 .unwrap();
1752 });
1753 }
1754
1755 #[test]
1756 fn test_buffer_view_array() {
1757 Python::attach(|py| {
1758 let array = py
1759 .import("array")
1760 .unwrap()
1761 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1762 .unwrap();
1763 PyBufferView::<f32>::with(&array, |view| {
1764 assert_eq!(view.dimensions(), 1);
1765 assert_eq!(view.item_count(), 4);
1766 assert_eq!(view.format().to_str().unwrap(), "f");
1767 assert_eq!(view.shape(), [4]);
1768
1769 let slice = view.as_slice(py).unwrap();
1770 assert_eq!(slice.len(), 4);
1771 assert_eq!(slice[0].get(), 1.0);
1772 assert_eq!(slice[3].get(), 2.5);
1773
1774 let mut_slice = view.as_mut_slice(py).unwrap();
1776 assert_eq!(mut_slice[0].get(), 1.0);
1777 mut_slice[3].set(2.75);
1778 assert_eq!(slice[3].get(), 2.75);
1779 })
1780 .unwrap();
1781 });
1782 }
1783
1784 #[test]
1785 fn test_buffer_view_with_flags() {
1786 Python::attach(|py| {
1787 let bytes = PyBytes::new(py, b"abcde");
1788
1789 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple(), |view| {
1790 assert_eq!(view.item_count(), 5);
1791 assert_eq!(view.len_bytes(), 5);
1792 assert!(view.readonly());
1793 assert!(view.suboffsets().is_none());
1794 })
1795 .unwrap();
1796
1797 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().nd(), |view| {
1798 assert_eq!(view.item_count(), 5);
1799 assert_eq!(view.shape(), [5]);
1800 })
1801 .unwrap();
1802
1803 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().strides(), |view| {
1804 assert_eq!(view.shape(), [5]);
1805 assert_eq!(view.strides(), [1]);
1806 })
1807 .unwrap();
1808
1809 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().format(), |view| {
1810 assert_eq!(view.item_count(), 5);
1811 assert_eq!(view.format().to_str().unwrap(), "B");
1812 })
1813 .unwrap();
1814 });
1815 }
1816
1817 #[test]
1818 fn test_typed_buffer_view_with_flags() {
1819 Python::attach(|py| {
1820 let array = py
1821 .import("array")
1822 .unwrap()
1823 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1824 .unwrap();
1825
1826 PyBufferView::<f32>::with_flags(&array, PyBufferFlags::simple().nd(), |view| {
1827 assert_eq!(view.item_count(), 4);
1828 assert_eq!(view.format().to_str().unwrap(), "f");
1829 assert_eq!(view.shape(), [4]);
1830
1831 let slice = view.as_slice(py).unwrap();
1832 assert_eq!(slice[0].get(), 1.0);
1833 assert_eq!(slice[3].get(), 2.5);
1834
1835 let mut_slice = view.as_mut_slice(py).unwrap();
1836 mut_slice[0].set(9.0);
1837 assert_eq!(slice[0].get(), 9.0);
1838 })
1839 .unwrap();
1840 });
1841 }
1842
1843 #[test]
1844 fn test_typed_buffer_view_with_flags_incompatible() {
1845 Python::attach(|py| {
1846 let bytes = PyBytes::new(py, b"abcde");
1847 let result =
1848 PyBufferView::<f32>::with_flags(&bytes, PyBufferFlags::simple().nd(), |_view| {});
1849 assert!(result.is_err());
1850 });
1851 }
1852
1853 #[test]
1854 fn test_c_contiguous_slice() {
1855 Python::attach(|py| {
1856 let array = py
1857 .import("array")
1858 .unwrap()
1859 .call_method("array", ("f", (1.0, 1.5, 2.0)), None)
1860 .unwrap();
1861
1862 PyBufferView::<f32>::with_flags(
1864 &array,
1865 PyBufferFlags::simple().c_contiguous(),
1866 |view| {
1867 let slice = view.as_contiguous_slice(py);
1868 assert_eq!(slice.len(), 3);
1869 assert_eq!(slice[0].get(), 1.0);
1870 assert_eq!(slice[2].get(), 2.0);
1871 },
1872 )
1873 .unwrap();
1874
1875 PyBufferView::<f32>::with(&array, |view| {
1879 let mut_slice = view.as_mut_slice(py).unwrap();
1880 mut_slice[2].set(9.0);
1881 assert_eq!(view.as_slice(py).unwrap()[2].get(), 9.0);
1882 })
1883 .unwrap();
1884 });
1885 }
1886
1887 #[test]
1888 fn test_buffer_view_error() {
1889 Python::attach(|py| {
1890 let list = crate::types::PyList::empty(py);
1891 let result =
1892 PyUntypedBufferView::with_flags(&list, PyBufferFlags::full_ro(), |_view| {});
1893 assert!(result.is_err());
1894 });
1895 }
1896
1897 #[test]
1898 fn test_flag_builders() {
1899 Python::attach(|py| {
1900 let bytes = PyBytes::new(py, b"abcde");
1901 let array = py
1902 .import("array")
1903 .unwrap()
1904 .call_method("array", ("f", (1.0, 1.5, 2.0, 2.5)), None)
1905 .unwrap();
1906
1907 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple(), |view| {
1909 assert_eq!(view.format().to_str().unwrap(), "B");
1910 })
1911 .unwrap();
1912
1913 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().format(), |view| {
1914 assert_eq!(view.format().to_str().unwrap(), "B");
1915 })
1916 .unwrap();
1917
1918 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().nd(), |view| {
1919 assert_eq!(view.shape(), [5]);
1920 })
1921 .unwrap();
1922
1923 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().strides(), |view| {
1924 assert_eq!(view.shape(), [5]);
1925 assert_eq!(view.strides(), [1]);
1926 })
1927 .unwrap();
1928
1929 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple().indirect(), |view| {
1930 assert_eq!(view.shape(), [5]);
1931 assert_eq!(view.strides(), [1]);
1932 })
1933 .unwrap();
1934
1935 PyUntypedBufferView::with_flags(&array, PyBufferFlags::simple().writable(), |view| {
1936 assert_eq!(view.format().to_str().unwrap(), "B");
1937 assert!(!view.readonly());
1938 })
1939 .unwrap();
1940
1941 PyUntypedBufferView::with_flags(
1942 &array,
1943 PyBufferFlags::simple().writable().nd(),
1944 |view| {
1945 assert_eq!(view.shape(), [4]);
1946 assert!(!view.readonly());
1947 },
1948 )
1949 .unwrap();
1950
1951 PyUntypedBufferView::with_flags(
1953 &bytes,
1954 PyBufferFlags::simple().nd().format(),
1955 |view| {
1956 assert_eq!(view.shape(), [5]);
1957 assert_eq!(view.format().to_str().unwrap(), "B");
1958 },
1959 )
1960 .unwrap();
1961
1962 PyUntypedBufferView::with_flags(
1963 &bytes,
1964 PyBufferFlags::simple().strides().format(),
1965 |view| {
1966 assert_eq!(view.shape(), [5]);
1967 assert_eq!(view.strides(), [1]);
1968 assert_eq!(view.format().to_str().unwrap(), "B");
1969 },
1970 )
1971 .unwrap();
1972
1973 PyUntypedBufferView::with_flags(
1975 &bytes,
1976 PyBufferFlags::simple().c_contiguous(),
1977 |view| {
1978 assert_eq!(view.shape(), [5]);
1979 assert_eq!(view.strides(), [1]);
1980 },
1981 )
1982 .unwrap();
1983
1984 PyUntypedBufferView::with_flags(
1985 &bytes,
1986 PyBufferFlags::simple().f_contiguous(),
1987 |view| {
1988 assert_eq!(view.shape(), [5]);
1989 assert_eq!(view.strides(), [1]);
1990 },
1991 )
1992 .unwrap();
1993
1994 PyUntypedBufferView::with_flags(
1995 &bytes,
1996 PyBufferFlags::simple().any_contiguous(),
1997 |view| {
1998 assert_eq!(view.shape(), [5]);
1999 assert_eq!(view.strides(), [1]);
2000 },
2001 )
2002 .unwrap();
2003
2004 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::full_ro(), |view| {
2006 assert_eq!(view.format().to_str().unwrap(), "B");
2007 assert_eq!(view.shape(), [5]);
2008 assert_eq!(view.strides(), [1]);
2009 })
2010 .unwrap();
2011
2012 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::records_ro(), |view| {
2013 assert_eq!(view.format().to_str().unwrap(), "B");
2014 assert_eq!(view.shape(), [5]);
2015 assert_eq!(view.strides(), [1]);
2016 })
2017 .unwrap();
2018
2019 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::strided_ro(), |view| {
2020 assert_eq!(view.shape(), [5]);
2021 assert_eq!(view.strides(), [1]);
2022 })
2023 .unwrap();
2024
2025 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::contig_ro(), |view| {
2026 assert_eq!(view.shape(), [5]);
2027 assert!(view.is_c_contiguous());
2028 })
2029 .unwrap();
2030
2031 PyUntypedBufferView::with_flags(&array, PyBufferFlags::full(), |view| {
2033 assert_eq!(view.format().to_str().unwrap(), "f");
2034 assert_eq!(view.shape(), [4]);
2035 assert_eq!(view.strides(), [4]);
2036 assert!(!view.readonly());
2037 })
2038 .unwrap();
2039
2040 PyUntypedBufferView::with_flags(&array, PyBufferFlags::records(), |view| {
2041 assert_eq!(view.format().to_str().unwrap(), "f");
2042 assert_eq!(view.shape(), [4]);
2043 assert!(!view.readonly());
2044 })
2045 .unwrap();
2046
2047 PyUntypedBufferView::with_flags(&array, PyBufferFlags::strided(), |view| {
2048 assert_eq!(view.shape(), [4]);
2049 assert_eq!(view.strides(), [4]);
2050 assert!(!view.readonly());
2051 })
2052 .unwrap();
2053
2054 PyUntypedBufferView::with_flags(&array, PyBufferFlags::contig(), |view| {
2055 assert_eq!(view.shape(), [4]);
2056 assert!(!view.readonly());
2057 assert!(view.is_c_contiguous());
2058 })
2059 .unwrap();
2060
2061 PyUntypedBufferView::with_flags(
2063 &bytes,
2064 PyBufferFlags::full_ro().c_contiguous(),
2065 |view| {
2066 assert_eq!(view.format().to_str().unwrap(), "B");
2067 assert_eq!(view.shape(), [5]);
2068 assert_eq!(view.strides(), [1]);
2069 },
2070 )
2071 .unwrap();
2072
2073 PyUntypedBufferView::with_flags(&array, PyBufferFlags::full().c_contiguous(), |view| {
2074 assert_eq!(view.format().to_str().unwrap(), "f");
2075 assert!(!view.readonly());
2076 })
2077 .unwrap();
2078
2079 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::strided_ro().format(), |view| {
2080 assert_eq!(view.format().to_str().unwrap(), "B");
2081 assert_eq!(view.shape(), [5]);
2082 assert_eq!(view.strides(), [1]);
2083 })
2084 .unwrap();
2085
2086 PyUntypedBufferView::with_flags(
2087 &bytes,
2088 PyBufferFlags::simple().c_contiguous().format(),
2089 |view| {
2090 assert_eq!(view.format().to_str().unwrap(), "B");
2091 assert_eq!(view.shape(), [5]);
2092 },
2093 )
2094 .unwrap();
2095
2096 PyBufferView::<u8>::with_flags(&bytes, PyBufferFlags::simple().format(), |view| {
2098 assert_eq!(view.format().to_str().unwrap(), "B");
2099 assert_eq!(view.item_count(), 5);
2100 })
2101 .unwrap();
2102
2103 PyBufferView::<f32>::with_flags(&array, PyBufferFlags::contig(), |view| {
2104 let slice = view.as_contiguous_slice(py);
2105 assert_eq!(slice[0].get(), 1.0);
2106 })
2107 .unwrap();
2108
2109 PyBufferView::<f32>::with_flags(&array, PyBufferFlags::contig(), |view| {
2111 let slice = view.as_contiguous_slice(py);
2112 assert_eq!(slice[0].get(), 1.0);
2113 let mut_slice = view.as_contiguous_mut_slice(py);
2114 mut_slice[0].set(9.0);
2115 assert_eq!(slice[0].get(), 9.0);
2116 })
2117 .unwrap();
2118
2119 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::simple(), |view| {
2121 assert_eq!(view.format().to_str().unwrap(), "B");
2122 })
2123 .unwrap();
2124 });
2125 }
2126
2127 #[test]
2128 fn test_buffer_view_debug() {
2129 Python::attach(|py| {
2130 let bytes = PyBytes::new(py, b"abcde");
2131
2132 PyUntypedBufferView::with_flags(&bytes, PyBufferFlags::full_ro(), |view| {
2134 let expected = format!(
2135 concat!(
2136 "PyUntypedBufferView {{ buf: {:?}, obj: {:?}, ",
2137 "len: 5, itemsize: 1, readonly: 1, ",
2138 "ndim: 1, format: Some(\"B\"), shape: Some([5]), ",
2139 "strides: Some([1]), suboffsets: None, internal: {:?} }}",
2140 ),
2141 view.raw.buf, view.raw.obj, view.raw.internal,
2142 );
2143
2144 let debug_repr = format!("{:?}", view);
2145 assert_eq!(debug_repr, expected);
2146 })
2147 .unwrap();
2148
2149 PyBufferView::<u8>::with(&bytes, |view| {
2150 let expected = format!(
2151 concat!(
2152 "PyBufferView {{ buf: {:?}, obj: {:?}, ",
2153 "len: 5, itemsize: 1, readonly: 1, ",
2154 "ndim: 1, format: Some(\"B\"), shape: Some([5]), ",
2155 "strides: Some([1]), suboffsets: None, internal: {:?} }}",
2156 ),
2157 view.0.raw.buf, view.0.raw.obj, view.0.raw.internal,
2158 );
2159
2160 let debug_repr = format!("{:?}", view);
2161 assert_eq!(debug_repr, expected);
2162 })
2163 .unwrap();
2164 });
2165 }
2166}