|
| 1 | +use pyo3::buffer::PyBuffer; |
1 | 2 | use pyo3::class::PyBufferProtocol; |
2 | 3 | use pyo3::exceptions::BufferError; |
3 | 4 | use pyo3::ffi; |
4 | 5 | use pyo3::prelude::*; |
5 | 6 | use pyo3::types::IntoPyDict; |
| 7 | +use pyo3::{AsPyPointer, PyClassShell}; |
6 | 8 | use std::ffi::CStr; |
7 | 9 | use std::os::raw::{c_int, c_void}; |
8 | 10 | use std::ptr; |
| 11 | +use std::sync::atomic::{AtomicBool, Ordering}; |
| 12 | +use std::sync::Arc; |
9 | 13 |
|
10 | 14 | #[pyclass] |
11 | | -struct TestClass { |
| 15 | +struct TestBufferClass { |
12 | 16 | vec: Vec<u8>, |
| 17 | + drop_called: Arc<AtomicBool>, |
13 | 18 | } |
14 | 19 |
|
15 | 20 | #[pyproto] |
16 | | -impl PyBufferProtocol for TestClass { |
17 | | - fn bf_getbuffer(&self, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()> { |
| 21 | +impl PyBufferProtocol for TestBufferClass { |
| 22 | + fn bf_getbuffer( |
| 23 | + slf: &mut PyClassShell<Self>, |
| 24 | + view: *mut ffi::Py_buffer, |
| 25 | + flags: c_int, |
| 26 | + ) -> PyResult<()> { |
18 | 27 | if view.is_null() { |
19 | 28 | return Err(BufferError::py_err("View is null")); |
20 | 29 | } |
21 | 30 |
|
22 | | - unsafe { |
23 | | - (*view).obj = ptr::null_mut(); |
24 | | - } |
25 | | - |
26 | 31 | if (flags & ffi::PyBUF_WRITABLE) == ffi::PyBUF_WRITABLE { |
27 | 32 | return Err(BufferError::py_err("Object is not writable")); |
28 | 33 | } |
29 | 34 |
|
30 | | - let bytes = &self.vec; |
| 35 | + unsafe { |
| 36 | + (*view).obj = slf.as_ptr(); |
| 37 | + ffi::Py_INCREF((*view).obj); |
| 38 | + } |
| 39 | + |
| 40 | + let bytes = &slf.vec; |
31 | 41 |
|
32 | 42 | unsafe { |
33 | 43 | (*view).buf = bytes.as_ptr() as *mut c_void; |
@@ -58,21 +68,68 @@ impl PyBufferProtocol for TestClass { |
58 | 68 |
|
59 | 69 | Ok(()) |
60 | 70 | } |
| 71 | + |
| 72 | + fn bf_releasebuffer(_slf: &mut PyClassShell<Self>, _view: *mut ffi::Py_buffer) -> PyResult<()> { |
| 73 | + Ok(()) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +impl Drop for TestBufferClass { |
| 78 | + fn drop(&mut self) { |
| 79 | + print!("dropped"); |
| 80 | + self.drop_called.store(true, Ordering::Relaxed); |
| 81 | + } |
61 | 82 | } |
62 | 83 |
|
63 | 84 | #[test] |
64 | 85 | fn test_buffer() { |
65 | | - let gil = Python::acquire_gil(); |
66 | | - let py = gil.python(); |
67 | | - |
68 | | - let t = Py::new( |
69 | | - py, |
70 | | - TestClass { |
71 | | - vec: vec![b' ', b'2', b'3'], |
72 | | - }, |
73 | | - ) |
74 | | - .unwrap(); |
75 | | - |
76 | | - let d = [("ob", t)].into_py_dict(py); |
77 | | - py.run("assert bytes(ob) == b' 23'", None, Some(d)).unwrap(); |
| 86 | + let drop_called = Arc::new(AtomicBool::new(false)); |
| 87 | + |
| 88 | + { |
| 89 | + let gil = Python::acquire_gil(); |
| 90 | + let py = gil.python(); |
| 91 | + let instance = Py::new( |
| 92 | + py, |
| 93 | + TestBufferClass { |
| 94 | + vec: vec![b' ', b'2', b'3'], |
| 95 | + drop_called: drop_called.clone(), |
| 96 | + }, |
| 97 | + ) |
| 98 | + .unwrap(); |
| 99 | + let env = [("ob", instance)].into_py_dict(py); |
| 100 | + py.run("assert bytes(ob) == b' 23'", None, Some(env)) |
| 101 | + .unwrap(); |
| 102 | + } |
| 103 | + |
| 104 | + assert!(drop_called.load(Ordering::Relaxed)); |
| 105 | +} |
| 106 | + |
| 107 | +#[test] |
| 108 | +fn test_buffer_referenced() { |
| 109 | + let drop_called = Arc::new(AtomicBool::new(false)); |
| 110 | + |
| 111 | + let buf = { |
| 112 | + let input = vec![b' ', b'2', b'3']; |
| 113 | + let gil = Python::acquire_gil(); |
| 114 | + let py = gil.python(); |
| 115 | + let instance: PyObject = TestBufferClass { |
| 116 | + vec: input.clone(), |
| 117 | + drop_called: drop_called.clone(), |
| 118 | + } |
| 119 | + .into_py(py); |
| 120 | + |
| 121 | + let buf = PyBuffer::get(py, instance.as_ref(py)).unwrap(); |
| 122 | + assert_eq!(buf.to_vec::<u8>(py).unwrap(), input); |
| 123 | + drop(instance); |
| 124 | + buf |
| 125 | + }; |
| 126 | + |
| 127 | + assert!(!drop_called.load(Ordering::Relaxed)); |
| 128 | + |
| 129 | + { |
| 130 | + let _py = Python::acquire_gil().python(); |
| 131 | + drop(buf); |
| 132 | + } |
| 133 | + |
| 134 | + assert!(drop_called.load(Ordering::Relaxed)); |
78 | 135 | } |
0 commit comments