2021-09-13 20:58:15 +00:00
#![ cfg(not(Py_LIMITED_API)) ]
2017-01-20 00:08:51 +00:00
// Copyright (c) 2017 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2017-06-24 08:47:36 +00:00
//! `PyBuffer` implementation
2021-03-31 21:37:41 +00:00
use crate ::{
2021-08-01 21:41:32 +00:00
err , exceptions ::PyBufferError , ffi , AsPyPointer , FromPyObject , PyAny , PyResult , Python ,
2021-03-31 21:37:41 +00:00
} ;
2020-06-04 13:00:47 +00:00
use std ::marker ::PhantomData ;
2019-02-18 19:07:41 +00:00
use std ::os ::raw ;
2019-06-16 12:53:02 +00:00
use std ::pin ::Pin ;
2020-02-28 20:03:15 +00:00
use std ::{ cell , mem , ptr , slice } ;
2021-03-31 21:37:41 +00:00
use std ::{ ffi ::CStr , fmt ::Debug } ;
2017-01-20 00:08:51 +00:00
/// Allows access to the underlying buffer used by a python object such as `bytes`, `bytearray` or `array.array`.
2019-06-16 12:53:02 +00:00
// use Pin<Box> because Python expects that the Py_buffer struct has a stable memory address
2018-07-13 16:10:09 +00:00
#[ repr(transparent) ]
2020-06-04 13:00:47 +00:00
pub struct PyBuffer < T > ( Pin < Box < ffi ::Py_buffer > > , PhantomData < T > ) ;
2017-01-20 00:08:51 +00:00
2017-01-20 22:14:29 +00:00
// PyBuffer is thread-safe: the shape of the buffer is immutable while a Py_buffer exists.
// Accessing the buffer contents is protected using the GIL.
2020-06-04 13:00:47 +00:00
unsafe impl < T > Send for PyBuffer < T > { }
unsafe impl < T > Sync for PyBuffer < T > { }
2017-01-20 22:14:29 +00:00
2021-03-31 21:37:41 +00:00
impl < T > Debug for PyBuffer < T > {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
f . debug_struct ( " PyBuffer " )
. field ( " buf " , & self . 0. buf )
. field ( " obj " , & self . 0. obj )
. field ( " len " , & self . 0. len )
. field ( " itemsize " , & self . 0. itemsize )
. field ( " readonly " , & self . 0. readonly )
. field ( " ndim " , & self . 0. ndim )
. field ( " format " , & self . 0. format )
. field ( " shape " , & self . 0. shape )
. field ( " strides " , & self . 0. strides )
. field ( " suboffsets " , & self . 0. suboffsets )
. field ( " internal " , & self . 0. internal )
. finish ( )
}
}
2021-06-29 10:35:28 +00:00
/// Represents the type of a Python buffer element.
2017-01-20 22:14:29 +00:00
#[ derive(Copy, Clone, Eq, PartialEq) ]
pub enum ElementType {
2021-06-29 10:35:28 +00:00
/// A signed integer type and its width in bytes.
2017-01-20 22:14:29 +00:00
SignedInteger { bytes : usize } ,
2021-06-29 10:35:28 +00:00
/// An unsigned integer type and its width in bytes.
2017-01-20 22:14:29 +00:00
UnsignedInteger { bytes : usize } ,
2021-06-29 10:35:28 +00:00
/// A boolean type.
2017-01-20 22:14:29 +00:00
Bool ,
2021-06-29 10:35:28 +00:00
/// A float type and its width in bytes.
2017-01-20 22:14:29 +00:00
Float { bytes : usize } ,
2021-06-29 10:35:28 +00:00
/// An unknown type. This may occur when parsing has failed.
2018-07-30 21:01:46 +00:00
Unknown ,
2017-01-20 22:14:29 +00:00
}
impl ElementType {
2021-07-04 11:32:40 +00:00
/// Determines the `ElementType` from a Python `struct` module format string.
2017-01-20 22:14:29 +00:00
pub fn from_format ( format : & CStr ) -> ElementType {
2020-11-12 11:09:24 +00:00
match format . to_bytes ( ) {
[ char ] | [ b '@' , char ] = > native_element_type_from_type_char ( * char ) ,
2021-02-10 15:07:25 +00:00
[ modifier , char ]
if ( * modifier = = b '='
| | * modifier = = b '<'
| | * modifier = = b '>'
| | * modifier = = b '!' ) = >
{
2020-11-12 11:09:24 +00:00
standard_element_type_from_type_char ( * char )
2017-01-20 22:14:29 +00:00
}
2020-11-12 11:09:24 +00:00
_ = > ElementType ::Unknown ,
2017-01-20 22:14:29 +00:00
}
}
}
fn native_element_type_from_type_char ( type_char : u8 ) -> ElementType {
use self ::ElementType ::* ;
match type_char {
2018-07-30 21:01:46 +00:00
b 'c' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_char > ( ) ,
} ,
b 'b' = > SignedInteger {
bytes : mem ::size_of ::< raw ::c_schar > ( ) ,
} ,
b 'B' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_uchar > ( ) ,
} ,
2017-01-20 22:14:29 +00:00
b '?' = > Bool ,
2018-07-30 21:01:46 +00:00
b 'h' = > SignedInteger {
bytes : mem ::size_of ::< raw ::c_short > ( ) ,
} ,
b 'H' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_ushort > ( ) ,
} ,
b 'i' = > SignedInteger {
bytes : mem ::size_of ::< raw ::c_int > ( ) ,
} ,
b 'I' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_uint > ( ) ,
} ,
b 'l' = > SignedInteger {
bytes : mem ::size_of ::< raw ::c_long > ( ) ,
} ,
b 'L' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_ulong > ( ) ,
} ,
b 'q' = > SignedInteger {
bytes : mem ::size_of ::< raw ::c_longlong > ( ) ,
} ,
b 'Q' = > UnsignedInteger {
bytes : mem ::size_of ::< raw ::c_ulonglong > ( ) ,
} ,
b 'n' = > SignedInteger {
bytes : mem ::size_of ::< libc ::ssize_t > ( ) ,
} ,
b 'N' = > UnsignedInteger {
bytes : mem ::size_of ::< libc ::size_t > ( ) ,
} ,
2017-01-20 22:14:29 +00:00
b 'e' = > Float { bytes : 2 } ,
b 'f' = > Float { bytes : 4 } ,
b 'd' = > Float { bytes : 8 } ,
2018-07-30 21:01:46 +00:00
_ = > Unknown ,
2017-01-20 22:14:29 +00:00
}
}
fn standard_element_type_from_type_char ( type_char : u8 ) -> ElementType {
use self ::ElementType ::* ;
match type_char {
2018-01-19 18:02:36 +00:00
b 'c' | b 'B' = > UnsignedInteger { bytes : 1 } ,
2018-07-30 21:01:46 +00:00
b 'b' = > SignedInteger { bytes : 1 } ,
2017-01-20 22:14:29 +00:00
b '?' = > Bool ,
2018-07-30 21:01:46 +00:00
b 'h' = > SignedInteger { bytes : 2 } ,
2017-01-20 22:14:29 +00:00
b 'H' = > UnsignedInteger { bytes : 2 } ,
2018-07-30 21:01:46 +00:00
b 'i' | b 'l' = > SignedInteger { bytes : 4 } ,
2018-01-19 18:02:36 +00:00
b 'I' | b 'L' = > UnsignedInteger { bytes : 4 } ,
2018-07-30 21:01:46 +00:00
b 'q' = > SignedInteger { bytes : 8 } ,
2017-01-20 22:14:29 +00:00
b 'Q' = > UnsignedInteger { bytes : 8 } ,
b 'e' = > Float { bytes : 2 } ,
b 'f' = > Float { bytes : 4 } ,
b 'd' = > Float { bytes : 8 } ,
2018-07-30 21:01:46 +00:00
_ = > Unknown ,
2017-01-20 22:14:29 +00:00
}
}
#[ cfg(target_endian = " little " ) ]
fn is_matching_endian ( c : u8 ) -> bool {
2020-10-08 15:21:42 +00:00
c = = b '@' | | c = = b '=' | | c = = b '>'
2017-01-20 22:14:29 +00:00
}
#[ cfg(target_endian = " big " ) ]
fn is_matching_endian ( c : u8 ) -> bool {
2020-10-08 15:21:42 +00:00
c = = b '@' | | c = = b '=' | | c = = b '>' | | c = = b '!'
2017-01-20 22:14:29 +00:00
}
/// Trait implemented for possible element types of `PyBuffer`.
2020-06-04 13:00:47 +00:00
pub unsafe trait Element : Copy {
2017-01-20 22:14:29 +00:00
/// Gets whether the element specified in the format string is potentially compatible.
/// Alignment and size are checked separately from this function.
fn is_compatible_format ( format : & CStr ) -> bool ;
}
2020-06-04 13:00:47 +00:00
impl < ' source , T : Element > FromPyObject < ' source > for PyBuffer < T > {
fn extract ( obj : & PyAny ) -> PyResult < PyBuffer < T > > {
Self ::get ( obj )
}
2017-01-20 22:14:29 +00:00
}
2020-06-04 13:00:47 +00:00
impl < T : Element > PyBuffer < T > {
2017-01-20 00:08:51 +00:00
/// Get the underlying buffer from the specified python object.
2020-06-04 13:00:47 +00:00
pub fn get ( obj : & PyAny ) -> PyResult < PyBuffer < T > > {
2021-03-31 21:37:41 +00:00
// TODO: use nightly API Box::new_uninit() once stable
let mut buf = Box ::new ( mem ::MaybeUninit ::uninit ( ) ) ;
let buf : Box < ffi ::Py_buffer > = unsafe {
2017-05-25 03:31:51 +00:00
err ::error_on_minusone (
2020-06-04 13:00:47 +00:00
obj . py ( ) ,
2021-03-31 21:37:41 +00:00
ffi ::PyObject_GetBuffer ( obj . as_ptr ( ) , buf . as_mut_ptr ( ) , ffi ::PyBUF_FULL_RO ) ,
2018-07-30 21:01:46 +00:00
) ? ;
2021-03-31 21:37:41 +00:00
// Safety: buf is initialized by PyObject_GetBuffer.
// TODO: use nightly API Box::assume_init() once stable
mem ::transmute ( buf )
} ;
// Create PyBuffer immediately so that if validation checks fail, the PyBuffer::drop code
// will call PyBuffer_Release (thus avoiding any leaks).
let buf = PyBuffer ( Pin ::from ( buf ) , PhantomData ) ;
if buf . 0. shape . is_null ( ) {
Err ( PyBufferError ::new_err ( " shape is null " ) )
} else if buf . 0. strides . is_null ( ) {
Err ( PyBufferError ::new_err ( " strides is null " ) )
} else if mem ::size_of ::< T > ( ) ! = buf . item_size ( ) | | ! T ::is_compatible_format ( buf . format ( ) ) {
Err ( PyBufferError ::new_err ( format! (
" buffer contents are not compatible with {} " ,
std ::any ::type_name ::< T > ( )
) ) )
} else if buf . 0. buf . align_offset ( mem ::align_of ::< T > ( ) ) ! = 0 {
Err ( PyBufferError ::new_err ( format! (
" buffer contents are insufficiently aligned for {} " ,
std ::any ::type_name ::< T > ( )
) ) )
} else {
Ok ( buf )
2017-01-20 00:08:51 +00:00
}
}
/// Gets the pointer to the start of the buffer memory.
2017-01-20 22:14:29 +00:00
///
/// Warning: the buffer memory might be mutated by other Python functions,
/// and thus may only be accessed while the GIL is held.
2017-01-20 00:08:51 +00:00
#[ inline ]
2017-05-13 05:05:00 +00:00
pub fn buf_ptr ( & self ) -> * mut raw ::c_void {
2017-01-20 00:08:51 +00:00
self . 0. buf
}
2017-01-20 22:14:29 +00:00
/// Gets a pointer to the specified item.
///
/// If `indices.len() < self.dimensions()`, returns the start address of the sub-array at the specified dimension.
2017-05-13 05:05:00 +00:00
pub fn get_ptr ( & self , indices : & [ usize ] ) -> * mut raw ::c_void {
2017-01-20 22:14:29 +00:00
let shape = & self . shape ( ) [ .. indices . len ( ) ] ;
for i in 0 .. indices . len ( ) {
assert! ( indices [ i ] < shape [ i ] ) ;
}
unsafe {
ffi ::PyBuffer_GetPointer (
& * self . 0 as * const ffi ::Py_buffer as * mut ffi ::Py_buffer ,
2018-07-30 21:01:46 +00:00
indices . as_ptr ( ) as * mut usize as * mut ffi ::Py_ssize_t ,
2017-01-20 22:14:29 +00:00
)
}
}
2017-01-20 00:08:51 +00:00
/// Gets whether the underlying buffer is read-only.
#[ inline ]
pub fn readonly ( & self ) -> bool {
self . 0. readonly ! = 0
}
/// Gets the size of a single element, in bytes.
2017-06-24 08:47:36 +00:00
/// Important exception: when requesting an unformatted buffer, item_size still has the value
2017-01-20 00:08:51 +00:00
#[ inline ]
pub fn item_size ( & self ) -> usize {
self . 0. itemsize as usize
}
/// Gets the total number of items.
#[ inline ]
pub fn item_count ( & self ) -> usize {
( self . 0. len as usize ) / ( self . 0. itemsize as usize )
}
/// `item_size() * item_count()`.
/// For contiguous arrays, this is the length of the underlying memory block.
/// For non-contiguous arrays, it is the length that the logical structure would have if it were copied to a contiguous representation.
#[ inline ]
pub fn len_bytes ( & self ) -> usize {
self . 0. len as usize
}
/// Gets the number of dimensions.
///
/// May be 0 to indicate a single scalar value.
#[ inline ]
pub fn dimensions ( & self ) -> usize {
self . 0. ndim as usize
}
/// Returns an array of length `dimensions`. `shape()[i]` is the length of the array in dimension number `i`.
///
/// May return None for single-dimensional arrays or scalar values (`dimensions() <= 1`);
/// You can call `item_count()` to get the length of the single dimension.
///
/// Despite Python using an array of signed integers, the values are guaranteed to be non-negative.
/// However, dimensions of length 0 are possible and might need special attention.
#[ inline ]
2017-01-20 22:14:29 +00:00
pub fn shape ( & self ) -> & [ usize ] {
2018-07-30 21:01:46 +00:00
unsafe { slice ::from_raw_parts ( self . 0. shape as * const usize , self . 0. ndim as usize ) }
2017-01-20 00:08:51 +00:00
}
/// Returns an array that holds, for each dimension, the number of bytes to skip to get to the next element in the dimension.
///
/// Stride values can be any integer. For regular arrays, strides are usually positive,
/// but a consumer MUST be able to handle the case `strides[n] <= 0`.
#[ inline ]
2017-01-20 22:14:29 +00:00
pub fn strides ( & self ) -> & [ isize ] {
2018-07-30 21:01:46 +00:00
unsafe { slice ::from_raw_parts ( self . 0. strides , self . 0. ndim as usize ) }
2017-01-20 00:08:51 +00:00
}
2017-01-20 22:14:29 +00:00
/// An array of length ndim.
2018-08-24 17:01:14 +00:00
/// If `suboffsets[n] >= 0`, the values stored along the nth dimension are pointers and the suboffset value dictates how many bytes to add to each pointer after de-referencing.
2017-01-20 22:14:29 +00:00
/// A suboffset value that is negative indicates that no de-referencing should occur (striding in a contiguous memory block).
///
/// If all suboffsets are negative (i.e. no de-referencing is needed), then this field must be NULL (the default value).
2017-01-20 00:08:51 +00:00
#[ inline ]
2017-01-20 22:14:29 +00:00
pub fn suboffsets ( & self ) -> Option < & [ isize ] > {
2017-01-20 00:08:51 +00:00
unsafe {
if self . 0. suboffsets . is_null ( ) {
None
} else {
2018-07-30 21:01:46 +00:00
Some ( slice ::from_raw_parts (
self . 0. suboffsets ,
self . 0. ndim as usize ,
) )
2017-01-20 00:08:51 +00:00
}
}
}
/// A NUL terminated string in struct module style syntax describing the contents of a single item.
#[ inline ]
pub fn format ( & self ) -> & CStr {
if self . 0. format . is_null ( ) {
2019-03-20 20:48:36 +00:00
CStr ::from_bytes_with_nul ( b " B \0 " ) . unwrap ( )
2017-01-20 00:08:51 +00:00
} else {
unsafe { CStr ::from_ptr ( self . 0. format ) }
}
}
/// Gets whether the buffer is contiguous in C-style order (last index varies fastest when visiting items in order of memory address).
#[ inline ]
pub fn is_c_contiguous ( & self ) -> bool {
unsafe {
2021-01-07 07:34:47 +00:00
ffi ::PyBuffer_IsContiguous (
& * self . 0 as * const ffi ::Py_buffer ,
b 'C' as std ::os ::raw ::c_char ,
) ! = 0
2017-01-20 00:08:51 +00:00
}
}
2017-01-20 22:14:29 +00:00
/// Gets whether the buffer is contiguous in Fortran-style order (first index varies fastest when visiting items in order of memory address).
2017-01-20 00:08:51 +00:00
#[ inline ]
pub fn is_fortran_contiguous ( & self ) -> bool {
unsafe {
2021-01-07 07:34:47 +00:00
ffi ::PyBuffer_IsContiguous (
& * self . 0 as * const ffi ::Py_buffer ,
b 'F' as std ::os ::raw ::c_char ,
) ! = 0
2017-01-20 00:08:51 +00:00
}
}
2017-01-20 22:14:29 +00:00
/// Gets the buffer memory as a slice.
///
/// This function succeeds if:
/// * the buffer format is compatible with `T`
/// * alignment and size of buffer elements is matching the expectations for type `T`
/// * the buffer is C-style contiguous
///
/// The returned slice uses type `Cell<T>` because it's theoretically possible for any call into the Python runtime
/// to modify the values in the slice.
2020-06-04 13:00:47 +00:00
pub fn as_slice < ' a > ( & ' a self , _py : Python < ' a > ) -> Option < & ' a [ ReadOnlyCell < T > ] > {
if self . is_c_contiguous ( ) {
2018-07-30 21:01:46 +00:00
unsafe {
Some ( slice ::from_raw_parts (
self . 0. buf as * mut ReadOnlyCell < T > ,
self . item_count ( ) ,
) )
}
2017-01-20 22:14:29 +00:00
} else {
None
}
}
/// Gets the buffer memory as a slice.
///
/// This function succeeds if:
/// * the buffer is not read-only
/// * the buffer format is compatible with `T`
/// * alignment and size of buffer elements is matching the expectations for type `T`
/// * the buffer is C-style contiguous
///
/// The returned slice uses type `Cell<T>` because it's theoretically possible for any call into the Python runtime
/// to modify the values in the slice.
2020-06-04 13:00:47 +00:00
pub fn as_mut_slice < ' a > ( & ' a self , _py : Python < ' a > ) -> Option < & ' a [ cell ::Cell < T > ] > {
if ! self . readonly ( ) & & self . is_c_contiguous ( ) {
2018-07-30 21:01:46 +00:00
unsafe {
Some ( slice ::from_raw_parts (
self . 0. buf as * mut cell ::Cell < T > ,
self . item_count ( ) ,
) )
}
2017-01-20 22:14:29 +00:00
} else {
None
}
}
/// Gets the buffer memory as a slice.
///
/// This function succeeds if:
/// * the buffer format is compatible with `T`
/// * alignment and size of buffer elements is matching the expectations for type `T`
/// * the buffer is Fortran-style contiguous
///
/// The returned slice uses type `Cell<T>` because it's theoretically possible for any call into the Python runtime
/// to modify the values in the slice.
2020-06-04 13:00:47 +00:00
pub fn as_fortran_slice < ' a > ( & ' a self , _py : Python < ' a > ) -> Option < & ' a [ ReadOnlyCell < T > ] > {
if mem ::size_of ::< T > ( ) = = self . item_size ( ) & & self . is_fortran_contiguous ( ) {
2018-07-30 21:01:46 +00:00
unsafe {
Some ( slice ::from_raw_parts (
self . 0. buf as * mut ReadOnlyCell < T > ,
self . item_count ( ) ,
) )
}
2017-01-20 22:14:29 +00:00
} else {
None
}
}
/// Gets the buffer memory as a slice.
///
/// This function succeeds if:
/// * the buffer is not read-only
/// * the buffer format is compatible with `T`
/// * alignment and size of buffer elements is matching the expectations for type `T`
/// * the buffer is Fortran-style contiguous
///
/// The returned slice uses type `Cell<T>` because it's theoretically possible for any call into the Python runtime
/// to modify the values in the slice.
2020-06-04 13:00:47 +00:00
pub fn as_fortran_mut_slice < ' a > ( & ' a self , _py : Python < ' a > ) -> Option < & ' a [ cell ::Cell < T > ] > {
if ! self . readonly ( ) & & self . is_fortran_contiguous ( ) {
2018-07-30 21:01:46 +00:00
unsafe {
Some ( slice ::from_raw_parts (
self . 0. buf as * mut cell ::Cell < T > ,
self . item_count ( ) ,
) )
}
2017-01-20 22:14:29 +00:00
} else {
None
}
}
/// Copies the buffer elements to the specified slice.
/// If the buffer is multi-dimensional, the elements are written in C-style order.
///
/// * Fails if the slice does not have the correct length (`buf.item_count()`).
/// * Fails if the buffer format is not compatible with type `T`.
///
/// To check whether the buffer format is compatible before calling this method,
/// you can use `<T as buffer::Element>::is_compatible_format(buf.format())`.
/// Alternatively, `match buffer::ElementType::from_format(buf.format())`.
2020-06-04 13:00:47 +00:00
pub fn copy_to_slice ( & self , py : Python , target : & mut [ T ] ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
self . copy_to_slice_impl ( py , target , b 'C' )
}
/// Copies the buffer elements to the specified slice.
/// If the buffer is multi-dimensional, the elements are written in Fortran-style order.
///
/// * Fails if the slice does not have the correct length (`buf.item_count()`).
/// * Fails if the buffer format is not compatible with type `T`.
///
/// To check whether the buffer format is compatible before calling this method,
/// you can use `<T as buffer::Element>::is_compatible_format(buf.format())`.
/// Alternatively, `match buffer::ElementType::from_format(buf.format())`.
2020-06-04 13:00:47 +00:00
pub fn copy_to_fortran_slice ( & self , py : Python , target : & mut [ T ] ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
self . copy_to_slice_impl ( py , target , b 'F' )
}
2020-06-04 13:00:47 +00:00
fn copy_to_slice_impl ( & self , py : Python , target : & mut [ T ] , fort : u8 ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
if mem ::size_of_val ( target ) ! = self . len_bytes ( ) {
2021-03-31 21:37:41 +00:00
return Err ( PyBufferError ::new_err ( format! (
" slice to copy to (of length {}) does not match buffer length of {} " ,
target . len ( ) ,
self . item_count ( )
) ) ) ;
2017-01-20 22:14:29 +00:00
}
unsafe {
2018-07-30 21:01:46 +00:00
err ::error_on_minusone (
py ,
ffi ::PyBuffer_ToContiguous (
target . as_ptr ( ) as * mut raw ::c_void ,
& * self . 0 as * const ffi ::Py_buffer as * mut ffi ::Py_buffer ,
self . 0. len ,
2021-01-07 07:34:47 +00:00
fort as std ::os ::raw ::c_char ,
2018-07-30 21:01:46 +00:00
) ,
)
2017-01-20 22:14:29 +00:00
}
}
2017-01-20 23:40:32 +00:00
/// Copies the buffer elements to a newly allocated vector.
/// If the buffer is multi-dimensional, the elements are written in C-style order.
///
/// Fails if the buffer format is not compatible with type `T`.
2020-06-04 13:00:47 +00:00
pub fn to_vec ( & self , py : Python ) -> PyResult < Vec < T > > {
2017-01-20 23:40:32 +00:00
self . to_vec_impl ( py , b 'C' )
}
/// Copies the buffer elements to a newly allocated vector.
/// If the buffer is multi-dimensional, the elements are written in Fortran-style order.
///
/// Fails if the buffer format is not compatible with type `T`.
2020-06-04 13:00:47 +00:00
pub fn to_fortran_vec ( & self , py : Python ) -> PyResult < Vec < T > > {
2017-01-20 23:40:32 +00:00
self . to_vec_impl ( py , b 'F' )
}
2020-06-04 13:00:47 +00:00
fn to_vec_impl ( & self , py : Python , fort : u8 ) -> PyResult < Vec < T > > {
2017-01-20 23:40:32 +00:00
let item_count = self . item_count ( ) ;
let mut vec : Vec < T > = Vec ::with_capacity ( item_count ) ;
unsafe {
// Copy the buffer into the uninitialized space in the vector.
// Due to T:Copy, we don't need to be concerned with Drop impls.
2018-07-30 21:01:46 +00:00
err ::error_on_minusone (
py ,
ffi ::PyBuffer_ToContiguous (
vec . as_mut_ptr ( ) as * mut raw ::c_void ,
& * self . 0 as * const ffi ::Py_buffer as * mut ffi ::Py_buffer ,
self . 0. len ,
2021-01-07 07:34:47 +00:00
fort as std ::os ::raw ::c_char ,
2018-07-30 21:01:46 +00:00
) ,
) ? ;
2017-01-20 23:40:32 +00:00
// set vector length to mark the now-initialized space as usable
vec . set_len ( item_count ) ;
}
Ok ( vec )
}
2017-01-20 22:14:29 +00:00
/// Copies the specified slice into the buffer.
/// If the buffer is multi-dimensional, the elements in the slice are expected to be in C-style order.
///
/// * Fails if the buffer is read-only.
/// * Fails if the slice does not have the correct length (`buf.item_count()`).
/// * Fails if the buffer format is not compatible with type `T`.
///
/// To check whether the buffer format is compatible before calling this method,
/// use `<T as buffer::Element>::is_compatible_format(buf.format())`.
/// Alternatively, `match buffer::ElementType::from_format(buf.format())`.
2020-06-04 13:00:47 +00:00
pub fn copy_from_slice ( & self , py : Python , source : & [ T ] ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
self . copy_from_slice_impl ( py , source , b 'C' )
}
2017-06-24 08:47:36 +00:00
2017-01-20 22:14:29 +00:00
/// Copies the specified slice into the buffer.
/// If the buffer is multi-dimensional, the elements in the slice are expected to be in Fortran-style order.
///
/// * Fails if the buffer is read-only.
/// * Fails if the slice does not have the correct length (`buf.item_count()`).
/// * Fails if the buffer format is not compatible with type `T`.
///
/// To check whether the buffer format is compatible before calling this method,
/// use `<T as buffer::Element>::is_compatible_format(buf.format())`.
/// Alternatively, `match buffer::ElementType::from_format(buf.format())`.
2020-06-04 13:00:47 +00:00
pub fn copy_from_fortran_slice ( & self , py : Python , source : & [ T ] ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
self . copy_from_slice_impl ( py , source , b 'F' )
}
2017-06-24 08:47:36 +00:00
2020-06-04 13:00:47 +00:00
fn copy_from_slice_impl ( & self , py : Python , source : & [ T ] , fort : u8 ) -> PyResult < ( ) > {
2017-01-20 22:14:29 +00:00
if self . readonly ( ) {
2021-03-31 21:37:41 +00:00
return Err ( PyBufferError ::new_err ( " cannot write to read-only buffer " ) ) ;
} else if mem ::size_of_val ( source ) ! = self . len_bytes ( ) {
return Err ( PyBufferError ::new_err ( format! (
" slice to copy from (of length {}) does not match buffer length of {} " ,
source . len ( ) ,
self . item_count ( )
) ) ) ;
2017-01-20 22:14:29 +00:00
}
unsafe {
2018-07-30 21:01:46 +00:00
err ::error_on_minusone (
py ,
ffi ::PyBuffer_FromContiguous (
& * self . 0 as * const ffi ::Py_buffer as * mut ffi ::Py_buffer ,
source . as_ptr ( ) as * mut raw ::c_void ,
self . 0. len ,
2021-01-07 07:34:47 +00:00
fort as std ::os ::raw ::c_char ,
2018-07-30 21:01:46 +00:00
) ,
)
2017-01-20 22:14:29 +00:00
}
}
2017-07-20 16:11:28 +00:00
2018-01-19 18:02:36 +00:00
pub fn release ( self , _py : Python ) {
2020-02-28 20:03:15 +00:00
// First move self into a ManuallyDrop, so that PyBuffer::drop will
// never be called. (It would acquire the GIL and call PyBuffer_Release
// again.)
let mut mdself = mem ::ManuallyDrop ::new ( self ) ;
2017-07-20 16:11:28 +00:00
unsafe {
2020-02-28 20:03:15 +00:00
// Next, make the actual PyBuffer_Release call.
ffi ::PyBuffer_Release ( & mut * mdself . 0 ) ;
// Finally, drop the contained Pin<Box<_>> in place, to free the
// Box memory.
let inner : * mut Pin < Box < ffi ::Py_buffer > > = & mut mdself . 0 ;
ptr ::drop_in_place ( inner ) ;
}
2017-07-20 16:11:28 +00:00
}
2017-01-20 22:14:29 +00:00
}
2020-06-04 13:00:47 +00:00
impl < T > Drop for PyBuffer < T > {
2017-01-20 00:08:51 +00:00
fn drop ( & mut self ) {
2020-07-13 21:37:40 +00:00
Python ::with_gil ( | _ | unsafe { ffi ::PyBuffer_Release ( & mut * self . 0 ) } ) ;
2017-01-20 00:08:51 +00:00
}
}
2021-06-30 10:31:27 +00:00
/// Like [std::cell::Cell], but only provides read-only access to the data.
2017-01-20 22:14:29 +00:00
///
/// `&ReadOnlyCell<T>` is basically a safe version of `*const T`:
/// The data cannot be modified through the reference, but other references may
/// be modifying the data.
2018-07-13 16:10:09 +00:00
#[ repr(transparent) ]
2020-06-04 13:00:47 +00:00
pub struct ReadOnlyCell < T : Element > ( cell ::UnsafeCell < T > ) ;
2017-01-20 22:14:29 +00:00
2020-06-04 13:00:47 +00:00
impl < T : Element > ReadOnlyCell < T > {
2021-07-04 11:32:40 +00:00
/// Returns a copy of the current value.
2017-01-20 22:14:29 +00:00
#[ inline ]
pub fn get ( & self ) -> T {
unsafe { * self . 0. get ( ) }
}
2021-07-04 11:32:40 +00:00
/// Returns a pointer to the current value.
2017-01-20 22:14:29 +00:00
#[ inline ]
pub fn as_ptr ( & self ) -> * const T {
self . 0. get ( )
}
}
macro_rules ! impl_element (
( $t :ty , $f :ident ) = > {
unsafe impl Element for $t {
fn is_compatible_format ( format : & CStr ) -> bool {
let slice = format . to_bytes ( ) ;
if slice . len ( ) > 1 & & ! is_matching_endian ( slice [ 0 ] ) {
return false ;
}
ElementType ::from_format ( format ) = = ElementType ::$f { bytes : mem ::size_of ::< $t > ( ) }
}
}
}
) ;
impl_element! ( u8 , UnsignedInteger ) ;
impl_element! ( u16 , UnsignedInteger ) ;
impl_element! ( u32 , UnsignedInteger ) ;
impl_element! ( u64 , UnsignedInteger ) ;
impl_element! ( usize , UnsignedInteger ) ;
impl_element! ( i8 , SignedInteger ) ;
impl_element! ( i16 , SignedInteger ) ;
impl_element! ( i32 , SignedInteger ) ;
impl_element! ( i64 , SignedInteger ) ;
impl_element! ( isize , SignedInteger ) ;
impl_element! ( f32 , Float ) ;
impl_element! ( f64 , Float ) ;
2017-01-20 00:08:51 +00:00
2017-05-31 23:26:21 +00:00
#[ cfg(test) ]
2021-07-26 17:19:53 +00:00
mod tests {
2017-01-20 00:08:51 +00:00
use super ::PyBuffer ;
2018-11-12 20:36:08 +00:00
use crate ::ffi ;
2019-02-23 17:01:22 +00:00
use crate ::Python ;
2017-01-20 00:08:51 +00:00
2017-01-20 22:14:29 +00:00
#[ test ]
fn test_compatible_size ( ) {
// for the cast in PyBuffer::shape()
2018-07-30 21:01:46 +00:00
assert_eq! (
2018-11-12 20:36:08 +00:00
std ::mem ::size_of ::< ffi ::Py_ssize_t > ( ) ,
2018-07-30 21:01:46 +00:00
std ::mem ::size_of ::< usize > ( )
) ;
2017-01-20 22:14:29 +00:00
}
2017-01-20 00:08:51 +00:00
#[ test ]
fn test_bytes_buffer ( ) {
2021-08-12 00:47:41 +00:00
Python ::with_gil ( | py | {
let bytes = py . eval ( " b'abcde' " , None , None ) . unwrap ( ) ;
let buffer = PyBuffer ::get ( bytes ) . unwrap ( ) ;
assert_eq! ( buffer . dimensions ( ) , 1 ) ;
assert_eq! ( buffer . item_count ( ) , 5 ) ;
assert_eq! ( buffer . format ( ) . to_str ( ) . unwrap ( ) , " B " ) ;
assert_eq! ( buffer . shape ( ) , [ 5 ] ) ;
// single-dimensional buffer is always contiguous
assert! ( buffer . is_c_contiguous ( ) ) ;
assert! ( buffer . is_fortran_contiguous ( ) ) ;
let slice = buffer . as_slice ( py ) . unwrap ( ) ;
assert_eq! ( slice . len ( ) , 5 ) ;
assert_eq! ( slice [ 0 ] . get ( ) , b 'a' ) ;
assert_eq! ( slice [ 2 ] . get ( ) , b 'c' ) ;
assert! ( buffer . copy_to_slice ( py , & mut [ 0 u8 ] ) . is_err ( ) ) ;
let mut arr = [ 0 ; 5 ] ;
buffer . copy_to_slice ( py , & mut arr ) . unwrap ( ) ;
assert_eq! ( arr , b " abcde " as & [ u8 ] ) ;
assert! ( buffer . copy_from_slice ( py , & [ 0 u8 ; 5 ] ) . is_err ( ) ) ;
assert_eq! ( buffer . to_vec ( py ) . unwrap ( ) , b " abcde " ) ;
} ) ;
2017-01-20 00:08:51 +00:00
}
2019-08-17 12:10:36 +00:00
#[ allow(clippy::float_cmp) ] // The test wants to ensure that no precision was lost on the Python round-trip
2017-01-20 00:08:51 +00:00
#[ test ]
fn test_array_buffer ( ) {
2021-08-12 00:47:41 +00:00
Python ::with_gil ( | py | {
let array = py
. import ( " array " )
. unwrap ( )
. call_method ( " array " , ( " f " , ( 1.0 , 1.5 , 2.0 , 2.5 ) ) , None )
. unwrap ( ) ;
let buffer = PyBuffer ::get ( array ) . unwrap ( ) ;
assert_eq! ( buffer . dimensions ( ) , 1 ) ;
assert_eq! ( buffer . item_count ( ) , 4 ) ;
assert_eq! ( buffer . format ( ) . to_str ( ) . unwrap ( ) , " f " ) ;
assert_eq! ( buffer . shape ( ) , [ 4 ] ) ;
let slice = buffer . as_slice ( py ) . unwrap ( ) ;
assert_eq! ( slice . len ( ) , 4 ) ;
assert_eq! ( slice [ 0 ] . get ( ) , 1.0 ) ;
assert_eq! ( slice [ 3 ] . get ( ) , 2.5 ) ;
let mut_slice = buffer . as_mut_slice ( py ) . unwrap ( ) ;
assert_eq! ( mut_slice . len ( ) , 4 ) ;
assert_eq! ( mut_slice [ 0 ] . get ( ) , 1.0 ) ;
mut_slice [ 3 ] . set ( 2.75 ) ;
assert_eq! ( slice [ 3 ] . get ( ) , 2.75 ) ;
buffer
. copy_from_slice ( py , & [ 10.0 f32 , 11.0 , 12.0 , 13.0 ] )
. unwrap ( ) ;
assert_eq! ( slice [ 2 ] . get ( ) , 12.0 ) ;
assert_eq! ( buffer . to_vec ( py ) . unwrap ( ) , [ 10.0 , 11.0 , 12.0 , 13.0 ] ) ;
} ) ;
2017-01-20 00:08:51 +00:00
}
}