##// END OF EJS Templates
rust-index: stop using C index...
Georges Racinet -
r52139:41e19e8a default
parent child Browse files
Show More
@@ -14,9 +14,9 b' use crate::{'
14 14 use cpython::{
15 15 buffer::{Element, PyBuffer},
16 16 exc::{IndexError, ValueError},
17 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
19 PythonObject, ToPyObject, UnsafePyLeaked,
17 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, PyModule,
18 PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject,
19 ToPyObject, UnsafePyLeaked,
20 20 };
21 21 use hg::{
22 22 errors::HgError,
@@ -123,14 +123,10 b' py_class!(pub class MixedIndex |py| {'
123 123 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
124 124 let opt = self.get_nodetree(py)?.borrow();
125 125 let nt = opt.as_ref().unwrap();
126 let idx = &*self.cindex(py).borrow();
127 126 let ridx = &*self.index(py).borrow();
128 127 let node = node_from_py_bytes(py, &node)?;
129 128 let rust_rev =
130 129 nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
131 let c_rev =
132 nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?;
133 assert_eq!(rust_rev, c_rev);
134 130 Ok(rust_rev.map(Into::into))
135 131
136 132 }
@@ -202,24 +198,22 b' py_class!(pub class MixedIndex |py| {'
202 198 let node = node_from_py_object(py, &node_bytes)?;
203 199
204 200 let rev = self.len(py)? as BaseRevision;
205 let mut idx = self.cindex(py).borrow_mut();
206 201
207 202 // This is ok since we will just add the revision to the index
208 203 let rev = Revision(rev);
209 idx.append(py, tup.clone_ref(py))?;
210 204 self.index(py)
211 205 .borrow_mut()
212 206 .append(py_tuple_to_revision_data_params(py, tup)?)
213 207 .unwrap();
208 let idx = &*self.index(py).borrow();
214 209 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
215 .insert(&*idx, &node, rev)
210 .insert(idx, &node, rev)
216 211 .map_err(|e| nodemap_error(py, e))?;
217 212 Ok(py.None())
218 213 }
219 214
220 215 def __delitem__(&self, key: PyObject) -> PyResult<()> {
221 216 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
222 self.cindex(py).borrow().inner().del_item(py, &key)?;
223 217 let start = key.getattr(py, "start")?;
224 218 let start = UncheckedRevision(start.extract(py)?);
225 219 let start = self.index(py)
@@ -237,80 +231,60 b' py_class!(pub class MixedIndex |py| {'
237 231 }
238 232
239 233 //
240 // Reforwarded C index API
234 // Index methods previously reforwarded to C index (tp_methods)
235 // Same ordering as in revlog.c
241 236 //
242 237
243 // index_methods (tp_methods). Same ordering as in revlog.c
244
245 238 /// return the gca set of the given revs
246 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
239 def ancestors(&self, *args, **_kw) -> PyResult<PyObject> {
247 240 let rust_res = self.inner_ancestors(py, args)?;
248
249 let c_res = self.call_cindex(py, "ancestors", args, kw)?;
250 // the algorithm should always provide the results in reverse ordering
251 assert_py_eq(py, "ancestors", &rust_res, &c_res)?;
252
253 241 Ok(rust_res)
254 242 }
255 243
256 244 /// return the heads of the common ancestors of the given revs
257 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
245 def commonancestorsheads(&self, *args, **_kw) -> PyResult<PyObject> {
258 246 let rust_res = self.inner_commonancestorsheads(py, args)?;
259
260 let c_res = self.call_cindex(py, "commonancestorsheads", args, kw)?;
261 // the algorithm should always provide the results in reverse ordering
262 assert_py_eq(py, "commonancestorsheads", &rust_res, &c_res)?;
263
264 247 Ok(rust_res)
265 248 }
266 249
267 250 /// Clear the index caches and inner py_class data.
268 251 /// It is Python's responsibility to call `update_nodemap_data` again.
269 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
252 def clearcaches(&self) -> PyResult<PyObject> {
270 253 self.nt(py).borrow_mut().take();
271 254 self.docket(py).borrow_mut().take();
272 255 self.nodemap_mmap(py).borrow_mut().take();
273 256 self.index(py).borrow().clear_caches();
274 self.call_cindex(py, "clearcaches", args, kw)
257 Ok(py.None())
275 258 }
276 259
277 260 /// return the raw binary string representing a revision
278 def entry_binary(&self, *args, **kw) -> PyResult<PyObject> {
261 def entry_binary(&self, *args, **_kw) -> PyResult<PyObject> {
279 262 let rindex = self.index(py).borrow();
280 263 let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
281 264 let rust_bytes = rindex.check_revision(rev).and_then(
282 265 |r| rindex.entry_binary(r))
283 266 .ok_or_else(|| rev_not_in_index(py, rev))?;
284 267 let rust_res = PyBytes::new(py, rust_bytes).into_object();
285
286 let c_res = self.call_cindex(py, "entry_binary", args, kw)?;
287 assert_py_eq(py, "entry_binary", &rust_res, &c_res)?;
288 268 Ok(rust_res)
289 269 }
290 270
291 271 /// return a binary packed version of the header
292 def pack_header(&self, *args, **kw) -> PyResult<PyObject> {
272 def pack_header(&self, *args, **_kw) -> PyResult<PyObject> {
293 273 let rindex = self.index(py).borrow();
294 274 let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
295 275 let rust_res = PyBytes::new(py, &packed).into_object();
296
297 let c_res = self.call_cindex(py, "pack_header", args, kw)?;
298 assert_py_eq(py, "pack_header", &rust_res, &c_res)?;
299 276 Ok(rust_res)
300 277 }
301 278
302 279 /// compute phases
303 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
280 def computephasesmapsets(&self, *args, **_kw) -> PyResult<PyObject> {
304 281 let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?;
305 282 let rust_res = self.inner_computephasesmapsets(py, py_roots)?;
306
307 let c_res = self.call_cindex(py, "computephasesmapsets", args, kw)?;
308 assert_py_eq(py, "computephasesmapsets", &rust_res, &c_res)?;
309 283 Ok(rust_res)
310 284 }
311 285
312 286 /// reachableroots
313 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
287 def reachableroots2(&self, *args, **_kw) -> PyResult<PyObject> {
314 288 let rust_res = self.inner_reachableroots2(
315 289 py,
316 290 UncheckedRevision(args.get_item(py, 0).extract(py)?),
@@ -318,51 +292,34 b' py_class!(pub class MixedIndex |py| {'
318 292 args.get_item(py, 2),
319 293 args.get_item(py, 3).extract(py)?,
320 294 )?;
321
322 let c_res = self.call_cindex(py, "reachableroots2", args, kw)?;
323 // ordering of C result depends on how the computation went, and
324 // Rust result ordering is arbitrary. Hence we compare after
325 // sorting the results (in Python to avoid reconverting everything
326 // back to Rust structs).
327 assert_py_eq_normalized(py, "reachableroots2", &rust_res, &c_res,
328 |v| format!("sorted({})", v))?;
329
330 295 Ok(rust_res)
331 296 }
332 297
333 298 /// get head revisions
334 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
299 def headrevs(&self) -> PyResult<PyObject> {
335 300 let rust_res = self.inner_headrevs(py)?;
336
337 let c_res = self.call_cindex(py, "headrevs", args, kw)?;
338 assert_py_eq(py, "headrevs", &rust_res, &c_res)?;
339 301 Ok(rust_res)
340 302 }
341 303
342 304 /// get filtered head revisions
343 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
305 def headrevsfiltered(&self, *args, **_kw) -> PyResult<PyObject> {
344 306 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
345 let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?;
346
347 assert_py_eq(py, "headrevsfiltered", &rust_res, &c_res)?;
348 307 Ok(rust_res)
349 308 }
350 309
351 310 /// True if the object is a snapshot
352 def issnapshot(&self, *args, **kw) -> PyResult<bool> {
311 def issnapshot(&self, *args, **_kw) -> PyResult<bool> {
353 312 let index = self.index(py).borrow();
354 313 let result = index
355 314 .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
356 315 .map_err(|e| {
357 316 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
358 317 })?;
359 let cresult = self.call_cindex(py, "issnapshot", args, kw)?;
360 assert_eq!(result, cresult.extract(py)?);
361 318 Ok(result)
362 319 }
363 320
364 321 /// Gather snapshot data in a cache dict
365 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
322 def findsnapshots(&self, *args, **_kw) -> PyResult<PyObject> {
366 323 let index = self.index(py).borrow();
367 324 let cache: PyDict = args.get_item(py, 0).extract(py)?;
368 325 // this methods operates by setting new values in the cache,
@@ -382,24 +339,11 b' py_class!(pub class MixedIndex |py| {'
382 339 end_rev,
383 340 &mut cache_wrapper,
384 341 ).map_err(|_| revlog_error(py))?;
385
386 let c_args = PyTuple::new(
387 py,
388 &[
389 c_cache.clone_ref(py).into_object(),
390 args.get_item(py, 1),
391 args.get_item(py, 2)
392 ]
393 );
394 self.call_cindex(py, "findsnapshots", &c_args, kw)?;
395 assert_py_eq(py, "findsnapshots cache",
396 &cache_wrapper.into_object(),
397 &c_cache.into_object())?;
398 342 Ok(py.None())
399 343 }
400 344
401 345 /// determine revisions with deltas to reconstruct fulltext
402 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
346 def deltachain(&self, *args, **_kw) -> PyResult<PyObject> {
403 347 let index = self.index(py).borrow();
404 348 let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
405 349 let stop_rev =
@@ -422,13 +366,7 b' py_class!(pub class MixedIndex |py| {'
422 366 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
423 367 })?;
424 368
425 let cresult = self.call_cindex(py, "deltachain", args, kw)?;
426 let cchain: Vec<BaseRevision> =
427 cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?;
428 369 let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
429 assert_eq!(chain, cchain);
430 assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?);
431
432 370 Ok(
433 371 PyTuple::new(
434 372 py,
@@ -442,16 +380,13 b' py_class!(pub class MixedIndex |py| {'
442 380 }
443 381
444 382 /// slice planned chunk read to reach a density threshold
445 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
383 def slicechunktodensity(&self, *args, **_kw) -> PyResult<PyObject> {
446 384 let rust_res = self.inner_slicechunktodensity(
447 385 py,
448 386 args.get_item(py, 0),
449 387 args.get_item(py, 1).extract(py)?,
450 388 args.get_item(py, 2).extract(py)?
451 389 )?;
452
453 let c_res = self.call_cindex(py, "slicechunktodensity", args, kw)?;
454 assert_py_eq(py, "slicechunktodensity", &rust_res, &c_res)?;
455 390 Ok(rust_res)
456 391 }
457 392
@@ -468,23 +403,6 b' py_class!(pub class MixedIndex |py| {'
468 403
469 404 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
470 405 let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
471
472 // this conversion seems needless, but that's actually because
473 // `index_getitem` does not handle conversion from PyLong,
474 // which expressions such as [e for e in index] internally use.
475 // Note that we don't seem to have a direct way to call
476 // PySequence_GetItem (does the job), which would possibly be better
477 // for performance
478 // gracinet 2023: the above comment can be removed when we use
479 // the pure Rust impl only. Note also that `key` can be a binary
480 // node id.
481 let c_key = match key.extract::<BaseRevision>(py) {
482 Ok(rev) => rev.to_py_object(py).into_object(),
483 Err(_) => key,
484 };
485 let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?;
486
487 assert_py_eq(py, "__getitem__", &rust_res, &c_res)?;
488 406 Ok(rust_res)
489 407 }
490 408
@@ -492,7 +410,6 b' py_class!(pub class MixedIndex |py| {'
492 410 // ObjectProtocol does not seem to provide contains(), so
493 411 // this is an equivalent implementation of the index_contains()
494 412 // defined in revlog.c
495 let cindex = self.cindex(py).borrow();
496 413 match item.extract::<i32>(py) {
497 414 Ok(rev) => {
498 415 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
@@ -500,15 +417,6 b' py_class!(pub class MixedIndex |py| {'
500 417 Err(_) => {
501 418 let item_bytes: PyBytes = item.extract(py)?;
502 419 let rust_res = self.has_node(py, item_bytes)?;
503
504 let c_res = cindex.inner().call_method(
505 py,
506 "has_node",
507 PyTuple::new(py, &[item.clone_ref(py)]),
508 None)?
509 .extract(py)?;
510
511 assert_eq!(rust_res, c_res);
512 420 Ok(rust_res)
513 421 }
514 422 }
@@ -532,11 +440,6 b' py_class!(pub class MixedIndex |py| {'
532 440 @property
533 441 def entry_size(&self) -> PyResult<PyInt> {
534 442 let rust_res: PyInt = INDEX_ENTRY_SIZE.to_py_object(py);
535
536 let c_res = self.cindex(py).borrow().inner()
537 .getattr(py, "entry_size")?;
538 assert_py_eq(py, "entry_size", rust_res.as_object(), &c_res)?;
539
540 443 Ok(rust_res)
541 444 }
542 445
@@ -545,11 +448,6 b' py_class!(pub class MixedIndex |py| {'
545 448 // will be entirely removed when the Rust index yet useful to
546 449 // implement in Rust to detangle things when removing `self.cindex`
547 450 let rust_res: PyInt = 1.to_py_object(py);
548
549 let c_res = self.cindex(py).borrow().inner()
550 .getattr(py, "rust_ext_compat")?;
551 assert_py_eq(py, "rust_ext_compat", rust_res.as_object(), &c_res)?;
552
553 451 Ok(rust_res)
554 452 }
555 453
@@ -672,12 +570,6 b" struct PySnapshotsCache<'p> {"
672 570 dict: PyDict,
673 571 }
674 572
675 impl<'p> PySnapshotsCache<'p> {
676 fn into_object(self) -> PyObject {
677 self.dict.into_object()
678 }
679 }
680
681 573 impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
682 574 fn insert_for(
683 575 &mut self,
@@ -731,8 +623,6 b' impl MixedIndex {'
731 623
732 624 fn len(&self, py: Python) -> PyResult<usize> {
733 625 let rust_index_len = self.index(py).borrow().len();
734 let cindex_len = self.cindex(py).borrow().inner().len(py)?;
735 assert_eq!(rust_index_len, cindex_len);
736 626 Ok(rust_index_len)
737 627 }
738 628
@@ -767,20 +657,6 b' impl MixedIndex {'
767 657 Ok(self.nt(py))
768 658 }
769 659
770 /// forward a method call to the underlying C index
771 fn call_cindex(
772 &self,
773 py: Python,
774 name: &str,
775 args: &PyTuple,
776 kwargs: Option<&PyDict>,
777 ) -> PyResult<PyObject> {
778 self.cindex(py)
779 .borrow()
780 .inner()
781 .call_method(py, name, args, kwargs)
782 }
783
784 660 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
785 661 self.cindex(py).borrow().clone_ref(py)
786 662 }
@@ -1144,51 +1020,6 b' fn nodemap_error(py: Python, err: NodeMa'
1144 1020 }
1145 1021 }
1146 1022
1147 /// assert two Python objects to be equal from a Python point of view
1148 ///
1149 /// `method` is a label for the assertion error message, intended to be the
1150 /// name of the caller.
1151 /// `normalizer` is a function that takes a Python variable name and returns
1152 /// an expression that the conparison will actually use.
1153 /// Foe example: `|v| format!("sorted({})", v)`
1154 fn assert_py_eq_normalized(
1155 py: Python,
1156 method: &str,
1157 rust: &PyObject,
1158 c: &PyObject,
1159 normalizer: impl FnOnce(&str) -> String + Copy,
1160 ) -> PyResult<()> {
1161 let locals = PyDict::new(py);
1162 locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?;
1163 locals.set_item(py, "c".into_py_object(py).into_object(), c)?;
1164 // let lhs = format!(normalizer_fmt, "rust");
1165 // let rhs = format!(normalizer_fmt, "c");
1166 let is_eq: PyBool = py
1167 .eval(
1168 &format!("{} == {}", &normalizer("rust"), &normalizer("c")),
1169 None,
1170 Some(&locals),
1171 )?
1172 .extract(py)?;
1173 assert!(
1174 is_eq.is_true(),
1175 "{} results differ. Rust: {:?} C: {:?} (before any normalization)",
1176 method,
1177 rust,
1178 c
1179 );
1180 Ok(())
1181 }
1182
1183 fn assert_py_eq(
1184 py: Python,
1185 method: &str,
1186 rust: &PyObject,
1187 c: &PyObject,
1188 ) -> PyResult<()> {
1189 assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned())
1190 }
1191
1192 1023 /// Create the module, with __package__ given from parent
1193 1024 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
1194 1025 let dotted_name = &format!("{}.revlog", package);
General Comments 0
You need to be logged in to leave comments. Login now