rust/hg-cpython/src/pybytes_deref.rs
changeset 52163 7346f93be7a4
parent 49933 be3b545c5cff
child 52851 d9d6ae9b9722
equal deleted inserted replaced
52162:13815c9decd4 52163:7346f93be7a4
     1 use cpython::{PyBytes, Python};
     1 use crate::cpython::buffer::Element;
       
     2 use cpython::{
       
     3     buffer::PyBuffer, exc::ValueError, PyBytes, PyErr, PyResult, Python,
       
     4 };
     2 use stable_deref_trait::StableDeref;
     5 use stable_deref_trait::StableDeref;
     3 
     6 
     4 /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice
     7 /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice
     5 /// that borrows it. Implements `Deref<Target = [u8]>`.
     8 /// that borrows it. Implements `Deref<Target = [u8]>`.
     6 ///
     9 ///
    53 
    56 
    54 // Safety: PyBytes is Send. Raw pointers are not by default,
    57 // Safety: PyBytes is Send. Raw pointers are not by default,
    55 // but here sending one to another thread is fine since we ensure it stays
    58 // but here sending one to another thread is fine since we ensure it stays
    56 // valid.
    59 // valid.
    57 unsafe impl Send for PyBytesDeref {}
    60 unsafe impl Send for PyBytesDeref {}
       
    61 
       
    62 ///
       
    63 /// It also enables using a (wrapped) `PyBuffer` in GIL-unaware generic code.
       
    64 pub struct PyBufferDeref {
       
    65     #[allow(unused)]
       
    66     keep_alive: PyBuffer,
       
    67 
       
    68     /// Borrows the buffer inside `self.keep_alive`,
       
    69     /// but the borrow-checker cannot express self-referential structs.
       
    70     data: *const [u8],
       
    71 }
       
    72 
       
    73 fn get_buffer<'a>(py: Python, buf: &'a PyBuffer) -> PyResult<&'a [u8]> {
       
    74     let len = buf.item_count();
       
    75 
       
    76     let cbuf = buf.buf_ptr();
       
    77     let has_correct_item_size = std::mem::size_of::<u8>() == buf.item_size();
       
    78     let is_valid_buffer = has_correct_item_size
       
    79         && buf.is_c_contiguous()
       
    80         && u8::is_compatible_format(buf.format())
       
    81         && buf.readonly();
       
    82 
       
    83     let bytes = if is_valid_buffer {
       
    84         unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
       
    85     } else {
       
    86         return Err(PyErr::new::<ValueError, _>(
       
    87             py,
       
    88             "Buffer has an invalid memory representation",
       
    89         ));
       
    90     };
       
    91     Ok(bytes)
       
    92 }
       
    93 
       
    94 impl PyBufferDeref {
       
    95     pub fn new(py: Python, buf: PyBuffer) -> PyResult<Self> {
       
    96         Ok(Self {
       
    97             data: get_buffer(py, &buf)?,
       
    98             keep_alive: buf,
       
    99         })
       
   100     }
       
   101 }
       
   102 
       
   103 impl std::ops::Deref for PyBufferDeref {
       
   104     type Target = [u8];
       
   105 
       
   106     fn deref(&self) -> &[u8] {
       
   107         // Safety: the raw pointer is valid as long as the PyBuffer is still
       
   108         // alive, and the returned slice borrows `self`.
       
   109         unsafe { &*self.data }
       
   110     }
       
   111 }
       
   112 
       
   113 unsafe impl StableDeref for PyBufferDeref {}
       
   114 
       
   115 #[allow(unused)]
       
   116 fn static_assert_pybuffer_is_send() {
       
   117     #[allow(clippy::no_effect)]
       
   118     require_send::<PyBuffer>;
       
   119 }
       
   120 
       
   121 // Safety: PyBuffer is Send. Raw pointers are not by default,
       
   122 // but here sending one to another thread is fine since we ensure it stays
       
   123 // valid.
       
   124 unsafe impl Send for PyBufferDeref {}