|
| 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