Show More
@@ -1,3 +1,4 b'' | |||
|
1 | use std::collections::HashSet; | |
|
1 | 2 | use std::fmt::Debug; |
|
2 | 3 | use std::ops::Deref; |
|
3 | 4 | use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; |
@@ -10,7 +11,10 b' use crate::errors::HgError;' | |||
|
10 | 11 | use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES}; |
|
11 | 12 | use crate::revlog::node::Node; |
|
12 | 13 | use crate::revlog::{Revision, NULL_REVISION}; |
|
13 | use crate::{Graph, GraphError, RevlogError, RevlogIndex, UncheckedRevision}; | |
|
14 | use crate::{ | |
|
15 | BaseRevision, FastHashMap, Graph, GraphError, RevlogError, RevlogIndex, | |
|
16 | UncheckedRevision, | |
|
17 | }; | |
|
14 | 18 | |
|
15 | 19 | pub const INDEX_ENTRY_SIZE: usize = 64; |
|
16 | 20 | pub const COMPRESSION_MODE_INLINE: u8 = 2; |
@@ -283,6 +287,35 b' impl Graph for Index {' | |||
|
283 | 287 | } |
|
284 | 288 | } |
|
285 | 289 | |
|
290 | /// A cache suitable for find_snapshots | |
|
291 | /// | |
|
292 | /// Logically equivalent to a mapping whose keys are [`BaseRevision`] and | |
|
293 | /// values sets of [`BaseRevision`] | |
|
294 | /// | |
|
295 | /// TODO the dubious part is insisting that errors must be RevlogError | |
|
296 | /// we would probably need to sprinkle some magic here, such as an associated | |
|
297 | /// type that would be Into<RevlogError> but even that would not be | |
|
298 | /// satisfactory, as errors potentially have nothing to do with the revlog. | |
|
299 | pub trait SnapshotsCache { | |
|
300 | fn insert_for( | |
|
301 | &mut self, | |
|
302 | rev: BaseRevision, | |
|
303 | value: BaseRevision, | |
|
304 | ) -> Result<(), RevlogError>; | |
|
305 | } | |
|
306 | ||
|
307 | impl SnapshotsCache for FastHashMap<BaseRevision, HashSet<BaseRevision>> { | |
|
308 | fn insert_for( | |
|
309 | &mut self, | |
|
310 | rev: BaseRevision, | |
|
311 | value: BaseRevision, | |
|
312 | ) -> Result<(), RevlogError> { | |
|
313 | let all_values = self.entry(rev).or_insert_with(HashSet::new); | |
|
314 | all_values.insert(value); | |
|
315 | Ok(()) | |
|
316 | } | |
|
317 | } | |
|
318 | ||
|
286 | 319 | impl Index { |
|
287 | 320 | /// Create an index from bytes. |
|
288 | 321 | /// Calculate the start of each entry when is_inline is true. |
@@ -479,6 +512,38 b' impl Index {' | |||
|
479 | 512 | } |
|
480 | 513 | } |
|
481 | 514 | |
|
515 | pub fn find_snapshots( | |
|
516 | &self, | |
|
517 | start_rev: UncheckedRevision, | |
|
518 | end_rev: UncheckedRevision, | |
|
519 | cache: &mut impl SnapshotsCache, | |
|
520 | ) -> Result<(), RevlogError> { | |
|
521 | let mut start_rev = start_rev.0; | |
|
522 | let mut end_rev = end_rev.0; | |
|
523 | end_rev += 1; | |
|
524 | let len = self.len().try_into().unwrap(); | |
|
525 | if end_rev > len { | |
|
526 | end_rev = len; | |
|
527 | } | |
|
528 | if start_rev < 0 { | |
|
529 | start_rev = 0; | |
|
530 | } | |
|
531 | for rev in start_rev..end_rev { | |
|
532 | if !self.is_snapshot_unchecked(Revision(rev))? { | |
|
533 | continue; | |
|
534 | } | |
|
535 | let mut base = self | |
|
536 | .get_entry(Revision(rev)) | |
|
537 | .unwrap() | |
|
538 | .base_revision_or_base_of_delta_chain(); | |
|
539 | if base.0 == rev { | |
|
540 | base = NULL_REVISION.into(); | |
|
541 | } | |
|
542 | cache.insert_for(base.0, rev)?; | |
|
543 | } | |
|
544 | Ok(()) | |
|
545 | } | |
|
546 | ||
|
482 | 547 | /// TODO move this to the trait probably, along with other things |
|
483 | 548 | pub fn append( |
|
484 | 549 | &mut self, |
@@ -14,12 +14,14 b' use cpython::{' | |||
|
14 | 14 | buffer::{Element, PyBuffer}, |
|
15 | 15 | exc::{IndexError, ValueError}, |
|
16 | 16 | ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule, |
|
17 |
PyObject, PyResult, PyString, PyTuple, Python, PythonObject, |
|
|
17 | PyObject, PyResult, PySet, PyString, PyTuple, Python, PythonObject, | |
|
18 | ToPyObject, | |
|
18 | 19 | }; |
|
19 | 20 | use hg::{ |
|
20 | index::{IndexHeader, RevisionDataParams}, | |
|
21 | errors::HgError, | |
|
22 | index::{IndexHeader, RevisionDataParams, SnapshotsCache}, | |
|
21 | 23 | nodemap::{Block, NodeMapError, NodeTree}, |
|
22 | revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex}, | |
|
24 | revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex}, | |
|
23 | 25 | BaseRevision, Revision, UncheckedRevision, NULL_REVISION, |
|
24 | 26 | }; |
|
25 | 27 | use std::cell::RefCell; |
@@ -271,7 +273,39 b' py_class!(pub class MixedIndex |py| {' | |||
|
271 | 273 | |
|
272 | 274 | /// Gather snapshot data in a cache dict |
|
273 | 275 | def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { |
|
274 | self.call_cindex(py, "findsnapshots", args, kw) | |
|
276 | let index = self.index(py).borrow(); | |
|
277 | let cache: PyDict = args.get_item(py, 0).extract(py)?; | |
|
278 | // this methods operates by setting new values in the cache, | |
|
279 | // hence we will compare results by letting the C implementation | |
|
280 | // operate over a deepcopy of the cache, and finally compare both | |
|
281 | // caches. | |
|
282 | let c_cache = PyDict::new(py); | |
|
283 | for (k, v) in cache.items(py) { | |
|
284 | c_cache.set_item(py, k, PySet::new(py, v)?)?; | |
|
285 | } | |
|
286 | ||
|
287 | let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?); | |
|
288 | let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?); | |
|
289 | let mut cache_wrapper = PySnapshotsCache{ py, dict: cache }; | |
|
290 | index.find_snapshots( | |
|
291 | start_rev, | |
|
292 | end_rev, | |
|
293 | &mut cache_wrapper, | |
|
294 | ).map_err(|_| revlog_error(py))?; | |
|
295 | ||
|
296 | let c_args = PyTuple::new( | |
|
297 | py, | |
|
298 | &[ | |
|
299 | c_cache.clone_ref(py).into_object(), | |
|
300 | args.get_item(py, 1), | |
|
301 | args.get_item(py, 2) | |
|
302 | ] | |
|
303 | ); | |
|
304 | self.call_cindex(py, "findsnapshots", &c_args, kw)?; | |
|
305 | assert_py_eq(py, "findsnapshots cache", | |
|
306 | &cache_wrapper.into_object(), | |
|
307 | &c_cache.into_object())?; | |
|
308 | Ok(py.None()) | |
|
275 | 309 | } |
|
276 | 310 | |
|
277 | 311 | /// determine revisions with deltas to reconstruct fulltext |
@@ -487,6 +521,39 b' fn revision_data_params_to_py_tuple(' | |||
|
487 | 521 | ) |
|
488 | 522 | } |
|
489 | 523 | |
|
524 | struct PySnapshotsCache<'p> { | |
|
525 | py: Python<'p>, | |
|
526 | dict: PyDict, | |
|
527 | } | |
|
528 | ||
|
529 | impl<'p> PySnapshotsCache<'p> { | |
|
530 | fn into_object(self) -> PyObject { | |
|
531 | self.dict.into_object() | |
|
532 | } | |
|
533 | } | |
|
534 | ||
|
535 | impl<'p> SnapshotsCache for PySnapshotsCache<'p> { | |
|
536 | fn insert_for( | |
|
537 | &mut self, | |
|
538 | rev: BaseRevision, | |
|
539 | value: BaseRevision, | |
|
540 | ) -> Result<(), RevlogError> { | |
|
541 | let pyvalue = value.into_py_object(self.py).into_object(); | |
|
542 | match self.dict.get_item(self.py, rev) { | |
|
543 | Some(obj) => obj | |
|
544 | .extract::<PySet>(self.py) | |
|
545 | .and_then(|set| set.add(self.py, pyvalue)), | |
|
546 | None => PySet::new(self.py, vec![pyvalue]) | |
|
547 | .and_then(|set| self.dict.set_item(self.py, rev, set)), | |
|
548 | } | |
|
549 | .map_err(|_| { | |
|
550 | RevlogError::Other(HgError::unsupported( | |
|
551 | "Error in Python caches handling", | |
|
552 | )) | |
|
553 | }) | |
|
554 | } | |
|
555 | } | |
|
556 | ||
|
490 | 557 | impl MixedIndex { |
|
491 | 558 | fn new( |
|
492 | 559 | py: Python, |
General Comments 0
You need to be logged in to leave comments.
Login now