swadhinbiswas feat(py): implement PyO3 bindings with ASGI shim and native handler API · 7/8/2026
243e26c
60 lines · 2.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use pyo3::ffi;
use pyo3::prelude::*;
use std::os::raw::c_int;

#[pyclass]
pub struct ZeroCopyBuffer {
    data: Vec<u8>,
}

#[pymethods]
impl ZeroCopyBuffer {
    #[new]
    pub fn new(data: Vec<u8>) -> Self {
        ZeroCopyBuffer { data }
    }

    unsafe fn __getbuffer__(
        slf: PyRefMut<'_, Self>,
        view: *mut ffi::Py_buffer,
        _flags: c_int,
    ) -> PyResult<()> {
        // SAFETY: The `view` pointer is checked for null before any
        // dereference (line 22-23).  `Py_NewRef` is a safe FFI call that
        // increments the reference count of the Python object.  The `buf`
        // pointer points into `slf.data` which is a `Vec<u8>` and remains
        // valid for the lifetime of the `ZeroCopyBuffer`.  All other fields
        // (`readonly`, `itemsize`, `ndim`, `format`, `shape`, `strides`,
        // `suboffsets`, `internal`) are set to safe defaults that indicate
        // a flat, read-only, one-dimensional buffer of bytes.  The `unsafe`
        // qualifier is required by PyO3's buffer-protocol trait.
        if view.is_null() {
            return Err(pyo3::exceptions::PyBufferError::new_err("View is null"));
        }

        (*view).obj = ffi::Py_NewRef(slf.as_ptr());
        (*view).buf = slf.data.as_ptr() as *mut _;
        (*view).len = slf.data.len() as isize;
        (*view).readonly = 1;
        (*view).itemsize = 1;

        (*view).format = std::ptr::null_mut();
        (*view).ndim = 0;
        (*view).shape = std::ptr::null_mut();
        (*view).strides = std::ptr::null_mut();
        (*view).suboffsets = std::ptr::null_mut();
        (*view).internal = std::ptr::null_mut();

        Ok(())
    }

    unsafe fn __releasebuffer__(&self, _view: *mut ffi::Py_buffer) {
        // SAFETY: The `__releasebuffer__` is a no-op because no heap
        // allocations were made inside `__getbuffer__` that require
        // explicit cleanup (the buffer view directly references the
        // `Vec<u8>` owned by `ZeroCopyBuffer` which is dropped by Rust's
        // normal destructor).  The `unsafe` qualifier is required by
        // PyO3's buffer-protocol trait.
    }
}