Mercurial > hg
comparison rust/hg-core/src/revlog/nodemap.rs @ 44387:00d251d32007
rust-nodemap: special case for prefixes of NULL_NODE
We have to behave as though NULL_NODE was stored in the node tree,
although we don't store it.
Differential Revision: https://phab.mercurial-scm.org/D7798
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Tue, 18 Feb 2020 19:11:16 +0100 |
parents | a98ba6983a63 |
children | 5ac1eecc9c64 |
comparison
equal
deleted
inserted
replaced
44386:8f7c6656ac79 | 44387:00d251d32007 |
---|---|
11 //! | 11 //! |
12 //! Following existing implicit conventions, the "nodemap" terminology | 12 //! Following existing implicit conventions, the "nodemap" terminology |
13 //! is used in a more abstract context. | 13 //! is used in a more abstract context. |
14 | 14 |
15 use super::{ | 15 use super::{ |
16 Node, NodeError, NodePrefix, NodePrefixRef, Revision, RevlogIndex, | 16 node::NULL_NODE, Node, NodeError, NodePrefix, NodePrefixRef, Revision, |
17 RevlogIndex, NULL_REVISION, | |
17 }; | 18 }; |
18 | 19 |
19 use std::fmt; | 20 use std::fmt; |
20 use std::mem; | 21 use std::mem; |
21 use std::ops::Deref; | 22 use std::ops::Deref; |
268 None | 269 None |
269 } | 270 } |
270 }) | 271 }) |
271 } | 272 } |
272 | 273 |
274 /// validate that the candidate's node starts indeed with given prefix, | |
275 /// and treat ambiguities related to `NULL_REVISION`. | |
276 /// | |
277 /// From the data in the NodeTree, one can only conclude that some | |
278 /// revision is the only one for a *subprefix* of the one being looked up. | |
279 fn validate_candidate( | |
280 idx: &impl RevlogIndex, | |
281 prefix: NodePrefixRef, | |
282 rev: Option<Revision>, | |
283 ) -> Result<Option<Revision>, NodeMapError> { | |
284 if prefix.is_prefix_of(&NULL_NODE) { | |
285 // NULL_REVISION always matches a prefix made only of zeros | |
286 // and any other *valid* result is an ambiguity | |
287 match rev { | |
288 None => Ok(Some(NULL_REVISION)), | |
289 Some(r) => match has_prefix_or_none(idx, prefix, r)? { | |
290 None => Ok(Some(NULL_REVISION)), | |
291 _ => Err(NodeMapError::MultipleResults), | |
292 }, | |
293 } | |
294 } else { | |
295 rev.map_or(Ok(None), |r| has_prefix_or_none(idx, prefix, r)) | |
296 } | |
297 } | |
298 | |
273 impl NodeTree { | 299 impl NodeTree { |
274 /// Initiate a NodeTree from an immutable slice-like of `Block` | 300 /// Initiate a NodeTree from an immutable slice-like of `Block` |
275 /// | 301 /// |
276 /// We keep `readonly` and clone its root block if it isn't empty. | 302 /// We keep `readonly` and clone its root block if it isn't empty. |
277 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self { | 303 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self { |
359 fn is_empty(&self) -> bool { | 385 fn is_empty(&self) -> bool { |
360 false | 386 false |
361 } | 387 } |
362 | 388 |
363 /// Main working method for `NodeTree` searches | 389 /// Main working method for `NodeTree` searches |
364 /// | |
365 /// This partial implementation lacks special cases for NULL_REVISION | |
366 fn lookup<'p>( | 390 fn lookup<'p>( |
367 &self, | 391 &self, |
368 prefix: NodePrefixRef<'p>, | 392 prefix: NodePrefixRef<'p>, |
369 ) -> Result<Option<Revision>, NodeMapError> { | 393 ) -> Result<Option<Revision>, NodeMapError> { |
370 for visit_item in self.visit(prefix) { | 394 for visit_item in self.visit(prefix) { |
611 fn find_bin<'a>( | 635 fn find_bin<'a>( |
612 &self, | 636 &self, |
613 idx: &impl RevlogIndex, | 637 idx: &impl RevlogIndex, |
614 prefix: NodePrefixRef<'a>, | 638 prefix: NodePrefixRef<'a>, |
615 ) -> Result<Option<Revision>, NodeMapError> { | 639 ) -> Result<Option<Revision>, NodeMapError> { |
616 self.lookup(prefix.clone()).and_then(|opt| { | 640 validate_candidate(idx, prefix.clone(), self.lookup(prefix)?) |
617 opt.map_or(Ok(None), |rev| has_prefix_or_none(idx, prefix, rev)) | |
618 }) | |
619 } | 641 } |
620 } | 642 } |
621 | 643 |
622 #[cfg(test)] | 644 #[cfg(test)] |
623 mod tests { | 645 mod tests { |
746 | 768 |
747 let nt = sample_nodetree(); | 769 let nt = sample_nodetree(); |
748 | 770 |
749 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults)); | 771 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults)); |
750 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9))); | 772 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9))); |
751 assert_eq!(nt.find_hex(&idx, "00"), Ok(Some(0))); | 773 assert_eq!(nt.find_hex(&idx, "00"), Err(MultipleResults)); |
752 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0))); | 774 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0))); |
775 assert_eq!(nt.find_hex(&idx, "000"), Ok(Some(NULL_REVISION))); | |
753 } | 776 } |
754 | 777 |
755 #[test] | 778 #[test] |
756 fn test_mutated_find() -> Result<(), NodeMapError> { | 779 fn test_mutated_find() -> Result<(), NodeMapError> { |
757 let mut idx = TestIndex::new(); | 780 let mut idx = TestIndex::new(); |
766 growable: vec![block![0: Rev(1), 5: Rev(3)]], | 789 growable: vec![block![0: Rev(1), 5: Rev(3)]], |
767 root: block![0: Block(1), 1:Block(3), 12: Rev(2)], | 790 root: block![0: Block(1), 1:Block(3), 12: Rev(2)], |
768 }; | 791 }; |
769 assert_eq!(nt.find_hex(&idx, "10")?, Some(1)); | 792 assert_eq!(nt.find_hex(&idx, "10")?, Some(1)); |
770 assert_eq!(nt.find_hex(&idx, "c")?, Some(2)); | 793 assert_eq!(nt.find_hex(&idx, "c")?, Some(2)); |
771 assert_eq!(nt.find_hex(&idx, "00")?, Some(0)); | 794 assert_eq!(nt.find_hex(&idx, "00"), Err(MultipleResults)); |
795 assert_eq!(nt.find_hex(&idx, "000")?, Some(NULL_REVISION)); | |
772 assert_eq!(nt.find_hex(&idx, "01")?, Some(9)); | 796 assert_eq!(nt.find_hex(&idx, "01")?, Some(9)); |
773 Ok(()) | 797 Ok(()) |
774 } | 798 } |
775 | 799 |
776 struct TestNtIndex { | 800 struct TestNtIndex { |