-
Notifications
You must be signed in to change notification settings - Fork 953
Open
Description
We've got APIs that give you a memoryview given a Python object that implements the buffer protocol, but not one that gives you a memoryview just given a Py that stores some bytes!
I think it's possible to do this soundly!
As concrete, motivating examples:
- https://github.com/pyca/cryptography/blob/main/src/rust/src/x509/certificate.rs#L228-L231
- https://github.com/pyca/cryptography/blob/main/src/rust/src/x509/ocsp_resp.rs#L287-L292
These allocates new PyBytes every time they're accessed, doing allocations and copying all those bytes. Sad!
Here's a rough sketch of what I think can work:
impl PyMemoryView {
pub fn from_owned_buffer<'py, T>(
py: Python<'py>,
owner: Py<T>,
getbuf: impl for<'a> FnOnce(&'a T) -> &'a [u8],
) -> PyResult<Bound<'py, Self>>
where
T: PyClass<Frozen = True>,
{
let data: &T = owner.get();
let buf: &[u8] = getbuf(data);
let buf_ptr = buf.as_ptr();
let buf_len = buf.len();
let obj_ptr = owner.into_ptr();
let mut view: ffi::Py_buffer = unsafe { std::mem::zeroed() };
let rc = unsafe {
ffi::PyBuffer_FillInfo(
&mut view,
obj_ptr,
buf_ptr as *mut _,
buf_len as ffi::Py_ssize_t,
1, // readonly
ffi::PyBUF_FULL_RO,
)
};
if rc < 0 {
unsafe { ffi::Py_DECREF(obj_ptr); }
return Err(PyErr::fetch(py));
}
let mv_ptr = unsafe { ffi::PyMemoryView_FromBuffer(&view) };
if mv_ptr.is_null() {
// PyMemoryView_FromBuffer calls PyBuffer_Release on failure,
// which decrefs view.obj — so we do NOT decref again.
return Err(PyErr::fetch(py));
}
// PyBuffer_FillInfo already incref'd obj_ptr via view.obj,
// but we consumed a refcount from into_ptr() that nobody owns
// now. Decref the extra one.
unsafe { ffi::Py_DECREF(obj_ptr); }
unsafe {
Ok(Bound::from_owned_ptr(py, mv_ptr).downcast_into_unchecked())
}
}
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels