##// END OF EJS Templates
rust-cpython: pair leaked reference with its manager object...
Yuya Nishihara -
r43116:8f549c46 default
parent child Browse files
Show More
@@ -1,126 +1,127 b''
1 // dirs_multiset.rs
1 // dirs_multiset.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13
13
14 use cpython::{
14 use cpython::{
15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
15 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
16 Python,
16 Python,
17 };
17 };
18
18
19 use crate::dirstate::extract_dirstate;
19 use crate::dirstate::extract_dirstate;
20 use crate::ref_sharing::{PySharedRefCell, PySharedState};
20 use crate::ref_sharing::{PySharedRefCell, PySharedState};
21 use hg::{DirsMultiset, DirstateMapError, DirstateParseError, EntryState};
21 use hg::{DirsMultiset, DirstateMapError, DirstateParseError, EntryState};
22
22
23 py_class!(pub class Dirs |py| {
23 py_class!(pub class Dirs |py| {
24 data inner: PySharedRefCell<DirsMultiset>;
24 data inner: PySharedRefCell<DirsMultiset>;
25 data py_shared_state: PySharedState;
25 data py_shared_state: PySharedState;
26
26
27 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
27 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
28 // a `list`)
28 // a `list`)
29 def __new__(
29 def __new__(
30 _cls,
30 _cls,
31 map: PyObject,
31 map: PyObject,
32 skip: Option<PyObject> = None
32 skip: Option<PyObject> = None
33 ) -> PyResult<Self> {
33 ) -> PyResult<Self> {
34 let mut skip_state: Option<EntryState> = None;
34 let mut skip_state: Option<EntryState> = None;
35 if let Some(skip) = skip {
35 if let Some(skip) = skip {
36 skip_state = Some(
36 skip_state = Some(
37 skip.extract::<PyBytes>(py)?.data(py)[0]
37 skip.extract::<PyBytes>(py)?.data(py)[0]
38 .try_into()
38 .try_into()
39 .map_err(|e: DirstateParseError| {
39 .map_err(|e: DirstateParseError| {
40 PyErr::new::<exc::ValueError, _>(py, e.to_string())
40 PyErr::new::<exc::ValueError, _>(py, e.to_string())
41 })?,
41 })?,
42 );
42 );
43 }
43 }
44 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
44 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
45 let dirstate = extract_dirstate(py, &map)?;
45 let dirstate = extract_dirstate(py, &map)?;
46 DirsMultiset::from_dirstate(&dirstate, skip_state)
46 DirsMultiset::from_dirstate(&dirstate, skip_state)
47 } else {
47 } else {
48 let map: Result<Vec<Vec<u8>>, PyErr> = map
48 let map: Result<Vec<Vec<u8>>, PyErr> = map
49 .iter(py)?
49 .iter(py)?
50 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
50 .map(|o| Ok(o?.extract::<PyBytes>(py)?.data(py).to_owned()))
51 .collect();
51 .collect();
52 DirsMultiset::from_manifest(&map?)
52 DirsMultiset::from_manifest(&map?)
53 };
53 };
54
54
55 Self::create_instance(
55 Self::create_instance(
56 py,
56 py,
57 PySharedRefCell::new(inner),
57 PySharedRefCell::new(inner),
58 PySharedState::default()
58 PySharedState::default()
59 )
59 )
60 }
60 }
61
61
62 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
62 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
63 self.borrow_mut(py)?.add_path(
63 self.borrow_mut(py)?.add_path(
64 path.extract::<PyBytes>(py)?.data(py),
64 path.extract::<PyBytes>(py)?.data(py),
65 );
65 );
66 Ok(py.None())
66 Ok(py.None())
67 }
67 }
68
68
69 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
69 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
70 self.borrow_mut(py)?.delete_path(
70 self.borrow_mut(py)?.delete_path(
71 path.extract::<PyBytes>(py)?.data(py),
71 path.extract::<PyBytes>(py)?.data(py),
72 )
72 )
73 .and(Ok(py.None()))
73 .and(Ok(py.None()))
74 .or_else(|e| {
74 .or_else(|e| {
75 match e {
75 match e {
76 DirstateMapError::PathNotFound(_p) => {
76 DirstateMapError::PathNotFound(_p) => {
77 Err(PyErr::new::<exc::ValueError, _>(
77 Err(PyErr::new::<exc::ValueError, _>(
78 py,
78 py,
79 "expected a value, found none".to_string(),
79 "expected a value, found none".to_string(),
80 ))
80 ))
81 }
81 }
82 DirstateMapError::EmptyPath => {
82 DirstateMapError::EmptyPath => {
83 Ok(py.None())
83 Ok(py.None())
84 }
84 }
85 }
85 }
86 })
86 })
87 }
87 }
88 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
88 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
89 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
89 DirsMultisetKeysIterator::create_instance(
90 DirsMultisetKeysIterator::create_instance(
90 py,
91 py,
91 RefCell::new(Some(DirsMultisetLeakedRef::new(py, &self))),
92 RefCell::new(Some(leak_handle)),
92 RefCell::new(Box::new(self.leak_immutable(py)?.iter())),
93 RefCell::new(Box::new(leaked_ref.iter())),
93 )
94 )
94 }
95 }
95
96
96 def __contains__(&self, item: PyObject) -> PyResult<bool> {
97 def __contains__(&self, item: PyObject) -> PyResult<bool> {
97 Ok(self
98 Ok(self
98 .inner(py)
99 .inner(py)
99 .borrow()
100 .borrow()
100 .contains(item.extract::<PyBytes>(py)?.data(py).as_ref()))
101 .contains(item.extract::<PyBytes>(py)?.data(py).as_ref()))
101 }
102 }
102 });
103 });
103
104
104 py_shared_ref!(Dirs, DirsMultiset, inner, DirsMultisetLeakedRef,);
105 py_shared_ref!(Dirs, DirsMultiset, inner, DirsMultisetLeakedRef,);
105
106
106 impl Dirs {
107 impl Dirs {
107 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
108 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
108 Self::create_instance(
109 Self::create_instance(
109 py,
110 py,
110 PySharedRefCell::new(d),
111 PySharedRefCell::new(d),
111 PySharedState::default(),
112 PySharedState::default(),
112 )
113 )
113 }
114 }
114
115
115 fn translate_key(py: Python, res: &Vec<u8>) -> PyResult<Option<PyBytes>> {
116 fn translate_key(py: Python, res: &Vec<u8>) -> PyResult<Option<PyBytes>> {
116 Ok(Some(PyBytes::new(py, res)))
117 Ok(Some(PyBytes::new(py, res)))
117 }
118 }
118 }
119 }
119
120
120 py_shared_sequence_iterator!(
121 py_shared_sequence_iterator!(
121 DirsMultisetKeysIterator,
122 DirsMultisetKeysIterator,
122 DirsMultisetLeakedRef,
123 DirsMultisetLeakedRef,
123 Vec<u8>,
124 Vec<u8>,
124 Dirs::translate_key,
125 Dirs::translate_key,
125 Option<PyBytes>
126 Option<PyBytes>
126 );
127 );
@@ -1,505 +1,510 b''
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13 use std::time::Duration;
13 use std::time::Duration;
14
14
15 use cpython::{
15 use cpython::{
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
16 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
17 PyResult, PyTuple, Python, PythonObject, ToPyObject,
18 };
18 };
19 use libc::c_char;
19 use libc::c_char;
20
20
21 use crate::{
21 use crate::{
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
22 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
23 dirstate::{decapsule_make_dirstate_tuple, dirs_multiset::Dirs},
24 ref_sharing::{PySharedRefCell, PySharedState},
24 ref_sharing::{PySharedRefCell, PySharedState},
25 };
25 };
26 use hg::{
26 use hg::{
27 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
27 DirsMultiset, DirstateEntry, DirstateMap as RustDirstateMap,
28 DirstateParents, DirstateParseError, EntryState, PARENT_SIZE,
28 DirstateParents, DirstateParseError, EntryState, PARENT_SIZE,
29 };
29 };
30
30
31 // TODO
31 // TODO
32 // This object needs to share references to multiple members of its Rust
32 // This object needs to share references to multiple members of its Rust
33 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
33 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
34 // Right now `CopyMap` is done, but it needs to have an explicit reference
34 // Right now `CopyMap` is done, but it needs to have an explicit reference
35 // to `RustDirstateMap` which itself needs to have an encapsulation for
35 // to `RustDirstateMap` which itself needs to have an encapsulation for
36 // every method in `CopyMap` (copymapcopy, etc.).
36 // every method in `CopyMap` (copymapcopy, etc.).
37 // This is ugly and hard to maintain.
37 // This is ugly and hard to maintain.
38 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
38 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
39 // `py_class!` is already implemented and does not mention
39 // `py_class!` is already implemented and does not mention
40 // `RustDirstateMap`, rightfully so.
40 // `RustDirstateMap`, rightfully so.
41 // All attributes also have to have a separate refcount data attribute for
41 // All attributes also have to have a separate refcount data attribute for
42 // leaks, with all methods that go along for reference sharing.
42 // leaks, with all methods that go along for reference sharing.
43 py_class!(pub class DirstateMap |py| {
43 py_class!(pub class DirstateMap |py| {
44 data inner: PySharedRefCell<RustDirstateMap>;
44 data inner: PySharedRefCell<RustDirstateMap>;
45 data py_shared_state: PySharedState;
45 data py_shared_state: PySharedState;
46
46
47 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
47 def __new__(_cls, _root: PyObject) -> PyResult<Self> {
48 let inner = RustDirstateMap::default();
48 let inner = RustDirstateMap::default();
49 Self::create_instance(
49 Self::create_instance(
50 py,
50 py,
51 PySharedRefCell::new(inner),
51 PySharedRefCell::new(inner),
52 PySharedState::default()
52 PySharedState::default()
53 )
53 )
54 }
54 }
55
55
56 def clear(&self) -> PyResult<PyObject> {
56 def clear(&self) -> PyResult<PyObject> {
57 self.borrow_mut(py)?.clear();
57 self.borrow_mut(py)?.clear();
58 Ok(py.None())
58 Ok(py.None())
59 }
59 }
60
60
61 def get(
61 def get(
62 &self,
62 &self,
63 key: PyObject,
63 key: PyObject,
64 default: Option<PyObject> = None
64 default: Option<PyObject> = None
65 ) -> PyResult<Option<PyObject>> {
65 ) -> PyResult<Option<PyObject>> {
66 let key = key.extract::<PyBytes>(py)?;
66 let key = key.extract::<PyBytes>(py)?;
67 match self.inner(py).borrow().get(key.data(py)) {
67 match self.inner(py).borrow().get(key.data(py)) {
68 Some(entry) => {
68 Some(entry) => {
69 // Explicitly go through u8 first, then cast to
69 // Explicitly go through u8 first, then cast to
70 // platform-specific `c_char`.
70 // platform-specific `c_char`.
71 let state: u8 = entry.state.into();
71 let state: u8 = entry.state.into();
72 Ok(Some(decapsule_make_dirstate_tuple(py)?(
72 Ok(Some(decapsule_make_dirstate_tuple(py)?(
73 state as c_char,
73 state as c_char,
74 entry.mode,
74 entry.mode,
75 entry.size,
75 entry.size,
76 entry.mtime,
76 entry.mtime,
77 )))
77 )))
78 },
78 },
79 None => Ok(default)
79 None => Ok(default)
80 }
80 }
81 }
81 }
82
82
83 def addfile(
83 def addfile(
84 &self,
84 &self,
85 f: PyObject,
85 f: PyObject,
86 oldstate: PyObject,
86 oldstate: PyObject,
87 state: PyObject,
87 state: PyObject,
88 mode: PyObject,
88 mode: PyObject,
89 size: PyObject,
89 size: PyObject,
90 mtime: PyObject
90 mtime: PyObject
91 ) -> PyResult<PyObject> {
91 ) -> PyResult<PyObject> {
92 self.borrow_mut(py)?.add_file(
92 self.borrow_mut(py)?.add_file(
93 f.extract::<PyBytes>(py)?.data(py),
93 f.extract::<PyBytes>(py)?.data(py),
94 oldstate.extract::<PyBytes>(py)?.data(py)[0]
94 oldstate.extract::<PyBytes>(py)?.data(py)[0]
95 .try_into()
95 .try_into()
96 .map_err(|e: DirstateParseError| {
96 .map_err(|e: DirstateParseError| {
97 PyErr::new::<exc::ValueError, _>(py, e.to_string())
97 PyErr::new::<exc::ValueError, _>(py, e.to_string())
98 })?,
98 })?,
99 DirstateEntry {
99 DirstateEntry {
100 state: state.extract::<PyBytes>(py)?.data(py)[0]
100 state: state.extract::<PyBytes>(py)?.data(py)[0]
101 .try_into()
101 .try_into()
102 .map_err(|e: DirstateParseError| {
102 .map_err(|e: DirstateParseError| {
103 PyErr::new::<exc::ValueError, _>(py, e.to_string())
103 PyErr::new::<exc::ValueError, _>(py, e.to_string())
104 })?,
104 })?,
105 mode: mode.extract(py)?,
105 mode: mode.extract(py)?,
106 size: size.extract(py)?,
106 size: size.extract(py)?,
107 mtime: mtime.extract(py)?,
107 mtime: mtime.extract(py)?,
108 },
108 },
109 );
109 );
110 Ok(py.None())
110 Ok(py.None())
111 }
111 }
112
112
113 def removefile(
113 def removefile(
114 &self,
114 &self,
115 f: PyObject,
115 f: PyObject,
116 oldstate: PyObject,
116 oldstate: PyObject,
117 size: PyObject
117 size: PyObject
118 ) -> PyResult<PyObject> {
118 ) -> PyResult<PyObject> {
119 self.borrow_mut(py)?
119 self.borrow_mut(py)?
120 .remove_file(
120 .remove_file(
121 f.extract::<PyBytes>(py)?.data(py),
121 f.extract::<PyBytes>(py)?.data(py),
122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
122 oldstate.extract::<PyBytes>(py)?.data(py)[0]
123 .try_into()
123 .try_into()
124 .map_err(|e: DirstateParseError| {
124 .map_err(|e: DirstateParseError| {
125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
125 PyErr::new::<exc::ValueError, _>(py, e.to_string())
126 })?,
126 })?,
127 size.extract(py)?,
127 size.extract(py)?,
128 )
128 )
129 .or_else(|_| {
129 .or_else(|_| {
130 Err(PyErr::new::<exc::OSError, _>(
130 Err(PyErr::new::<exc::OSError, _>(
131 py,
131 py,
132 "Dirstate error".to_string(),
132 "Dirstate error".to_string(),
133 ))
133 ))
134 })?;
134 })?;
135 Ok(py.None())
135 Ok(py.None())
136 }
136 }
137
137
138 def dropfile(
138 def dropfile(
139 &self,
139 &self,
140 f: PyObject,
140 f: PyObject,
141 oldstate: PyObject
141 oldstate: PyObject
142 ) -> PyResult<PyBool> {
142 ) -> PyResult<PyBool> {
143 self.borrow_mut(py)?
143 self.borrow_mut(py)?
144 .drop_file(
144 .drop_file(
145 f.extract::<PyBytes>(py)?.data(py),
145 f.extract::<PyBytes>(py)?.data(py),
146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
146 oldstate.extract::<PyBytes>(py)?.data(py)[0]
147 .try_into()
147 .try_into()
148 .map_err(|e: DirstateParseError| {
148 .map_err(|e: DirstateParseError| {
149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
149 PyErr::new::<exc::ValueError, _>(py, e.to_string())
150 })?,
150 })?,
151 )
151 )
152 .and_then(|b| Ok(b.to_py_object(py)))
152 .and_then(|b| Ok(b.to_py_object(py)))
153 .or_else(|_| {
153 .or_else(|_| {
154 Err(PyErr::new::<exc::OSError, _>(
154 Err(PyErr::new::<exc::OSError, _>(
155 py,
155 py,
156 "Dirstate error".to_string(),
156 "Dirstate error".to_string(),
157 ))
157 ))
158 })
158 })
159 }
159 }
160
160
161 def clearambiguoustimes(
161 def clearambiguoustimes(
162 &self,
162 &self,
163 files: PyObject,
163 files: PyObject,
164 now: PyObject
164 now: PyObject
165 ) -> PyResult<PyObject> {
165 ) -> PyResult<PyObject> {
166 let files: PyResult<Vec<Vec<u8>>> = files
166 let files: PyResult<Vec<Vec<u8>>> = files
167 .iter(py)?
167 .iter(py)?
168 .map(|filename| {
168 .map(|filename| {
169 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
169 Ok(filename?.extract::<PyBytes>(py)?.data(py).to_owned())
170 })
170 })
171 .collect();
171 .collect();
172 self.borrow_mut(py)?
172 self.borrow_mut(py)?
173 .clear_ambiguous_times(files?, now.extract(py)?);
173 .clear_ambiguous_times(files?, now.extract(py)?);
174 Ok(py.None())
174 Ok(py.None())
175 }
175 }
176
176
177 // TODO share the reference
177 // TODO share the reference
178 def nonnormalentries(&self) -> PyResult<PyObject> {
178 def nonnormalentries(&self) -> PyResult<PyObject> {
179 let (non_normal, other_parent) =
179 let (non_normal, other_parent) =
180 self.inner(py).borrow().non_normal_other_parent_entries();
180 self.inner(py).borrow().non_normal_other_parent_entries();
181
181
182 let locals = PyDict::new(py);
182 let locals = PyDict::new(py);
183 locals.set_item(
183 locals.set_item(
184 py,
184 py,
185 "non_normal",
185 "non_normal",
186 non_normal
186 non_normal
187 .iter()
187 .iter()
188 .map(|v| PyBytes::new(py, &v))
188 .map(|v| PyBytes::new(py, &v))
189 .collect::<Vec<PyBytes>>()
189 .collect::<Vec<PyBytes>>()
190 .to_py_object(py),
190 .to_py_object(py),
191 )?;
191 )?;
192 locals.set_item(
192 locals.set_item(
193 py,
193 py,
194 "other_parent",
194 "other_parent",
195 other_parent
195 other_parent
196 .iter()
196 .iter()
197 .map(|v| PyBytes::new(py, &v))
197 .map(|v| PyBytes::new(py, &v))
198 .collect::<Vec<PyBytes>>()
198 .collect::<Vec<PyBytes>>()
199 .to_py_object(py),
199 .to_py_object(py),
200 )?;
200 )?;
201
201
202 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
202 py.eval("set(non_normal), set(other_parent)", None, Some(&locals))
203 }
203 }
204
204
205 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
205 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
206 let d = d.extract::<PyBytes>(py)?;
206 let d = d.extract::<PyBytes>(py)?;
207 Ok(self.borrow_mut(py)?
207 Ok(self.borrow_mut(py)?
208 .has_tracked_dir(d.data(py))
208 .has_tracked_dir(d.data(py))
209 .to_py_object(py))
209 .to_py_object(py))
210 }
210 }
211
211
212 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
212 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
213 let d = d.extract::<PyBytes>(py)?;
213 let d = d.extract::<PyBytes>(py)?;
214 Ok(self.borrow_mut(py)?
214 Ok(self.borrow_mut(py)?
215 .has_dir(d.data(py))
215 .has_dir(d.data(py))
216 .to_py_object(py))
216 .to_py_object(py))
217 }
217 }
218
218
219 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
219 def parents(&self, st: PyObject) -> PyResult<PyTuple> {
220 self.borrow_mut(py)?
220 self.borrow_mut(py)?
221 .parents(st.extract::<PyBytes>(py)?.data(py))
221 .parents(st.extract::<PyBytes>(py)?.data(py))
222 .and_then(|d| {
222 .and_then(|d| {
223 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
223 Ok((PyBytes::new(py, &d.p1), PyBytes::new(py, &d.p2))
224 .to_py_object(py))
224 .to_py_object(py))
225 })
225 })
226 .or_else(|_| {
226 .or_else(|_| {
227 Err(PyErr::new::<exc::OSError, _>(
227 Err(PyErr::new::<exc::OSError, _>(
228 py,
228 py,
229 "Dirstate error".to_string(),
229 "Dirstate error".to_string(),
230 ))
230 ))
231 })
231 })
232 }
232 }
233
233
234 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
234 def setparents(&self, p1: PyObject, p2: PyObject) -> PyResult<PyObject> {
235 let p1 = extract_node_id(py, &p1)?;
235 let p1 = extract_node_id(py, &p1)?;
236 let p2 = extract_node_id(py, &p2)?;
236 let p2 = extract_node_id(py, &p2)?;
237
237
238 self.borrow_mut(py)?
238 self.borrow_mut(py)?
239 .set_parents(&DirstateParents { p1, p2 });
239 .set_parents(&DirstateParents { p1, p2 });
240 Ok(py.None())
240 Ok(py.None())
241 }
241 }
242
242
243 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
243 def read(&self, st: PyObject) -> PyResult<Option<PyObject>> {
244 match self.borrow_mut(py)?
244 match self.borrow_mut(py)?
245 .read(st.extract::<PyBytes>(py)?.data(py))
245 .read(st.extract::<PyBytes>(py)?.data(py))
246 {
246 {
247 Ok(Some(parents)) => Ok(Some(
247 Ok(Some(parents)) => Ok(Some(
248 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
248 (PyBytes::new(py, &parents.p1), PyBytes::new(py, &parents.p2))
249 .to_py_object(py)
249 .to_py_object(py)
250 .into_object(),
250 .into_object(),
251 )),
251 )),
252 Ok(None) => Ok(Some(py.None())),
252 Ok(None) => Ok(Some(py.None())),
253 Err(_) => Err(PyErr::new::<exc::OSError, _>(
253 Err(_) => Err(PyErr::new::<exc::OSError, _>(
254 py,
254 py,
255 "Dirstate error".to_string(),
255 "Dirstate error".to_string(),
256 )),
256 )),
257 }
257 }
258 }
258 }
259 def write(
259 def write(
260 &self,
260 &self,
261 p1: PyObject,
261 p1: PyObject,
262 p2: PyObject,
262 p2: PyObject,
263 now: PyObject
263 now: PyObject
264 ) -> PyResult<PyBytes> {
264 ) -> PyResult<PyBytes> {
265 let now = Duration::new(now.extract(py)?, 0);
265 let now = Duration::new(now.extract(py)?, 0);
266 let parents = DirstateParents {
266 let parents = DirstateParents {
267 p1: extract_node_id(py, &p1)?,
267 p1: extract_node_id(py, &p1)?,
268 p2: extract_node_id(py, &p2)?,
268 p2: extract_node_id(py, &p2)?,
269 };
269 };
270
270
271 match self.borrow_mut(py)?.pack(parents, now) {
271 match self.borrow_mut(py)?.pack(parents, now) {
272 Ok(packed) => Ok(PyBytes::new(py, &packed)),
272 Ok(packed) => Ok(PyBytes::new(py, &packed)),
273 Err(_) => Err(PyErr::new::<exc::OSError, _>(
273 Err(_) => Err(PyErr::new::<exc::OSError, _>(
274 py,
274 py,
275 "Dirstate error".to_string(),
275 "Dirstate error".to_string(),
276 )),
276 )),
277 }
277 }
278 }
278 }
279
279
280 def filefoldmapasdict(&self) -> PyResult<PyDict> {
280 def filefoldmapasdict(&self) -> PyResult<PyDict> {
281 let dict = PyDict::new(py);
281 let dict = PyDict::new(py);
282 for (key, value) in
282 for (key, value) in
283 self.borrow_mut(py)?.build_file_fold_map().iter()
283 self.borrow_mut(py)?.build_file_fold_map().iter()
284 {
284 {
285 dict.set_item(py, key, value)?;
285 dict.set_item(py, key, value)?;
286 }
286 }
287 Ok(dict)
287 Ok(dict)
288 }
288 }
289
289
290 def __len__(&self) -> PyResult<usize> {
290 def __len__(&self) -> PyResult<usize> {
291 Ok(self.inner(py).borrow().len())
291 Ok(self.inner(py).borrow().len())
292 }
292 }
293
293
294 def __contains__(&self, key: PyObject) -> PyResult<bool> {
294 def __contains__(&self, key: PyObject) -> PyResult<bool> {
295 let key = key.extract::<PyBytes>(py)?;
295 let key = key.extract::<PyBytes>(py)?;
296 Ok(self.inner(py).borrow().contains_key(key.data(py)))
296 Ok(self.inner(py).borrow().contains_key(key.data(py)))
297 }
297 }
298
298
299 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
299 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
300 let key = key.extract::<PyBytes>(py)?;
300 let key = key.extract::<PyBytes>(py)?;
301 let key = key.data(py);
301 let key = key.data(py);
302 match self.inner(py).borrow().get(key) {
302 match self.inner(py).borrow().get(key) {
303 Some(entry) => {
303 Some(entry) => {
304 // Explicitly go through u8 first, then cast to
304 // Explicitly go through u8 first, then cast to
305 // platform-specific `c_char`.
305 // platform-specific `c_char`.
306 let state: u8 = entry.state.into();
306 let state: u8 = entry.state.into();
307 Ok(decapsule_make_dirstate_tuple(py)?(
307 Ok(decapsule_make_dirstate_tuple(py)?(
308 state as c_char,
308 state as c_char,
309 entry.mode,
309 entry.mode,
310 entry.size,
310 entry.size,
311 entry.mtime,
311 entry.mtime,
312 ))
312 ))
313 },
313 },
314 None => Err(PyErr::new::<exc::KeyError, _>(
314 None => Err(PyErr::new::<exc::KeyError, _>(
315 py,
315 py,
316 String::from_utf8_lossy(key),
316 String::from_utf8_lossy(key),
317 )),
317 )),
318 }
318 }
319 }
319 }
320
320
321 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
321 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
322 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
322 DirstateMapKeysIterator::from_inner(
323 DirstateMapKeysIterator::from_inner(
323 py,
324 py,
324 Some(DirstateMapLeakedRef::new(py, &self)),
325 Some(leak_handle),
325 Box::new(self.leak_immutable(py)?.iter()),
326 Box::new(leaked_ref.iter()),
326 )
327 )
327 }
328 }
328
329
329 def items(&self) -> PyResult<DirstateMapItemsIterator> {
330 def items(&self) -> PyResult<DirstateMapItemsIterator> {
331 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
330 DirstateMapItemsIterator::from_inner(
332 DirstateMapItemsIterator::from_inner(
331 py,
333 py,
332 Some(DirstateMapLeakedRef::new(py, &self)),
334 Some(leak_handle),
333 Box::new(self.leak_immutable(py)?.iter()),
335 Box::new(leaked_ref.iter()),
334 )
336 )
335 }
337 }
336
338
337 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
339 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
340 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
338 DirstateMapKeysIterator::from_inner(
341 DirstateMapKeysIterator::from_inner(
339 py,
342 py,
340 Some(DirstateMapLeakedRef::new(py, &self)),
343 Some(leak_handle),
341 Box::new(self.leak_immutable(py)?.iter()),
344 Box::new(leaked_ref.iter()),
342 )
345 )
343 }
346 }
344
347
345 def getdirs(&self) -> PyResult<Dirs> {
348 def getdirs(&self) -> PyResult<Dirs> {
346 // TODO don't copy, share the reference
349 // TODO don't copy, share the reference
347 self.borrow_mut(py)?.set_dirs();
350 self.borrow_mut(py)?.set_dirs();
348 Dirs::from_inner(
351 Dirs::from_inner(
349 py,
352 py,
350 DirsMultiset::from_dirstate(
353 DirsMultiset::from_dirstate(
351 &self.inner(py).borrow(),
354 &self.inner(py).borrow(),
352 Some(EntryState::Removed),
355 Some(EntryState::Removed),
353 ),
356 ),
354 )
357 )
355 }
358 }
356 def getalldirs(&self) -> PyResult<Dirs> {
359 def getalldirs(&self) -> PyResult<Dirs> {
357 // TODO don't copy, share the reference
360 // TODO don't copy, share the reference
358 self.borrow_mut(py)?.set_all_dirs();
361 self.borrow_mut(py)?.set_all_dirs();
359 Dirs::from_inner(
362 Dirs::from_inner(
360 py,
363 py,
361 DirsMultiset::from_dirstate(
364 DirsMultiset::from_dirstate(
362 &self.inner(py).borrow(),
365 &self.inner(py).borrow(),
363 None,
366 None,
364 ),
367 ),
365 )
368 )
366 }
369 }
367
370
368 // TODO all copymap* methods, see docstring above
371 // TODO all copymap* methods, see docstring above
369 def copymapcopy(&self) -> PyResult<PyDict> {
372 def copymapcopy(&self) -> PyResult<PyDict> {
370 let dict = PyDict::new(py);
373 let dict = PyDict::new(py);
371 for (key, value) in self.inner(py).borrow().copy_map.iter() {
374 for (key, value) in self.inner(py).borrow().copy_map.iter() {
372 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
375 dict.set_item(py, PyBytes::new(py, key), PyBytes::new(py, value))?;
373 }
376 }
374 Ok(dict)
377 Ok(dict)
375 }
378 }
376
379
377 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
380 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
378 let key = key.extract::<PyBytes>(py)?;
381 let key = key.extract::<PyBytes>(py)?;
379 match self.inner(py).borrow().copy_map.get(key.data(py)) {
382 match self.inner(py).borrow().copy_map.get(key.data(py)) {
380 Some(copy) => Ok(PyBytes::new(py, copy)),
383 Some(copy) => Ok(PyBytes::new(py, copy)),
381 None => Err(PyErr::new::<exc::KeyError, _>(
384 None => Err(PyErr::new::<exc::KeyError, _>(
382 py,
385 py,
383 String::from_utf8_lossy(key.data(py)),
386 String::from_utf8_lossy(key.data(py)),
384 )),
387 )),
385 }
388 }
386 }
389 }
387 def copymap(&self) -> PyResult<CopyMap> {
390 def copymap(&self) -> PyResult<CopyMap> {
388 CopyMap::from_inner(py, self.clone_ref(py))
391 CopyMap::from_inner(py, self.clone_ref(py))
389 }
392 }
390
393
391 def copymaplen(&self) -> PyResult<usize> {
394 def copymaplen(&self) -> PyResult<usize> {
392 Ok(self.inner(py).borrow().copy_map.len())
395 Ok(self.inner(py).borrow().copy_map.len())
393 }
396 }
394 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
397 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
395 let key = key.extract::<PyBytes>(py)?;
398 let key = key.extract::<PyBytes>(py)?;
396 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
399 Ok(self.inner(py).borrow().copy_map.contains_key(key.data(py)))
397 }
400 }
398 def copymapget(
401 def copymapget(
399 &self,
402 &self,
400 key: PyObject,
403 key: PyObject,
401 default: Option<PyObject>
404 default: Option<PyObject>
402 ) -> PyResult<Option<PyObject>> {
405 ) -> PyResult<Option<PyObject>> {
403 let key = key.extract::<PyBytes>(py)?;
406 let key = key.extract::<PyBytes>(py)?;
404 match self.inner(py).borrow().copy_map.get(key.data(py)) {
407 match self.inner(py).borrow().copy_map.get(key.data(py)) {
405 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
408 Some(copy) => Ok(Some(PyBytes::new(py, copy).into_object())),
406 None => Ok(default),
409 None => Ok(default),
407 }
410 }
408 }
411 }
409 def copymapsetitem(
412 def copymapsetitem(
410 &self,
413 &self,
411 key: PyObject,
414 key: PyObject,
412 value: PyObject
415 value: PyObject
413 ) -> PyResult<PyObject> {
416 ) -> PyResult<PyObject> {
414 let key = key.extract::<PyBytes>(py)?;
417 let key = key.extract::<PyBytes>(py)?;
415 let value = value.extract::<PyBytes>(py)?;
418 let value = value.extract::<PyBytes>(py)?;
416 self.borrow_mut(py)?
419 self.borrow_mut(py)?
417 .copy_map
420 .copy_map
418 .insert(key.data(py).to_vec(), value.data(py).to_vec());
421 .insert(key.data(py).to_vec(), value.data(py).to_vec());
419 Ok(py.None())
422 Ok(py.None())
420 }
423 }
421 def copymappop(
424 def copymappop(
422 &self,
425 &self,
423 key: PyObject,
426 key: PyObject,
424 default: Option<PyObject>
427 default: Option<PyObject>
425 ) -> PyResult<Option<PyObject>> {
428 ) -> PyResult<Option<PyObject>> {
426 let key = key.extract::<PyBytes>(py)?;
429 let key = key.extract::<PyBytes>(py)?;
427 match self.borrow_mut(py)?.copy_map.remove(key.data(py)) {
430 match self.borrow_mut(py)?.copy_map.remove(key.data(py)) {
428 Some(_) => Ok(None),
431 Some(_) => Ok(None),
429 None => Ok(default),
432 None => Ok(default),
430 }
433 }
431 }
434 }
432
435
433 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
436 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
437 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
434 CopyMapKeysIterator::from_inner(
438 CopyMapKeysIterator::from_inner(
435 py,
439 py,
436 Some(DirstateMapLeakedRef::new(py, &self)),
440 Some(leak_handle),
437 Box::new(self.leak_immutable(py)?.copy_map.iter()),
441 Box::new(leaked_ref.copy_map.iter()),
438 )
442 )
439 }
443 }
440
444
441 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
445 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
446 let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
442 CopyMapItemsIterator::from_inner(
447 CopyMapItemsIterator::from_inner(
443 py,
448 py,
444 Some(DirstateMapLeakedRef::new(py, &self)),
449 Some(leak_handle),
445 Box::new(self.leak_immutable(py)?.copy_map.iter()),
450 Box::new(leaked_ref.copy_map.iter()),
446 )
451 )
447 }
452 }
448
453
449 });
454 });
450
455
451 impl DirstateMap {
456 impl DirstateMap {
452 fn translate_key(
457 fn translate_key(
453 py: Python,
458 py: Python,
454 res: (&Vec<u8>, &DirstateEntry),
459 res: (&Vec<u8>, &DirstateEntry),
455 ) -> PyResult<Option<PyBytes>> {
460 ) -> PyResult<Option<PyBytes>> {
456 Ok(Some(PyBytes::new(py, res.0)))
461 Ok(Some(PyBytes::new(py, res.0)))
457 }
462 }
458 fn translate_key_value(
463 fn translate_key_value(
459 py: Python,
464 py: Python,
460 res: (&Vec<u8>, &DirstateEntry),
465 res: (&Vec<u8>, &DirstateEntry),
461 ) -> PyResult<Option<(PyBytes, PyObject)>> {
466 ) -> PyResult<Option<(PyBytes, PyObject)>> {
462 let (f, entry) = res;
467 let (f, entry) = res;
463
468
464 // Explicitly go through u8 first, then cast to
469 // Explicitly go through u8 first, then cast to
465 // platform-specific `c_char`.
470 // platform-specific `c_char`.
466 let state: u8 = entry.state.into();
471 let state: u8 = entry.state.into();
467 Ok(Some((
472 Ok(Some((
468 PyBytes::new(py, f),
473 PyBytes::new(py, f),
469 decapsule_make_dirstate_tuple(py)?(
474 decapsule_make_dirstate_tuple(py)?(
470 state as c_char,
475 state as c_char,
471 entry.mode,
476 entry.mode,
472 entry.size,
477 entry.size,
473 entry.mtime,
478 entry.mtime,
474 ),
479 ),
475 )))
480 )))
476 }
481 }
477 }
482 }
478
483
479 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
484 py_shared_ref!(DirstateMap, RustDirstateMap, inner, DirstateMapLeakedRef,);
480
485
481 py_shared_mapping_iterator!(
486 py_shared_mapping_iterator!(
482 DirstateMapKeysIterator,
487 DirstateMapKeysIterator,
483 DirstateMapLeakedRef,
488 DirstateMapLeakedRef,
484 Vec<u8>,
489 Vec<u8>,
485 DirstateEntry,
490 DirstateEntry,
486 DirstateMap::translate_key,
491 DirstateMap::translate_key,
487 Option<PyBytes>
492 Option<PyBytes>
488 );
493 );
489
494
490 py_shared_mapping_iterator!(
495 py_shared_mapping_iterator!(
491 DirstateMapItemsIterator,
496 DirstateMapItemsIterator,
492 DirstateMapLeakedRef,
497 DirstateMapLeakedRef,
493 Vec<u8>,
498 Vec<u8>,
494 DirstateEntry,
499 DirstateEntry,
495 DirstateMap::translate_key_value,
500 DirstateMap::translate_key_value,
496 Option<(PyBytes, PyObject)>
501 Option<(PyBytes, PyObject)>
497 );
502 );
498
503
499 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
504 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<[u8; PARENT_SIZE]> {
500 let bytes = obj.extract::<PyBytes>(py)?;
505 let bytes = obj.extract::<PyBytes>(py)?;
501 match bytes.data(py).try_into() {
506 match bytes.data(py).try_into() {
502 Ok(s) => Ok(s),
507 Ok(s) => Ok(s),
503 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
508 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
504 }
509 }
505 }
510 }
@@ -1,412 +1,423 b''
1 // macros.rs
1 // macros.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Macros for use in the `hg-cpython` bridge library.
8 //! Macros for use in the `hg-cpython` bridge library.
9
9
10 use crate::exceptions::AlreadyBorrowed;
10 use crate::exceptions::AlreadyBorrowed;
11 use cpython::{PyResult, Python};
11 use cpython::{PyResult, Python};
12 use std::cell::{Cell, Ref, RefCell, RefMut};
12 use std::cell::{Cell, Ref, RefCell, RefMut};
13
13
14 /// Manages the shared state between Python and Rust
14 /// Manages the shared state between Python and Rust
15 #[derive(Default)]
15 #[derive(Default)]
16 pub struct PySharedState {
16 pub struct PySharedState {
17 leak_count: Cell<usize>,
17 leak_count: Cell<usize>,
18 mutably_borrowed: Cell<bool>,
18 mutably_borrowed: Cell<bool>,
19 }
19 }
20
20
21 impl PySharedState {
21 impl PySharedState {
22 pub fn borrow_mut<'a, T>(
22 pub fn borrow_mut<'a, T>(
23 &'a self,
23 &'a self,
24 py: Python<'a>,
24 py: Python<'a>,
25 pyrefmut: RefMut<'a, T>,
25 pyrefmut: RefMut<'a, T>,
26 ) -> PyResult<PyRefMut<'a, T>> {
26 ) -> PyResult<PyRefMut<'a, T>> {
27 if self.mutably_borrowed.get() {
27 if self.mutably_borrowed.get() {
28 return Err(AlreadyBorrowed::new(
28 return Err(AlreadyBorrowed::new(
29 py,
29 py,
30 "Cannot borrow mutably while there exists another \
30 "Cannot borrow mutably while there exists another \
31 mutable reference in a Python object",
31 mutable reference in a Python object",
32 ));
32 ));
33 }
33 }
34 match self.leak_count.get() {
34 match self.leak_count.get() {
35 0 => {
35 0 => {
36 self.mutably_borrowed.replace(true);
36 self.mutably_borrowed.replace(true);
37 Ok(PyRefMut::new(py, pyrefmut, self))
37 Ok(PyRefMut::new(py, pyrefmut, self))
38 }
38 }
39 // TODO
39 // TODO
40 // For now, this works differently than Python references
40 // For now, this works differently than Python references
41 // in the case of iterators.
41 // in the case of iterators.
42 // Python does not complain when the data an iterator
42 // Python does not complain when the data an iterator
43 // points to is modified if the iterator is never used
43 // points to is modified if the iterator is never used
44 // afterwards.
44 // afterwards.
45 // Here, we are stricter than this by refusing to give a
45 // Here, we are stricter than this by refusing to give a
46 // mutable reference if it is already borrowed.
46 // mutable reference if it is already borrowed.
47 // While the additional safety might be argued for, it
47 // While the additional safety might be argued for, it
48 // breaks valid programming patterns in Python and we need
48 // breaks valid programming patterns in Python and we need
49 // to fix this issue down the line.
49 // to fix this issue down the line.
50 _ => Err(AlreadyBorrowed::new(
50 _ => Err(AlreadyBorrowed::new(
51 py,
51 py,
52 "Cannot borrow mutably while there are \
52 "Cannot borrow mutably while there are \
53 immutable references in Python objects",
53 immutable references in Python objects",
54 )),
54 )),
55 }
55 }
56 }
56 }
57
57
58 /// Return a reference to the wrapped data with an artificial static
58 /// Return a reference to the wrapped data with an artificial static
59 /// lifetime.
59 /// lifetime.
60 /// We need to be protected by the GIL for thread-safety.
60 /// We need to be protected by the GIL for thread-safety.
61 pub fn leak_immutable<T>(
61 pub fn leak_immutable<T>(
62 &self,
62 &self,
63 py: Python,
63 py: Python,
64 data: &PySharedRefCell<T>,
64 data: &PySharedRefCell<T>,
65 ) -> PyResult<&'static T> {
65 ) -> PyResult<&'static T> {
66 if self.mutably_borrowed.get() {
66 if self.mutably_borrowed.get() {
67 return Err(AlreadyBorrowed::new(
67 return Err(AlreadyBorrowed::new(
68 py,
68 py,
69 "Cannot borrow immutably while there is a \
69 "Cannot borrow immutably while there is a \
70 mutable reference in Python objects",
70 mutable reference in Python objects",
71 ));
71 ));
72 }
72 }
73 let ptr = data.as_ptr();
73 let ptr = data.as_ptr();
74 self.leak_count.replace(self.leak_count.get() + 1);
74 self.leak_count.replace(self.leak_count.get() + 1);
75 unsafe { Ok(&*ptr) }
75 unsafe { Ok(&*ptr) }
76 }
76 }
77
77
78 pub fn decrease_leak_count(&self, _py: Python, mutable: bool) {
78 pub fn decrease_leak_count(&self, _py: Python, mutable: bool) {
79 self.leak_count
79 self.leak_count
80 .replace(self.leak_count.get().saturating_sub(1));
80 .replace(self.leak_count.get().saturating_sub(1));
81 if mutable {
81 if mutable {
82 self.mutably_borrowed.replace(false);
82 self.mutably_borrowed.replace(false);
83 }
83 }
84 }
84 }
85 }
85 }
86
86
87 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
87 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
88 ///
88 ///
89 /// Only immutable operation is allowed through this interface.
89 /// Only immutable operation is allowed through this interface.
90 #[derive(Debug)]
90 #[derive(Debug)]
91 pub struct PySharedRefCell<T> {
91 pub struct PySharedRefCell<T> {
92 inner: RefCell<T>,
92 inner: RefCell<T>,
93 }
93 }
94
94
95 impl<T> PySharedRefCell<T> {
95 impl<T> PySharedRefCell<T> {
96 pub const fn new(value: T) -> PySharedRefCell<T> {
96 pub const fn new(value: T) -> PySharedRefCell<T> {
97 Self {
97 Self {
98 inner: RefCell::new(value),
98 inner: RefCell::new(value),
99 }
99 }
100 }
100 }
101
101
102 pub fn borrow(&self) -> Ref<T> {
102 pub fn borrow(&self) -> Ref<T> {
103 // py_shared_state isn't involved since
103 // py_shared_state isn't involved since
104 // - inner.borrow() would fail if self is mutably borrowed,
104 // - inner.borrow() would fail if self is mutably borrowed,
105 // - and inner.borrow_mut() would fail while self is borrowed.
105 // - and inner.borrow_mut() would fail while self is borrowed.
106 self.inner.borrow()
106 self.inner.borrow()
107 }
107 }
108
108
109 pub fn as_ptr(&self) -> *mut T {
109 pub fn as_ptr(&self) -> *mut T {
110 self.inner.as_ptr()
110 self.inner.as_ptr()
111 }
111 }
112
112
113 pub unsafe fn borrow_mut(&self) -> RefMut<T> {
113 pub unsafe fn borrow_mut(&self) -> RefMut<T> {
114 // must be borrowed by self.py_shared_state(py).borrow_mut().
114 // must be borrowed by self.py_shared_state(py).borrow_mut().
115 self.inner.borrow_mut()
115 self.inner.borrow_mut()
116 }
116 }
117 }
117 }
118
118
119 /// Holds a mutable reference to data shared between Python and Rust.
119 /// Holds a mutable reference to data shared between Python and Rust.
120 pub struct PyRefMut<'a, T> {
120 pub struct PyRefMut<'a, T> {
121 inner: RefMut<'a, T>,
121 inner: RefMut<'a, T>,
122 py_shared_state: &'a PySharedState,
122 py_shared_state: &'a PySharedState,
123 }
123 }
124
124
125 impl<'a, T> PyRefMut<'a, T> {
125 impl<'a, T> PyRefMut<'a, T> {
126 fn new(
126 fn new(
127 _py: Python<'a>,
127 _py: Python<'a>,
128 inner: RefMut<'a, T>,
128 inner: RefMut<'a, T>,
129 py_shared_state: &'a PySharedState,
129 py_shared_state: &'a PySharedState,
130 ) -> Self {
130 ) -> Self {
131 Self {
131 Self {
132 inner,
132 inner,
133 py_shared_state,
133 py_shared_state,
134 }
134 }
135 }
135 }
136 }
136 }
137
137
138 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
138 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
139 type Target = RefMut<'a, T>;
139 type Target = RefMut<'a, T>;
140
140
141 fn deref(&self) -> &Self::Target {
141 fn deref(&self) -> &Self::Target {
142 &self.inner
142 &self.inner
143 }
143 }
144 }
144 }
145 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
145 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
146 fn deref_mut(&mut self) -> &mut Self::Target {
146 fn deref_mut(&mut self) -> &mut Self::Target {
147 &mut self.inner
147 &mut self.inner
148 }
148 }
149 }
149 }
150
150
151 impl<'a, T> Drop for PyRefMut<'a, T> {
151 impl<'a, T> Drop for PyRefMut<'a, T> {
152 fn drop(&mut self) {
152 fn drop(&mut self) {
153 let gil = Python::acquire_gil();
153 let gil = Python::acquire_gil();
154 let py = gil.python();
154 let py = gil.python();
155 self.py_shared_state.decrease_leak_count(py, true);
155 self.py_shared_state.decrease_leak_count(py, true);
156 }
156 }
157 }
157 }
158
158
159 /// Allows a `py_class!` generated struct to share references to one of its
159 /// Allows a `py_class!` generated struct to share references to one of its
160 /// data members with Python.
160 /// data members with Python.
161 ///
161 ///
162 /// # Warning
162 /// # Warning
163 ///
163 ///
164 /// The targeted `py_class!` needs to have the
164 /// The targeted `py_class!` needs to have the
165 /// `data py_shared_state: PySharedState;` data attribute to compile.
165 /// `data py_shared_state: PySharedState;` data attribute to compile.
166 /// A better, more complicated macro is needed to automatically insert it,
166 /// A better, more complicated macro is needed to automatically insert it,
167 /// but this one is not yet really battle tested (what happens when
167 /// but this one is not yet really battle tested (what happens when
168 /// multiple references are needed?). See the example below.
168 /// multiple references are needed?). See the example below.
169 ///
169 ///
170 /// TODO allow Python container types: for now, integration with the garbage
170 /// TODO allow Python container types: for now, integration with the garbage
171 /// collector does not extend to Rust structs holding references to Python
171 /// collector does not extend to Rust structs holding references to Python
172 /// objects. Should the need surface, `__traverse__` and `__clear__` will
172 /// objects. Should the need surface, `__traverse__` and `__clear__` will
173 /// need to be written as per the `rust-cpython` docs on GC integration.
173 /// need to be written as per the `rust-cpython` docs on GC integration.
174 ///
174 ///
175 /// # Parameters
175 /// # Parameters
176 ///
176 ///
177 /// * `$name` is the same identifier used in for `py_class!` macro call.
177 /// * `$name` is the same identifier used in for `py_class!` macro call.
178 /// * `$inner_struct` is the identifier of the underlying Rust struct
178 /// * `$inner_struct` is the identifier of the underlying Rust struct
179 /// * `$data_member` is the identifier of the data member of `$inner_struct`
179 /// * `$data_member` is the identifier of the data member of `$inner_struct`
180 /// that will be shared.
180 /// that will be shared.
181 /// * `$leaked` is the identifier to give to the struct that will manage
181 /// * `$leaked` is the identifier to give to the struct that will manage
182 /// references to `$name`, to be used for example in other macros like
182 /// references to `$name`, to be used for example in other macros like
183 /// `py_shared_mapping_iterator`.
183 /// `py_shared_mapping_iterator`.
184 ///
184 ///
185 /// # Example
185 /// # Example
186 ///
186 ///
187 /// ```
187 /// ```
188 /// struct MyStruct {
188 /// struct MyStruct {
189 /// inner: Vec<u32>;
189 /// inner: Vec<u32>;
190 /// }
190 /// }
191 ///
191 ///
192 /// py_class!(pub class MyType |py| {
192 /// py_class!(pub class MyType |py| {
193 /// data inner: PySharedRefCell<MyStruct>;
193 /// data inner: PySharedRefCell<MyStruct>;
194 /// data py_shared_state: PySharedState;
194 /// data py_shared_state: PySharedState;
195 /// });
195 /// });
196 ///
196 ///
197 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
197 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
198 /// ```
198 /// ```
199 macro_rules! py_shared_ref {
199 macro_rules! py_shared_ref {
200 (
200 (
201 $name: ident,
201 $name: ident,
202 $inner_struct: ident,
202 $inner_struct: ident,
203 $data_member: ident,
203 $data_member: ident,
204 $leaked: ident,
204 $leaked: ident,
205 ) => {
205 ) => {
206 impl $name {
206 impl $name {
207 fn borrow_mut<'a>(
207 fn borrow_mut<'a>(
208 &'a self,
208 &'a self,
209 py: Python<'a>,
209 py: Python<'a>,
210 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
210 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
211 {
211 {
212 // assert $data_member type
212 // assert $data_member type
213 use crate::ref_sharing::PySharedRefCell;
213 use crate::ref_sharing::PySharedRefCell;
214 let data: &PySharedRefCell<_> = self.$data_member(py);
214 let data: &PySharedRefCell<_> = self.$data_member(py);
215 self.py_shared_state(py)
215 self.py_shared_state(py)
216 .borrow_mut(py, unsafe { data.borrow_mut() })
216 .borrow_mut(py, unsafe { data.borrow_mut() })
217 }
217 }
218
218
219 /// Returns a leaked reference and its management object.
220 ///
221 /// # Safety
222 ///
223 /// It's up to you to make sure that the management object lives
224 /// longer than the leaked reference. Otherwise, you'll get a
225 /// dangling reference.
219 fn leak_immutable<'a>(
226 fn leak_immutable<'a>(
220 &'a self,
227 &'a self,
221 py: Python<'a>,
228 py: Python<'a>,
222 ) -> PyResult<&'static $inner_struct> {
229 ) -> PyResult<($leaked, &'static $inner_struct)> {
223 // assert $data_member type
230 // assert $data_member type
224 use crate::ref_sharing::PySharedRefCell;
231 use crate::ref_sharing::PySharedRefCell;
225 let data: &PySharedRefCell<_> = self.$data_member(py);
232 let data: &PySharedRefCell<_> = self.$data_member(py);
226 self.py_shared_state(py).leak_immutable(py, data)
233 let static_ref =
234 self.py_shared_state(py).leak_immutable(py, data)?;
235 let leak_handle = $leaked::new(py, self);
236 Ok((leak_handle, static_ref))
227 }
237 }
228 }
238 }
229
239
230 /// Manage immutable references to `$name` leaked into Python
240 /// Manage immutable references to `$name` leaked into Python
231 /// iterators.
241 /// iterators.
232 ///
242 ///
233 /// In truth, this does not represent leaked references themselves;
243 /// In truth, this does not represent leaked references themselves;
234 /// it is instead useful alongside them to manage them.
244 /// it is instead useful alongside them to manage them.
235 pub struct $leaked {
245 pub struct $leaked {
236 inner: $name,
246 inner: $name,
237 }
247 }
238
248
239 impl $leaked {
249 impl $leaked {
240 fn new(py: Python, inner: &$name) -> Self {
250 fn new(py: Python, inner: &$name) -> Self {
241 Self {
251 Self {
242 inner: inner.clone_ref(py),
252 inner: inner.clone_ref(py),
243 }
253 }
244 }
254 }
245 }
255 }
246
256
247 impl Drop for $leaked {
257 impl Drop for $leaked {
248 fn drop(&mut self) {
258 fn drop(&mut self) {
249 let gil = Python::acquire_gil();
259 let gil = Python::acquire_gil();
250 let py = gil.python();
260 let py = gil.python();
251 self.inner
261 self.inner
252 .py_shared_state(py)
262 .py_shared_state(py)
253 .decrease_leak_count(py, false);
263 .decrease_leak_count(py, false);
254 }
264 }
255 }
265 }
256 };
266 };
257 }
267 }
258
268
259 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
269 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
260 macro_rules! py_shared_iterator_impl {
270 macro_rules! py_shared_iterator_impl {
261 (
271 (
262 $name: ident,
272 $name: ident,
263 $leaked: ident,
273 $leaked: ident,
264 $iterator_type: ty,
274 $iterator_type: ty,
265 $success_func: expr,
275 $success_func: expr,
266 $success_type: ty
276 $success_type: ty
267 ) => {
277 ) => {
268 py_class!(pub class $name |py| {
278 py_class!(pub class $name |py| {
269 data inner: RefCell<Option<$leaked>>;
279 data inner: RefCell<Option<$leaked>>;
270 data it: RefCell<$iterator_type>;
280 data it: RefCell<$iterator_type>;
271
281
272 def __next__(&self) -> PyResult<$success_type> {
282 def __next__(&self) -> PyResult<$success_type> {
273 let mut inner_opt = self.inner(py).borrow_mut();
283 let mut inner_opt = self.inner(py).borrow_mut();
274 if inner_opt.is_some() {
284 if inner_opt.is_some() {
275 match self.it(py).borrow_mut().next() {
285 match self.it(py).borrow_mut().next() {
276 None => {
286 None => {
277 // replace Some(inner) by None, drop $leaked
287 // replace Some(inner) by None, drop $leaked
278 inner_opt.take();
288 inner_opt.take();
279 Ok(None)
289 Ok(None)
280 }
290 }
281 Some(res) => {
291 Some(res) => {
282 $success_func(py, res)
292 $success_func(py, res)
283 }
293 }
284 }
294 }
285 } else {
295 } else {
286 Ok(None)
296 Ok(None)
287 }
297 }
288 }
298 }
289
299
290 def __iter__(&self) -> PyResult<Self> {
300 def __iter__(&self) -> PyResult<Self> {
291 Ok(self.clone_ref(py))
301 Ok(self.clone_ref(py))
292 }
302 }
293 });
303 });
294
304
295 impl $name {
305 impl $name {
296 pub fn from_inner(
306 pub fn from_inner(
297 py: Python,
307 py: Python,
298 leaked: Option<$leaked>,
308 leaked: Option<$leaked>,
299 it: $iterator_type
309 it: $iterator_type
300 ) -> PyResult<Self> {
310 ) -> PyResult<Self> {
301 Self::create_instance(
311 Self::create_instance(
302 py,
312 py,
303 RefCell::new(leaked),
313 RefCell::new(leaked),
304 RefCell::new(it)
314 RefCell::new(it)
305 )
315 )
306 }
316 }
307 }
317 }
308 };
318 };
309 }
319 }
310
320
311 /// Defines a `py_class!` that acts as a Python mapping iterator over a Rust
321 /// Defines a `py_class!` that acts as a Python mapping iterator over a Rust
312 /// iterator.
322 /// iterator.
313 ///
323 ///
314 /// TODO: this is a bit awkward to use, and a better (more complicated)
324 /// TODO: this is a bit awkward to use, and a better (more complicated)
315 /// procedural macro would simplify the interface a lot.
325 /// procedural macro would simplify the interface a lot.
316 ///
326 ///
317 /// # Parameters
327 /// # Parameters
318 ///
328 ///
319 /// * `$name` is the identifier to give to the resulting Rust struct.
329 /// * `$name` is the identifier to give to the resulting Rust struct.
320 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
330 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
321 /// * `$key_type` is the type of the key in the mapping
331 /// * `$key_type` is the type of the key in the mapping
322 /// * `$value_type` is the type of the value in the mapping
332 /// * `$value_type` is the type of the value in the mapping
323 /// * `$success_func` is a function for processing the Rust `(key, value)`
333 /// * `$success_func` is a function for processing the Rust `(key, value)`
324 /// tuple on iteration success, turning it into something Python understands.
334 /// tuple on iteration success, turning it into something Python understands.
325 /// * `$success_func` is the return type of `$success_func`
335 /// * `$success_func` is the return type of `$success_func`
326 ///
336 ///
327 /// # Example
337 /// # Example
328 ///
338 ///
329 /// ```
339 /// ```
330 /// struct MyStruct {
340 /// struct MyStruct {
331 /// inner: HashMap<Vec<u8>, Vec<u8>>;
341 /// inner: HashMap<Vec<u8>, Vec<u8>>;
332 /// }
342 /// }
333 ///
343 ///
334 /// py_class!(pub class MyType |py| {
344 /// py_class!(pub class MyType |py| {
335 /// data inner: PySharedRefCell<MyStruct>;
345 /// data inner: PySharedRefCell<MyStruct>;
336 /// data py_shared_state: PySharedState;
346 /// data py_shared_state: PySharedState;
337 ///
347 ///
338 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
348 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
349 /// let (leak_handle, leaked_ref) = self.leak_immutable(py)?;
339 /// MyTypeItemsIterator::create_instance(
350 /// MyTypeItemsIterator::create_instance(
340 /// py,
351 /// py,
341 /// RefCell::new(Some(MyTypeLeakedRef::new(py, &self))),
352 /// RefCell::new(Some(leak_handle)),
342 /// RefCell::new(self.leak_immutable(py).iter()),
353 /// RefCell::new(leaked_ref.iter()),
343 /// )
354 /// )
344 /// }
355 /// }
345 /// });
356 /// });
346 ///
357 ///
347 /// impl MyType {
358 /// impl MyType {
348 /// fn translate_key_value(
359 /// fn translate_key_value(
349 /// py: Python,
360 /// py: Python,
350 /// res: (&Vec<u8>, &Vec<u8>),
361 /// res: (&Vec<u8>, &Vec<u8>),
351 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
362 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
352 /// let (f, entry) = res;
363 /// let (f, entry) = res;
353 /// Ok(Some((
364 /// Ok(Some((
354 /// PyBytes::new(py, f),
365 /// PyBytes::new(py, f),
355 /// PyBytes::new(py, entry),
366 /// PyBytes::new(py, entry),
356 /// )))
367 /// )))
357 /// }
368 /// }
358 /// }
369 /// }
359 ///
370 ///
360 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
371 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
361 ///
372 ///
362 /// py_shared_mapping_iterator!(
373 /// py_shared_mapping_iterator!(
363 /// MyTypeItemsIterator,
374 /// MyTypeItemsIterator,
364 /// MyTypeLeakedRef,
375 /// MyTypeLeakedRef,
365 /// Vec<u8>,
376 /// Vec<u8>,
366 /// Vec<u8>,
377 /// Vec<u8>,
367 /// MyType::translate_key_value,
378 /// MyType::translate_key_value,
368 /// Option<(PyBytes, PyBytes)>
379 /// Option<(PyBytes, PyBytes)>
369 /// );
380 /// );
370 /// ```
381 /// ```
371 #[allow(unused)] // Removed in a future patch
382 #[allow(unused)] // Removed in a future patch
372 macro_rules! py_shared_mapping_iterator {
383 macro_rules! py_shared_mapping_iterator {
373 (
384 (
374 $name:ident,
385 $name:ident,
375 $leaked:ident,
386 $leaked:ident,
376 $key_type: ty,
387 $key_type: ty,
377 $value_type: ty,
388 $value_type: ty,
378 $success_func: path,
389 $success_func: path,
379 $success_type: ty
390 $success_type: ty
380 ) => {
391 ) => {
381 py_shared_iterator_impl!(
392 py_shared_iterator_impl!(
382 $name,
393 $name,
383 $leaked,
394 $leaked,
384 Box<
395 Box<
385 dyn Iterator<Item = (&'static $key_type, &'static $value_type)>
396 dyn Iterator<Item = (&'static $key_type, &'static $value_type)>
386 + Send,
397 + Send,
387 >,
398 >,
388 $success_func,
399 $success_func,
389 $success_type
400 $success_type
390 );
401 );
391 };
402 };
392 }
403 }
393
404
394 /// Works basically the same as `py_shared_mapping_iterator`, but with only a
405 /// Works basically the same as `py_shared_mapping_iterator`, but with only a
395 /// key.
406 /// key.
396 macro_rules! py_shared_sequence_iterator {
407 macro_rules! py_shared_sequence_iterator {
397 (
408 (
398 $name:ident,
409 $name:ident,
399 $leaked:ident,
410 $leaked:ident,
400 $key_type: ty,
411 $key_type: ty,
401 $success_func: path,
412 $success_func: path,
402 $success_type: ty
413 $success_type: ty
403 ) => {
414 ) => {
404 py_shared_iterator_impl!(
415 py_shared_iterator_impl!(
405 $name,
416 $name,
406 $leaked,
417 $leaked,
407 Box<dyn Iterator<Item = &'static $key_type> + Send>,
418 Box<dyn Iterator<Item = &'static $key_type> + Send>,
408 $success_func,
419 $success_func,
409 $success_type
420 $success_type
410 );
421 );
411 };
422 };
412 }
423 }
General Comments 0
You need to be logged in to leave comments. Login now