Show More
@@ -1,3 +1,4 b'' | |||||
|
1 | use std::collections::HashSet; | |||
1 | use std::fmt::Debug; |
|
2 | use std::fmt::Debug; | |
2 | use std::ops::Deref; |
|
3 | use std::ops::Deref; | |
3 | use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; |
|
4 | use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; | |
@@ -10,7 +11,10 b' use crate::errors::HgError;' | |||||
10 | use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES}; |
|
11 | use crate::node::{NODE_BYTES_LENGTH, NULL_NODE, STORED_NODE_ID_BYTES}; | |
11 | use crate::revlog::node::Node; |
|
12 | use crate::revlog::node::Node; | |
12 | use crate::revlog::{Revision, NULL_REVISION}; |
|
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 | pub const INDEX_ENTRY_SIZE: usize = 64; |
|
19 | pub const INDEX_ENTRY_SIZE: usize = 64; | |
16 | pub const COMPRESSION_MODE_INLINE: u8 = 2; |
|
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 | impl Index { |
|
319 | impl Index { | |
287 | /// Create an index from bytes. |
|
320 | /// Create an index from bytes. | |
288 | /// Calculate the start of each entry when is_inline is true. |
|
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 | /// TODO move this to the trait probably, along with other things |
|
547 | /// TODO move this to the trait probably, along with other things | |
483 | pub fn append( |
|
548 | pub fn append( | |
484 | &mut self, |
|
549 | &mut self, |
@@ -14,12 +14,14 b' use cpython::{' | |||||
14 | buffer::{Element, PyBuffer}, |
|
14 | buffer::{Element, PyBuffer}, | |
15 | exc::{IndexError, ValueError}, |
|
15 | exc::{IndexError, ValueError}, | |
16 | ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyModule, |
|
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 | use hg::{ |
|
20 | use hg::{ | |
20 | index::{IndexHeader, RevisionDataParams}, |
|
21 | errors::HgError, | |
|
22 | index::{IndexHeader, RevisionDataParams, SnapshotsCache}, | |||
21 | nodemap::{Block, NodeMapError, NodeTree}, |
|
23 | nodemap::{Block, NodeMapError, NodeTree}, | |
22 | revlog::{nodemap::NodeMap, NodePrefix, RevlogIndex}, |
|
24 | revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex}, | |
23 | BaseRevision, Revision, UncheckedRevision, NULL_REVISION, |
|
25 | BaseRevision, Revision, UncheckedRevision, NULL_REVISION, | |
24 | }; |
|
26 | }; | |
25 | use std::cell::RefCell; |
|
27 | use std::cell::RefCell; | |
@@ -271,7 +273,39 b' py_class!(pub class MixedIndex |py| {' | |||||
271 |
|
273 | |||
272 | /// Gather snapshot data in a cache dict |
|
274 | /// Gather snapshot data in a cache dict | |
273 | def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { |
|
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 | /// determine revisions with deltas to reconstruct fulltext |
|
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 | impl MixedIndex { |
|
557 | impl MixedIndex { | |
491 | fn new( |
|
558 | fn new( | |
492 | py: Python, |
|
559 | py: Python, |
General Comments 0
You need to be logged in to leave comments.
Login now