rust/hg-cpython/src/revlog.rs
changeset 51246 41e19e8a6133
parent 51245 0b81440e2a73
child 51247 8dbd985733ff
equal deleted inserted replaced
51245:0b81440e2a73 51246:41e19e8a6133
    12     PyRevision,
    12     PyRevision,
    13 };
    13 };
    14 use cpython::{
    14 use cpython::{
    15     buffer::{Element, PyBuffer},
    15     buffer::{Element, PyBuffer},
    16     exc::{IndexError, ValueError},
    16     exc::{IndexError, ValueError},
    17     ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
    17     ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, PyModule,
    18     PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
    18     PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject,
    19     PythonObject, ToPyObject, UnsafePyLeaked,
    19     ToPyObject, UnsafePyLeaked,
    20 };
    20 };
    21 use hg::{
    21 use hg::{
    22     errors::HgError,
    22     errors::HgError,
    23     index::{
    23     index::{
    24         IndexHeader, Phase, RevisionDataParams, SnapshotsCache,
    24         IndexHeader, Phase, RevisionDataParams, SnapshotsCache,
   121     /// Return Revision if found, raises a bare `error.RevlogError`
   121     /// Return Revision if found, raises a bare `error.RevlogError`
   122     /// in case of ambiguity, same as C version does
   122     /// in case of ambiguity, same as C version does
   123     def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
   123     def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
   124         let opt = self.get_nodetree(py)?.borrow();
   124         let opt = self.get_nodetree(py)?.borrow();
   125         let nt = opt.as_ref().unwrap();
   125         let nt = opt.as_ref().unwrap();
   126         let idx = &*self.cindex(py).borrow();
       
   127         let ridx = &*self.index(py).borrow();
   126         let ridx = &*self.index(py).borrow();
   128         let node = node_from_py_bytes(py, &node)?;
   127         let node = node_from_py_bytes(py, &node)?;
   129         let rust_rev =
   128         let rust_rev =
   130             nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
   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         Ok(rust_rev.map(Into::into))
   130         Ok(rust_rev.map(Into::into))
   135 
   131 
   136     }
   132     }
   137 
   133 
   138     /// same as `get_rev()` but raises a bare `error.RevlogError` if node
   134     /// same as `get_rev()` but raises a bare `error.RevlogError` if node
   200         }
   196         }
   201         let node_bytes = tup.get_item(py, 7).extract(py)?;
   197         let node_bytes = tup.get_item(py, 7).extract(py)?;
   202         let node = node_from_py_object(py, &node_bytes)?;
   198         let node = node_from_py_object(py, &node_bytes)?;
   203 
   199 
   204         let rev = self.len(py)? as BaseRevision;
   200         let rev = self.len(py)? as BaseRevision;
   205         let mut idx = self.cindex(py).borrow_mut();
       
   206 
   201 
   207         // This is ok since we will just add the revision to the index
   202         // This is ok since we will just add the revision to the index
   208         let rev = Revision(rev);
   203         let rev = Revision(rev);
   209         idx.append(py, tup.clone_ref(py))?;
       
   210         self.index(py)
   204         self.index(py)
   211             .borrow_mut()
   205             .borrow_mut()
   212             .append(py_tuple_to_revision_data_params(py, tup)?)
   206             .append(py_tuple_to_revision_data_params(py, tup)?)
   213             .unwrap();
   207             .unwrap();
       
   208         let idx = &*self.index(py).borrow();
   214         self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
   209         self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
   215             .insert(&*idx, &node, rev)
   210             .insert(idx, &node, rev)
   216             .map_err(|e| nodemap_error(py, e))?;
   211             .map_err(|e| nodemap_error(py, e))?;
   217         Ok(py.None())
   212         Ok(py.None())
   218     }
   213     }
   219 
   214 
   220     def __delitem__(&self, key: PyObject) -> PyResult<()> {
   215     def __delitem__(&self, key: PyObject) -> PyResult<()> {
   221         // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
   216         // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
   222         self.cindex(py).borrow().inner().del_item(py, &key)?;
       
   223         let start = key.getattr(py, "start")?;
   217         let start = key.getattr(py, "start")?;
   224         let start = UncheckedRevision(start.extract(py)?);
   218         let start = UncheckedRevision(start.extract(py)?);
   225         let start = self.index(py)
   219         let start = self.index(py)
   226             .borrow()
   220             .borrow()
   227             .check_revision(start)
   221             .check_revision(start)
   235         self.fill_nodemap(py, nt)?;
   229         self.fill_nodemap(py, nt)?;
   236         Ok(())
   230         Ok(())
   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     /// return the gca set of the given revs
   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         let rust_res = self.inner_ancestors(py, args)?;
   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         Ok(rust_res)
   241         Ok(rust_res)
   254     }
   242     }
   255 
   243 
   256     /// return the heads of the common ancestors of the given revs
   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         let rust_res = self.inner_commonancestorsheads(py, args)?;
   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         Ok(rust_res)
   247         Ok(rust_res)
   265     }
   248     }
   266 
   249 
   267     /// Clear the index caches and inner py_class data.
   250     /// Clear the index caches and inner py_class data.
   268     /// It is Python's responsibility to call `update_nodemap_data` again.
   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         self.nt(py).borrow_mut().take();
   253         self.nt(py).borrow_mut().take();
   271         self.docket(py).borrow_mut().take();
   254         self.docket(py).borrow_mut().take();
   272         self.nodemap_mmap(py).borrow_mut().take();
   255         self.nodemap_mmap(py).borrow_mut().take();
   273         self.index(py).borrow().clear_caches();
   256         self.index(py).borrow().clear_caches();
   274         self.call_cindex(py, "clearcaches", args, kw)
   257         Ok(py.None())
   275     }
   258     }
   276 
   259 
   277     /// return the raw binary string representing a revision
   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         let rindex = self.index(py).borrow();
   262         let rindex = self.index(py).borrow();
   280         let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
   263         let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
   281         let rust_bytes = rindex.check_revision(rev).and_then(
   264         let rust_bytes = rindex.check_revision(rev).and_then(
   282             |r| rindex.entry_binary(r))
   265             |r| rindex.entry_binary(r))
   283             .ok_or_else(|| rev_not_in_index(py, rev))?;
   266             .ok_or_else(|| rev_not_in_index(py, rev))?;
   284         let rust_res = PyBytes::new(py, rust_bytes).into_object();
   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         Ok(rust_res)
   268         Ok(rust_res)
   289     }
   269     }
   290 
   270 
   291     /// return a binary packed version of the header
   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         let rindex = self.index(py).borrow();
   273         let rindex = self.index(py).borrow();
   294         let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
   274         let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
   295         let rust_res = PyBytes::new(py, &packed).into_object();
   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         Ok(rust_res)
   276         Ok(rust_res)
   300     }
   277     }
   301 
   278 
   302     /// compute phases
   279     /// compute phases
   303     def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
   280     def computephasesmapsets(&self, *args, **_kw) -> PyResult<PyObject> {
   304         let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?;
   281         let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?;
   305         let rust_res = self.inner_computephasesmapsets(py, py_roots)?;
   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         Ok(rust_res)
   283         Ok(rust_res)
   310     }
   284     }
   311 
   285 
   312     /// reachableroots
   286     /// reachableroots
   313     def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
   287     def reachableroots2(&self, *args, **_kw) -> PyResult<PyObject> {
   314         let rust_res = self.inner_reachableroots2(
   288         let rust_res = self.inner_reachableroots2(
   315             py,
   289             py,
   316             UncheckedRevision(args.get_item(py, 0).extract(py)?),
   290             UncheckedRevision(args.get_item(py, 0).extract(py)?),
   317             args.get_item(py, 1),
   291             args.get_item(py, 1),
   318             args.get_item(py, 2),
   292             args.get_item(py, 2),
   319             args.get_item(py, 3).extract(py)?,
   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         Ok(rust_res)
   295         Ok(rust_res)
   331     }
   296     }
   332 
   297 
   333     /// get head revisions
   298     /// get head revisions
   334     def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
   299     def headrevs(&self) -> PyResult<PyObject> {
   335         let rust_res = self.inner_headrevs(py)?;
   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         Ok(rust_res)
   301         Ok(rust_res)
   340     }
   302     }
   341 
   303 
   342     /// get filtered head revisions
   304     /// get filtered head revisions
   343     def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
   305     def headrevsfiltered(&self, *args, **_kw) -> PyResult<PyObject> {
   344         let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
   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         Ok(rust_res)
   307         Ok(rust_res)
   349     }
   308     }
   350 
   309 
   351     /// True if the object is a snapshot
   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         let index = self.index(py).borrow();
   312         let index = self.index(py).borrow();
   354         let result = index
   313         let result = index
   355             .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
   314             .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
   356             .map_err(|e| {
   315             .map_err(|e| {
   357                 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
   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         Ok(result)
   318         Ok(result)
   362     }
   319     }
   363 
   320 
   364     /// Gather snapshot data in a cache dict
   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         let index = self.index(py).borrow();
   323         let index = self.index(py).borrow();
   367         let cache: PyDict = args.get_item(py, 0).extract(py)?;
   324         let cache: PyDict = args.get_item(py, 0).extract(py)?;
   368         // this methods operates by setting new values in the cache,
   325         // this methods operates by setting new values in the cache,
   369         // hence we will compare results by letting the C implementation
   326         // hence we will compare results by letting the C implementation
   370         // operate over a deepcopy of the cache, and finally compare both
   327         // operate over a deepcopy of the cache, and finally compare both
   380         index.find_snapshots(
   337         index.find_snapshots(
   381             start_rev,
   338             start_rev,
   382             end_rev,
   339             end_rev,
   383             &mut cache_wrapper,
   340             &mut cache_wrapper,
   384         ).map_err(|_| revlog_error(py))?;
   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         Ok(py.None())
   342         Ok(py.None())
   399     }
   343     }
   400 
   344 
   401     /// determine revisions with deltas to reconstruct fulltext
   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         let index = self.index(py).borrow();
   347         let index = self.index(py).borrow();
   404         let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
   348         let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
   405         let stop_rev =
   349         let stop_rev =
   406             args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
   350             args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
   407         let rev = index.check_revision(rev).ok_or_else(|| {
   351         let rev = index.check_revision(rev).ok_or_else(|| {
   420             rev, stop_rev, using_general_delta
   364             rev, stop_rev, using_general_delta
   421         ).map_err(|e| {
   365         ).map_err(|e| {
   422             PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
   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         let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
   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         Ok(
   370         Ok(
   433             PyTuple::new(
   371             PyTuple::new(
   434                 py,
   372                 py,
   435                 &[
   373                 &[
   436                     chain.into_py_object(py).into_object(),
   374                     chain.into_py_object(py).into_object(),
   440         )
   378         )
   441 
   379 
   442     }
   380     }
   443 
   381 
   444     /// slice planned chunk read to reach a density threshold
   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         let rust_res = self.inner_slicechunktodensity(
   384         let rust_res = self.inner_slicechunktodensity(
   447             py,
   385             py,
   448             args.get_item(py, 0),
   386             args.get_item(py, 0),
   449             args.get_item(py, 1).extract(py)?,
   387             args.get_item(py, 1).extract(py)?,
   450             args.get_item(py, 2).extract(py)?
   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         Ok(rust_res)
   390         Ok(rust_res)
   456     }
   391     }
   457 
   392 
   458     // index_sequence_methods and index_mapping_methods.
   393     // index_sequence_methods and index_mapping_methods.
   459     //
   394     //
   466         self.len(py)
   401         self.len(py)
   467     }
   402     }
   468 
   403 
   469     def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
   404     def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
   470         let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
   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         Ok(rust_res)
   406         Ok(rust_res)
   489     }
   407     }
   490 
   408 
   491     def __contains__(&self, item: PyObject) -> PyResult<bool> {
   409     def __contains__(&self, item: PyObject) -> PyResult<bool> {
   492         // ObjectProtocol does not seem to provide contains(), so
   410         // ObjectProtocol does not seem to provide contains(), so
   493         // this is an equivalent implementation of the index_contains()
   411         // this is an equivalent implementation of the index_contains()
   494         // defined in revlog.c
   412         // defined in revlog.c
   495         let cindex = self.cindex(py).borrow();
       
   496         match item.extract::<i32>(py) {
   413         match item.extract::<i32>(py) {
   497             Ok(rev) => {
   414             Ok(rev) => {
   498                 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
   415                 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
   499             }
   416             }
   500             Err(_) => {
   417             Err(_) => {
   501                 let item_bytes: PyBytes = item.extract(py)?;
   418                 let item_bytes: PyBytes = item.extract(py)?;
   502                 let rust_res = self.has_node(py, item_bytes)?;
   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                 Ok(rust_res)
   420                 Ok(rust_res)
   513             }
   421             }
   514         }
   422         }
   515     }
   423     }
   516 
   424 
   530     }
   438     }
   531 
   439 
   532     @property
   440     @property
   533     def entry_size(&self) -> PyResult<PyInt> {
   441     def entry_size(&self) -> PyResult<PyInt> {
   534         let rust_res: PyInt = INDEX_ENTRY_SIZE.to_py_object(py);
   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         Ok(rust_res)
   443         Ok(rust_res)
   541     }
   444     }
   542 
   445 
   543     @property
   446     @property
   544     def rust_ext_compat(&self) -> PyResult<PyInt> {
   447     def rust_ext_compat(&self) -> PyResult<PyInt> {
   545         // will be entirely removed when the Rust index yet useful to
   448         // will be entirely removed when the Rust index yet useful to
   546         // implement in Rust to detangle things when removing `self.cindex`
   449         // implement in Rust to detangle things when removing `self.cindex`
   547         let rust_res: PyInt = 1.to_py_object(py);
   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         Ok(rust_res)
   451         Ok(rust_res)
   554     }
   452     }
   555 
   453 
   556 });
   454 });
   557 
   455 
   670 struct PySnapshotsCache<'p> {
   568 struct PySnapshotsCache<'p> {
   671     py: Python<'p>,
   569     py: Python<'p>,
   672     dict: PyDict,
   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 impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
   573 impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
   682     fn insert_for(
   574     fn insert_for(
   683         &mut self,
   575         &mut self,
   684         rev: BaseRevision,
   576         rev: BaseRevision,
   685         value: BaseRevision,
   577         value: BaseRevision,
   729         )
   621         )
   730     }
   622     }
   731 
   623 
   732     fn len(&self, py: Python) -> PyResult<usize> {
   624     fn len(&self, py: Python) -> PyResult<usize> {
   733         let rust_index_len = self.index(py).borrow().len();
   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         Ok(rust_index_len)
   626         Ok(rust_index_len)
   737     }
   627     }
   738 
   628 
   739     /// This is scaffolding at this point, but it could also become
   629     /// This is scaffolding at this point, but it could also become
   740     /// a way to start a persistent nodemap or perform a
   630     /// a way to start a persistent nodemap or perform a
   763             let mut nt = NodeTree::load_bytes(readonly, 0);
   653             let mut nt = NodeTree::load_bytes(readonly, 0);
   764             self.fill_nodemap(py, &mut nt)?;
   654             self.fill_nodemap(py, &mut nt)?;
   765             self.nt(py).borrow_mut().replace(nt);
   655             self.nt(py).borrow_mut().replace(nt);
   766         }
   656         }
   767         Ok(self.nt(py))
   657         Ok(self.nt(py))
   768     }
       
   769 
       
   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     }
   658     }
   783 
   659 
   784     pub fn clone_cindex(&self, py: Python) -> cindex::Index {
   660     pub fn clone_cindex(&self, py: Python) -> cindex::Index {
   785         self.cindex(py).borrow().clone_ref(py)
   661         self.cindex(py).borrow().clone_ref(py)
   786     }
   662     }
  1142         NodeMapError::MultipleResults => revlog_error(py),
  1018         NodeMapError::MultipleResults => revlog_error(py),
  1143         NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
  1019         NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
  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 /// Create the module, with __package__ given from parent
  1023 /// Create the module, with __package__ given from parent
  1193 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
  1024 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
  1194     let dotted_name = &format!("{}.revlog", package);
  1025     let dotted_name = &format!("{}.revlog", package);
  1195     let m = PyModule::new(py, dotted_name)?;
  1026     let m = PyModule::new(py, dotted_name)?;
  1196     m.add(py, "__package__", package)?;
  1027     m.add(py, "__package__", package)?;