##// END OF EJS Templates
rust-nodemap: also clear Rust data in `clearcaches`...
Georges Racinet -
r44998:cadcc8c2 default
parent child Browse files
Show More
@@ -1,492 +1,496 b''
1 // revlog.rs
1 // revlog.rs
2 //
2 //
3 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
3 // Copyright 2019-2020 Georges Racinet <georges.racinet@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 use crate::{
8 use crate::{
9 cindex,
9 cindex,
10 utils::{node_from_py_bytes, node_from_py_object},
10 utils::{node_from_py_bytes, node_from_py_object},
11 };
11 };
12 use cpython::{
12 use cpython::{
13 buffer::{Element, PyBuffer},
13 buffer::{Element, PyBuffer},
14 exc::{IndexError, ValueError},
14 exc::{IndexError, ValueError},
15 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyModule, PyObject,
15 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyModule, PyObject,
16 PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
16 PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
17 };
17 };
18 use hg::{
18 use hg::{
19 nodemap::{Block, NodeMapError, NodeTree},
19 nodemap::{Block, NodeMapError, NodeTree},
20 revlog::{nodemap::NodeMap, RevlogIndex},
20 revlog::{nodemap::NodeMap, RevlogIndex},
21 NodeError, Revision,
21 NodeError, Revision,
22 };
22 };
23 use std::cell::RefCell;
23 use std::cell::RefCell;
24
24
25 /// Return a Struct implementing the Graph trait
25 /// Return a Struct implementing the Graph trait
26 pub(crate) fn pyindex_to_graph(
26 pub(crate) fn pyindex_to_graph(
27 py: Python,
27 py: Python,
28 index: PyObject,
28 index: PyObject,
29 ) -> PyResult<cindex::Index> {
29 ) -> PyResult<cindex::Index> {
30 match index.extract::<MixedIndex>(py) {
30 match index.extract::<MixedIndex>(py) {
31 Ok(midx) => Ok(midx.clone_cindex(py)),
31 Ok(midx) => Ok(midx.clone_cindex(py)),
32 Err(_) => cindex::Index::new(py, index),
32 Err(_) => cindex::Index::new(py, index),
33 }
33 }
34 }
34 }
35
35
36 py_class!(pub class MixedIndex |py| {
36 py_class!(pub class MixedIndex |py| {
37 data cindex: RefCell<cindex::Index>;
37 data cindex: RefCell<cindex::Index>;
38 data nt: RefCell<Option<NodeTree>>;
38 data nt: RefCell<Option<NodeTree>>;
39 data docket: RefCell<Option<PyObject>>;
39 data docket: RefCell<Option<PyObject>>;
40 // Holds a reference to the mmap'ed persistent nodemap data
40 // Holds a reference to the mmap'ed persistent nodemap data
41 data mmap: RefCell<Option<PyBuffer>>;
41 data mmap: RefCell<Option<PyBuffer>>;
42
42
43 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
43 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
44 Self::new(py, cindex)
44 Self::new(py, cindex)
45 }
45 }
46
46
47 /// Compatibility layer used for Python consumers needing access to the C index
47 /// Compatibility layer used for Python consumers needing access to the C index
48 ///
48 ///
49 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
49 /// Only use case so far is `scmutil.shortesthexnodeidprefix`,
50 /// that may need to build a custom `nodetree`, based on a specified revset.
50 /// that may need to build a custom `nodetree`, based on a specified revset.
51 /// With a Rust implementation of the nodemap, we will be able to get rid of
51 /// With a Rust implementation of the nodemap, we will be able to get rid of
52 /// this, by exposing our own standalone nodemap class,
52 /// this, by exposing our own standalone nodemap class,
53 /// ready to accept `MixedIndex`.
53 /// ready to accept `MixedIndex`.
54 def get_cindex(&self) -> PyResult<PyObject> {
54 def get_cindex(&self) -> PyResult<PyObject> {
55 Ok(self.cindex(py).borrow().inner().clone_ref(py))
55 Ok(self.cindex(py).borrow().inner().clone_ref(py))
56 }
56 }
57
57
58 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
58 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
59
59
60 /// Return Revision if found, raises a bare `error.RevlogError`
60 /// Return Revision if found, raises a bare `error.RevlogError`
61 /// in case of ambiguity, same as C version does
61 /// in case of ambiguity, same as C version does
62 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
62 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
63 let opt = self.get_nodetree(py)?.borrow();
63 let opt = self.get_nodetree(py)?.borrow();
64 let nt = opt.as_ref().unwrap();
64 let nt = opt.as_ref().unwrap();
65 let idx = &*self.cindex(py).borrow();
65 let idx = &*self.cindex(py).borrow();
66 let node = node_from_py_bytes(py, &node)?;
66 let node = node_from_py_bytes(py, &node)?;
67 nt.find_bin(idx, (&node).into()).map_err(|e| nodemap_error(py, e))
67 nt.find_bin(idx, (&node).into()).map_err(|e| nodemap_error(py, e))
68 }
68 }
69
69
70 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
70 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
71 /// is not found.
71 /// is not found.
72 ///
72 ///
73 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
73 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
74 /// will catch and rewrap with it
74 /// will catch and rewrap with it
75 def rev(&self, node: PyBytes) -> PyResult<Revision> {
75 def rev(&self, node: PyBytes) -> PyResult<Revision> {
76 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
76 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
77 }
77 }
78
78
79 /// return True if the node exist in the index
79 /// return True if the node exist in the index
80 def has_node(&self, node: PyBytes) -> PyResult<bool> {
80 def has_node(&self, node: PyBytes) -> PyResult<bool> {
81 self.get_rev(py, node).map(|opt| opt.is_some())
81 self.get_rev(py, node).map(|opt| opt.is_some())
82 }
82 }
83
83
84 /// find length of shortest hex nodeid of a binary ID
84 /// find length of shortest hex nodeid of a binary ID
85 def shortest(&self, node: PyBytes) -> PyResult<usize> {
85 def shortest(&self, node: PyBytes) -> PyResult<usize> {
86 let opt = self.get_nodetree(py)?.borrow();
86 let opt = self.get_nodetree(py)?.borrow();
87 let nt = opt.as_ref().unwrap();
87 let nt = opt.as_ref().unwrap();
88 let idx = &*self.cindex(py).borrow();
88 let idx = &*self.cindex(py).borrow();
89 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
89 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
90 {
90 {
91 Ok(Some(l)) => Ok(l),
91 Ok(Some(l)) => Ok(l),
92 Ok(None) => Err(revlog_error(py)),
92 Ok(None) => Err(revlog_error(py)),
93 Err(e) => Err(nodemap_error(py, e)),
93 Err(e) => Err(nodemap_error(py, e)),
94 }
94 }
95 }
95 }
96
96
97 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
97 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
98 let opt = self.get_nodetree(py)?.borrow();
98 let opt = self.get_nodetree(py)?.borrow();
99 let nt = opt.as_ref().unwrap();
99 let nt = opt.as_ref().unwrap();
100 let idx = &*self.cindex(py).borrow();
100 let idx = &*self.cindex(py).borrow();
101
101
102 let node_as_string = if cfg!(feature = "python3-sys") {
102 let node_as_string = if cfg!(feature = "python3-sys") {
103 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
103 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
104 }
104 }
105 else {
105 else {
106 let node = node.extract::<PyBytes>(py)?;
106 let node = node.extract::<PyBytes>(py)?;
107 String::from_utf8_lossy(node.data(py)).to_string()
107 String::from_utf8_lossy(node.data(py)).to_string()
108 };
108 };
109
109
110 nt.find_hex(idx, &node_as_string)
110 nt.find_hex(idx, &node_as_string)
111 // TODO make an inner API returning the node directly
111 // TODO make an inner API returning the node directly
112 .map(|opt| opt.map(
112 .map(|opt| opt.map(
113 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
113 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
114 .map_err(|e| nodemap_error(py, e))
114 .map_err(|e| nodemap_error(py, e))
115
115
116 }
116 }
117
117
118 /// append an index entry
118 /// append an index entry
119 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
119 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
120 if tup.len(py) < 8 {
120 if tup.len(py) < 8 {
121 // this is better than the panic promised by tup.get_item()
121 // this is better than the panic promised by tup.get_item()
122 return Err(
122 return Err(
123 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
123 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
124 }
124 }
125 let node_bytes = tup.get_item(py, 7).extract(py)?;
125 let node_bytes = tup.get_item(py, 7).extract(py)?;
126 let node = node_from_py_object(py, &node_bytes)?;
126 let node = node_from_py_object(py, &node_bytes)?;
127
127
128 let mut idx = self.cindex(py).borrow_mut();
128 let mut idx = self.cindex(py).borrow_mut();
129 let rev = idx.len() as Revision;
129 let rev = idx.len() as Revision;
130
130
131 idx.append(py, tup)?;
131 idx.append(py, tup)?;
132 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
132 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
133 .insert(&*idx, &node, rev)
133 .insert(&*idx, &node, rev)
134 .map_err(|e| nodemap_error(py, e))?;
134 .map_err(|e| nodemap_error(py, e))?;
135 Ok(py.None())
135 Ok(py.None())
136 }
136 }
137
137
138 def __delitem__(&self, key: PyObject) -> PyResult<()> {
138 def __delitem__(&self, key: PyObject) -> PyResult<()> {
139 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
139 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
140 self.cindex(py).borrow().inner().del_item(py, key)?;
140 self.cindex(py).borrow().inner().del_item(py, key)?;
141 let mut opt = self.get_nodetree(py)?.borrow_mut();
141 let mut opt = self.get_nodetree(py)?.borrow_mut();
142 let mut nt = opt.as_mut().unwrap();
142 let mut nt = opt.as_mut().unwrap();
143 nt.invalidate_all();
143 nt.invalidate_all();
144 self.fill_nodemap(py, &mut nt)?;
144 self.fill_nodemap(py, &mut nt)?;
145 Ok(())
145 Ok(())
146 }
146 }
147
147
148 //
148 //
149 // Reforwarded C index API
149 // Reforwarded C index API
150 //
150 //
151
151
152 // index_methods (tp_methods). Same ordering as in revlog.c
152 // index_methods (tp_methods). Same ordering as in revlog.c
153
153
154 /// return the gca set of the given revs
154 /// return the gca set of the given revs
155 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
155 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
156 self.call_cindex(py, "ancestors", args, kw)
156 self.call_cindex(py, "ancestors", args, kw)
157 }
157 }
158
158
159 /// return the heads of the common ancestors of the given revs
159 /// return the heads of the common ancestors of the given revs
160 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
160 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
161 self.call_cindex(py, "commonancestorsheads", args, kw)
161 self.call_cindex(py, "commonancestorsheads", args, kw)
162 }
162 }
163
163
164 /// clear the index caches
164 /// Clear the index caches and inner py_class data.
165 /// It is Python's responsibility to call `update_nodemap_data` again.
165 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
166 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
167 self.nt(py).borrow_mut().take();
168 self.docket(py).borrow_mut().take();
169 self.mmap(py).borrow_mut().take();
166 self.call_cindex(py, "clearcaches", args, kw)
170 self.call_cindex(py, "clearcaches", args, kw)
167 }
171 }
168
172
169 /// get an index entry
173 /// get an index entry
170 def get(&self, *args, **kw) -> PyResult<PyObject> {
174 def get(&self, *args, **kw) -> PyResult<PyObject> {
171 self.call_cindex(py, "get", args, kw)
175 self.call_cindex(py, "get", args, kw)
172 }
176 }
173
177
174 /// compute phases
178 /// compute phases
175 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
179 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
176 self.call_cindex(py, "computephasesmapsets", args, kw)
180 self.call_cindex(py, "computephasesmapsets", args, kw)
177 }
181 }
178
182
179 /// reachableroots
183 /// reachableroots
180 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
184 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
181 self.call_cindex(py, "reachableroots2", args, kw)
185 self.call_cindex(py, "reachableroots2", args, kw)
182 }
186 }
183
187
184 /// get head revisions
188 /// get head revisions
185 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
189 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
186 self.call_cindex(py, "headrevs", args, kw)
190 self.call_cindex(py, "headrevs", args, kw)
187 }
191 }
188
192
189 /// get filtered head revisions
193 /// get filtered head revisions
190 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
194 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
191 self.call_cindex(py, "headrevsfiltered", args, kw)
195 self.call_cindex(py, "headrevsfiltered", args, kw)
192 }
196 }
193
197
194 /// True if the object is a snapshot
198 /// True if the object is a snapshot
195 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
199 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
196 self.call_cindex(py, "issnapshot", args, kw)
200 self.call_cindex(py, "issnapshot", args, kw)
197 }
201 }
198
202
199 /// Gather snapshot data in a cache dict
203 /// Gather snapshot data in a cache dict
200 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
204 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
201 self.call_cindex(py, "findsnapshots", args, kw)
205 self.call_cindex(py, "findsnapshots", args, kw)
202 }
206 }
203
207
204 /// determine revisions with deltas to reconstruct fulltext
208 /// determine revisions with deltas to reconstruct fulltext
205 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
209 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
206 self.call_cindex(py, "deltachain", args, kw)
210 self.call_cindex(py, "deltachain", args, kw)
207 }
211 }
208
212
209 /// slice planned chunk read to reach a density threshold
213 /// slice planned chunk read to reach a density threshold
210 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
214 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
211 self.call_cindex(py, "slicechunktodensity", args, kw)
215 self.call_cindex(py, "slicechunktodensity", args, kw)
212 }
216 }
213
217
214 /// stats for the index
218 /// stats for the index
215 def stats(&self, *args, **kw) -> PyResult<PyObject> {
219 def stats(&self, *args, **kw) -> PyResult<PyObject> {
216 self.call_cindex(py, "stats", args, kw)
220 self.call_cindex(py, "stats", args, kw)
217 }
221 }
218
222
219 // index_sequence_methods and index_mapping_methods.
223 // index_sequence_methods and index_mapping_methods.
220 //
224 //
221 // Since we call back through the high level Python API,
225 // Since we call back through the high level Python API,
222 // there's no point making a distinction between index_get
226 // there's no point making a distinction between index_get
223 // and index_getitem.
227 // and index_getitem.
224
228
225 def __len__(&self) -> PyResult<usize> {
229 def __len__(&self) -> PyResult<usize> {
226 self.cindex(py).borrow().inner().len(py)
230 self.cindex(py).borrow().inner().len(py)
227 }
231 }
228
232
229 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
233 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
230 // this conversion seems needless, but that's actually because
234 // this conversion seems needless, but that's actually because
231 // `index_getitem` does not handle conversion from PyLong,
235 // `index_getitem` does not handle conversion from PyLong,
232 // which expressions such as [e for e in index] internally use.
236 // which expressions such as [e for e in index] internally use.
233 // Note that we don't seem to have a direct way to call
237 // Note that we don't seem to have a direct way to call
234 // PySequence_GetItem (does the job), which would be better for
238 // PySequence_GetItem (does the job), which would possibly be better
235 // for performance
239 // for performance
236 let key = match key.extract::<Revision>(py) {
240 let key = match key.extract::<Revision>(py) {
237 Ok(rev) => rev.to_py_object(py).into_object(),
241 Ok(rev) => rev.to_py_object(py).into_object(),
238 Err(_) => key,
242 Err(_) => key,
239 };
243 };
240 self.cindex(py).borrow().inner().get_item(py, key)
244 self.cindex(py).borrow().inner().get_item(py, key)
241 }
245 }
242
246
243 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
247 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
244 self.cindex(py).borrow().inner().set_item(py, key, value)
248 self.cindex(py).borrow().inner().set_item(py, key, value)
245 }
249 }
246
250
247 def __contains__(&self, item: PyObject) -> PyResult<bool> {
251 def __contains__(&self, item: PyObject) -> PyResult<bool> {
248 // ObjectProtocol does not seem to provide contains(), so
252 // ObjectProtocol does not seem to provide contains(), so
249 // this is an equivalent implementation of the index_contains()
253 // this is an equivalent implementation of the index_contains()
250 // defined in revlog.c
254 // defined in revlog.c
251 let cindex = self.cindex(py).borrow();
255 let cindex = self.cindex(py).borrow();
252 match item.extract::<Revision>(py) {
256 match item.extract::<Revision>(py) {
253 Ok(rev) => {
257 Ok(rev) => {
254 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
258 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
255 }
259 }
256 Err(_) => {
260 Err(_) => {
257 cindex.inner().call_method(
261 cindex.inner().call_method(
258 py,
262 py,
259 "has_node",
263 "has_node",
260 PyTuple::new(py, &[item]),
264 PyTuple::new(py, &[item]),
261 None)?
265 None)?
262 .extract(py)
266 .extract(py)
263 }
267 }
264 }
268 }
265 }
269 }
266
270
267 def nodemap_data_all(&self) -> PyResult<PyBytes> {
271 def nodemap_data_all(&self) -> PyResult<PyBytes> {
268 self.inner_nodemap_data_all(py)
272 self.inner_nodemap_data_all(py)
269 }
273 }
270
274
271 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
275 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
272 self.inner_nodemap_data_incremental(py)
276 self.inner_nodemap_data_incremental(py)
273 }
277 }
274 def update_nodemap_data(
278 def update_nodemap_data(
275 &self,
279 &self,
276 docket: PyObject,
280 docket: PyObject,
277 nm_data: PyObject
281 nm_data: PyObject
278 ) -> PyResult<PyObject> {
282 ) -> PyResult<PyObject> {
279 self.inner_update_nodemap_data(py, docket, nm_data)
283 self.inner_update_nodemap_data(py, docket, nm_data)
280 }
284 }
281
285
282
286
283 });
287 });
284
288
285 impl MixedIndex {
289 impl MixedIndex {
286 fn new(py: Python, cindex: PyObject) -> PyResult<MixedIndex> {
290 fn new(py: Python, cindex: PyObject) -> PyResult<MixedIndex> {
287 Self::create_instance(
291 Self::create_instance(
288 py,
292 py,
289 RefCell::new(cindex::Index::new(py, cindex)?),
293 RefCell::new(cindex::Index::new(py, cindex)?),
290 RefCell::new(None),
294 RefCell::new(None),
291 RefCell::new(None),
295 RefCell::new(None),
292 RefCell::new(None),
296 RefCell::new(None),
293 )
297 )
294 }
298 }
295
299
296 /// This is scaffolding at this point, but it could also become
300 /// This is scaffolding at this point, but it could also become
297 /// a way to start a persistent nodemap or perform a
301 /// a way to start a persistent nodemap or perform a
298 /// vacuum / repack operation
302 /// vacuum / repack operation
299 fn fill_nodemap(
303 fn fill_nodemap(
300 &self,
304 &self,
301 py: Python,
305 py: Python,
302 nt: &mut NodeTree,
306 nt: &mut NodeTree,
303 ) -> PyResult<PyObject> {
307 ) -> PyResult<PyObject> {
304 let index = self.cindex(py).borrow();
308 let index = self.cindex(py).borrow();
305 for r in 0..index.len() {
309 for r in 0..index.len() {
306 let rev = r as Revision;
310 let rev = r as Revision;
307 // in this case node() won't ever return None
311 // in this case node() won't ever return None
308 nt.insert(&*index, index.node(rev).unwrap(), rev)
312 nt.insert(&*index, index.node(rev).unwrap(), rev)
309 .map_err(|e| nodemap_error(py, e))?
313 .map_err(|e| nodemap_error(py, e))?
310 }
314 }
311 Ok(py.None())
315 Ok(py.None())
312 }
316 }
313
317
314 fn get_nodetree<'a>(
318 fn get_nodetree<'a>(
315 &'a self,
319 &'a self,
316 py: Python<'a>,
320 py: Python<'a>,
317 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
321 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
318 if self.nt(py).borrow().is_none() {
322 if self.nt(py).borrow().is_none() {
319 let readonly = Box::new(Vec::new());
323 let readonly = Box::new(Vec::new());
320 let mut nt = NodeTree::load_bytes(readonly, 0);
324 let mut nt = NodeTree::load_bytes(readonly, 0);
321 self.fill_nodemap(py, &mut nt)?;
325 self.fill_nodemap(py, &mut nt)?;
322 self.nt(py).borrow_mut().replace(nt);
326 self.nt(py).borrow_mut().replace(nt);
323 }
327 }
324 Ok(self.nt(py))
328 Ok(self.nt(py))
325 }
329 }
326
330
327 /// forward a method call to the underlying C index
331 /// forward a method call to the underlying C index
328 fn call_cindex(
332 fn call_cindex(
329 &self,
333 &self,
330 py: Python,
334 py: Python,
331 name: &str,
335 name: &str,
332 args: &PyTuple,
336 args: &PyTuple,
333 kwargs: Option<&PyDict>,
337 kwargs: Option<&PyDict>,
334 ) -> PyResult<PyObject> {
338 ) -> PyResult<PyObject> {
335 self.cindex(py)
339 self.cindex(py)
336 .borrow()
340 .borrow()
337 .inner()
341 .inner()
338 .call_method(py, name, args, kwargs)
342 .call_method(py, name, args, kwargs)
339 }
343 }
340
344
341 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
345 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
342 self.cindex(py).borrow().clone_ref(py)
346 self.cindex(py).borrow().clone_ref(py)
343 }
347 }
344
348
345 /// Returns the full nodemap bytes to be written as-is to disk
349 /// Returns the full nodemap bytes to be written as-is to disk
346 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
350 fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
347 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
351 let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
348 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
352 let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
349
353
350 // If there's anything readonly, we need to build the data again from
354 // If there's anything readonly, we need to build the data again from
351 // scratch
355 // scratch
352 let bytes = if readonly.len() > 0 {
356 let bytes = if readonly.len() > 0 {
353 let mut nt = NodeTree::load_bytes(Box::new(vec![]), 0);
357 let mut nt = NodeTree::load_bytes(Box::new(vec![]), 0);
354 self.fill_nodemap(py, &mut nt)?;
358 self.fill_nodemap(py, &mut nt)?;
355
359
356 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
360 let (readonly, bytes) = nt.into_readonly_and_added_bytes();
357 assert_eq!(readonly.len(), 0);
361 assert_eq!(readonly.len(), 0);
358
362
359 bytes
363 bytes
360 } else {
364 } else {
361 bytes
365 bytes
362 };
366 };
363
367
364 let bytes = PyBytes::new(py, &bytes);
368 let bytes = PyBytes::new(py, &bytes);
365 Ok(bytes)
369 Ok(bytes)
366 }
370 }
367
371
368 /// Returns the last saved docket along with the size of any changed data
372 /// Returns the last saved docket along with the size of any changed data
369 /// (in number of blocks), and said data as bytes.
373 /// (in number of blocks), and said data as bytes.
370 fn inner_nodemap_data_incremental(
374 fn inner_nodemap_data_incremental(
371 &self,
375 &self,
372 py: Python,
376 py: Python,
373 ) -> PyResult<PyObject> {
377 ) -> PyResult<PyObject> {
374 let docket = self.docket(py).borrow();
378 let docket = self.docket(py).borrow();
375 let docket = match docket.as_ref() {
379 let docket = match docket.as_ref() {
376 Some(d) => d,
380 Some(d) => d,
377 None => return Ok(py.None()),
381 None => return Ok(py.None()),
378 };
382 };
379
383
380 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
384 let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
381 let masked_blocks = node_tree.masked_readonly_blocks();
385 let masked_blocks = node_tree.masked_readonly_blocks();
382 let (_, data) = node_tree.into_readonly_and_added_bytes();
386 let (_, data) = node_tree.into_readonly_and_added_bytes();
383 let changed = masked_blocks * std::mem::size_of::<Block>();
387 let changed = masked_blocks * std::mem::size_of::<Block>();
384
388
385 Ok((docket, changed, PyBytes::new(py, &data))
389 Ok((docket, changed, PyBytes::new(py, &data))
386 .to_py_object(py)
390 .to_py_object(py)
387 .into_object())
391 .into_object())
388 }
392 }
389
393
390 /// Update the nodemap from the new (mmaped) data.
394 /// Update the nodemap from the new (mmaped) data.
391 /// The docket is kept as a reference for later incremental calls.
395 /// The docket is kept as a reference for later incremental calls.
392 fn inner_update_nodemap_data(
396 fn inner_update_nodemap_data(
393 &self,
397 &self,
394 py: Python,
398 py: Python,
395 docket: PyObject,
399 docket: PyObject,
396 nm_data: PyObject,
400 nm_data: PyObject,
397 ) -> PyResult<PyObject> {
401 ) -> PyResult<PyObject> {
398 let buf = PyBuffer::get(py, &nm_data)?;
402 let buf = PyBuffer::get(py, &nm_data)?;
399 let len = buf.item_count();
403 let len = buf.item_count();
400
404
401 // Build a slice from the mmap'ed buffer data
405 // Build a slice from the mmap'ed buffer data
402 let cbuf = buf.buf_ptr();
406 let cbuf = buf.buf_ptr();
403 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
407 let bytes = if std::mem::size_of::<u8>() == buf.item_size()
404 && buf.is_c_contiguous()
408 && buf.is_c_contiguous()
405 && u8::is_compatible_format(buf.format())
409 && u8::is_compatible_format(buf.format())
406 {
410 {
407 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
411 unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
408 } else {
412 } else {
409 return Err(PyErr::new::<ValueError, _>(
413 return Err(PyErr::new::<ValueError, _>(
410 py,
414 py,
411 "Nodemap data buffer has an invalid memory representation"
415 "Nodemap data buffer has an invalid memory representation"
412 .to_string(),
416 .to_string(),
413 ));
417 ));
414 };
418 };
415
419
416 // Keep a reference to the mmap'ed buffer, otherwise we get a dangling
420 // Keep a reference to the mmap'ed buffer, otherwise we get a dangling
417 // pointer.
421 // pointer.
418 self.mmap(py).borrow_mut().replace(buf);
422 self.mmap(py).borrow_mut().replace(buf);
419
423
420 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
424 let mut nt = NodeTree::load_bytes(Box::new(bytes), len);
421
425
422 let data_tip =
426 let data_tip =
423 docket.getattr(py, "tip_rev")?.extract::<Revision>(py)?;
427 docket.getattr(py, "tip_rev")?.extract::<Revision>(py)?;
424 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
428 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
425 let idx = self.cindex(py).borrow();
429 let idx = self.cindex(py).borrow();
426 let current_tip = idx.len();
430 let current_tip = idx.len();
427
431
428 for r in (data_tip + 1)..current_tip as Revision {
432 for r in (data_tip + 1)..current_tip as Revision {
429 let rev = r as Revision;
433 let rev = r as Revision;
430 // in this case node() won't ever return None
434 // in this case node() won't ever return None
431 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
435 nt.insert(&*idx, idx.node(rev).unwrap(), rev)
432 .map_err(|e| nodemap_error(py, e))?
436 .map_err(|e| nodemap_error(py, e))?
433 }
437 }
434
438
435 *self.nt(py).borrow_mut() = Some(nt);
439 *self.nt(py).borrow_mut() = Some(nt);
436
440
437 Ok(py.None())
441 Ok(py.None())
438 }
442 }
439 }
443 }
440
444
441 fn revlog_error(py: Python) -> PyErr {
445 fn revlog_error(py: Python) -> PyErr {
442 match py
446 match py
443 .import("mercurial.error")
447 .import("mercurial.error")
444 .and_then(|m| m.get(py, "RevlogError"))
448 .and_then(|m| m.get(py, "RevlogError"))
445 {
449 {
446 Err(e) => e,
450 Err(e) => e,
447 Ok(cls) => PyErr::from_instance(py, cls),
451 Ok(cls) => PyErr::from_instance(py, cls),
448 }
452 }
449 }
453 }
450
454
451 fn rev_not_in_index(py: Python, rev: Revision) -> PyErr {
455 fn rev_not_in_index(py: Python, rev: Revision) -> PyErr {
452 PyErr::new::<ValueError, _>(
456 PyErr::new::<ValueError, _>(
453 py,
457 py,
454 format!(
458 format!(
455 "Inconsistency: Revision {} found in nodemap \
459 "Inconsistency: Revision {} found in nodemap \
456 is not in revlog index",
460 is not in revlog index",
457 rev
461 rev
458 ),
462 ),
459 )
463 )
460 }
464 }
461
465
462 /// Standard treatment of NodeMapError
466 /// Standard treatment of NodeMapError
463 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
467 fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
464 match err {
468 match err {
465 NodeMapError::MultipleResults => revlog_error(py),
469 NodeMapError::MultipleResults => revlog_error(py),
466 NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
470 NodeMapError::RevisionNotInIndex(r) => rev_not_in_index(py, r),
467 NodeMapError::InvalidNodePrefix(s) => invalid_node_prefix(py, &s),
471 NodeMapError::InvalidNodePrefix(s) => invalid_node_prefix(py, &s),
468 }
472 }
469 }
473 }
470
474
471 fn invalid_node_prefix(py: Python, ne: &NodeError) -> PyErr {
475 fn invalid_node_prefix(py: Python, ne: &NodeError) -> PyErr {
472 PyErr::new::<ValueError, _>(
476 PyErr::new::<ValueError, _>(
473 py,
477 py,
474 format!("Invalid node or prefix: {:?}", ne),
478 format!("Invalid node or prefix: {:?}", ne),
475 )
479 )
476 }
480 }
477
481
478 /// Create the module, with __package__ given from parent
482 /// Create the module, with __package__ given from parent
479 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
483 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
480 let dotted_name = &format!("{}.revlog", package);
484 let dotted_name = &format!("{}.revlog", package);
481 let m = PyModule::new(py, dotted_name)?;
485 let m = PyModule::new(py, dotted_name)?;
482 m.add(py, "__package__", package)?;
486 m.add(py, "__package__", package)?;
483 m.add(py, "__doc__", "RevLog - Rust implementations")?;
487 m.add(py, "__doc__", "RevLog - Rust implementations")?;
484
488
485 m.add_class::<MixedIndex>(py)?;
489 m.add_class::<MixedIndex>(py)?;
486
490
487 let sys = PyModule::import(py, "sys")?;
491 let sys = PyModule::import(py, "sys")?;
488 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
492 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
489 sys_modules.set_item(py, dotted_name, &m)?;
493 sys_modules.set_item(py, dotted_name, &m)?;
490
494
491 Ok(m)
495 Ok(m)
492 }
496 }
General Comments 0
You need to be logged in to leave comments. Login now