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