##// END OF EJS Templates
rust-nodemap: insert method...
Georges Racinet -
r44831:d2da8667 default
parent child Browse files
Show More
@@ -1,526 +1,790 b''
1 1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 2 // and Mercurial contributors
3 3 //
4 4 // This software may be used and distributed according to the terms of the
5 5 // GNU General Public License version 2 or any later version.
6 6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
7 7 //!
8 8 //! This provides a variation on the 16-ary radix tree that is
9 9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
10 10 //! on disk.
11 11 //!
12 12 //! Following existing implicit conventions, the "nodemap" terminology
13 13 //! is used in a more abstract context.
14 14
15 15 use super::{
16 16 Node, NodeError, NodePrefix, NodePrefixRef, Revision, RevlogIndex,
17 17 };
18
18 19 use std::fmt;
19 20 use std::ops::Deref;
20 21 use std::ops::Index;
21 22
22 23 #[derive(Debug, PartialEq)]
23 24 pub enum NodeMapError {
24 25 MultipleResults,
25 26 InvalidNodePrefix(NodeError),
26 27 /// A `Revision` stored in the nodemap could not be found in the index
27 28 RevisionNotInIndex(Revision),
28 29 }
29 30
30 31 impl From<NodeError> for NodeMapError {
31 32 fn from(err: NodeError) -> Self {
32 33 NodeMapError::InvalidNodePrefix(err)
33 34 }
34 35 }
35 36
36 37 /// Mapping system from Mercurial nodes to revision numbers.
37 38 ///
38 39 /// ## `RevlogIndex` and `NodeMap`
39 40 ///
40 41 /// One way to think about their relationship is that
41 42 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
42 43 /// carried by a [`RevlogIndex`].
43 44 ///
44 45 /// Many of the methods in this trait take a `RevlogIndex` argument
45 46 /// which is used for validation of their results. This index must naturally
46 47 /// be the one the `NodeMap` is about, and it must be consistent.
47 48 ///
48 49 /// Notably, the `NodeMap` must not store
49 50 /// information about more `Revision` values than there are in the index.
50 51 /// In these methods, an encountered `Revision` is not in the index, a
51 52 /// [`RevisionNotInIndex`] error is returned.
52 53 ///
53 54 /// In insert operations, the rule is thus that the `NodeMap` must always
54 55 /// be updated after the `RevlogIndex`
55 56 /// be updated first, and the `NodeMap` second.
56 57 ///
57 58 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
58 59 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
59 60 pub trait NodeMap {
60 61 /// Find the unique `Revision` having the given `Node`
61 62 ///
62 63 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
63 64 fn find_node(
64 65 &self,
65 66 index: &impl RevlogIndex,
66 67 node: &Node,
67 68 ) -> Result<Option<Revision>, NodeMapError> {
68 69 self.find_bin(index, node.into())
69 70 }
70 71
71 72 /// Find the unique Revision whose `Node` starts with a given binary prefix
72 73 ///
73 74 /// If no Revision matches the given prefix, `Ok(None)` is returned.
74 75 ///
75 76 /// If several Revisions match the given prefix, a [`MultipleResults`]
76 77 /// error is returned.
77 78 fn find_bin<'a>(
78 79 &self,
79 80 idx: &impl RevlogIndex,
80 81 prefix: NodePrefixRef<'a>,
81 82 ) -> Result<Option<Revision>, NodeMapError>;
82 83
83 84 /// Find the unique Revision whose `Node` hexadecimal string representation
84 85 /// starts with a given prefix
85 86 ///
86 87 /// If no Revision matches the given prefix, `Ok(None)` is returned.
87 88 ///
88 89 /// If several Revisions match the given prefix, a [`MultipleResults`]
89 90 /// error is returned.
90 91 fn find_hex(
91 92 &self,
92 93 idx: &impl RevlogIndex,
93 94 prefix: &str,
94 95 ) -> Result<Option<Revision>, NodeMapError> {
95 96 self.find_bin(idx, NodePrefix::from_hex(prefix)?.borrow())
96 97 }
97 98 }
98 99
100 pub trait MutableNodeMap: NodeMap {
101 fn insert<I: RevlogIndex>(
102 &mut self,
103 index: &I,
104 node: &Node,
105 rev: Revision,
106 ) -> Result<(), NodeMapError>;
107 }
108
99 109 /// Low level NodeTree [`Blocks`] elements
100 110 ///
101 111 /// These are exactly as for instance on persistent storage.
102 112 type RawElement = i32;
103 113
104 114 /// High level representation of values in NodeTree
105 115 /// [`Blocks`](struct.Block.html)
106 116 ///
107 117 /// This is the high level representation that most algorithms should
108 118 /// use.
109 119 #[derive(Clone, Debug, Eq, PartialEq)]
110 120 enum Element {
111 121 Rev(Revision),
112 122 Block(usize),
113 123 None,
114 124 }
115 125
116 126 impl From<RawElement> for Element {
117 127 /// Conversion from low level representation, after endianness conversion.
118 128 ///
119 129 /// See [`Block`](struct.Block.html) for explanation about the encoding.
120 130 fn from(raw: RawElement) -> Element {
121 131 if raw >= 0 {
122 132 Element::Block(raw as usize)
123 133 } else if raw == -1 {
124 134 Element::None
125 135 } else {
126 136 Element::Rev(-raw - 2)
127 137 }
128 138 }
129 139 }
130 140
131 141 impl From<Element> for RawElement {
132 142 fn from(element: Element) -> RawElement {
133 143 match element {
134 144 Element::None => 0,
135 145 Element::Block(i) => i as RawElement,
136 146 Element::Rev(rev) => -rev - 2,
137 147 }
138 148 }
139 149 }
140 150
141 151 /// A logical block of the `NodeTree`, packed with a fixed size.
142 152 ///
143 153 /// These are always used in container types implementing `Index<Block>`,
144 154 /// such as `&Block`
145 155 ///
146 156 /// As an array of integers, its ith element encodes that the
147 157 /// ith potential edge from the block, representing the ith hexadecimal digit
148 158 /// (nybble) `i` is either:
149 159 ///
150 160 /// - absent (value -1)
151 161 /// - another `Block` in the same indexable container (value β‰₯ 0)
152 162 /// - a `Revision` leaf (value ≀ -2)
153 163 ///
154 164 /// Endianness has to be fixed for consistency on shared storage across
155 165 /// different architectures.
156 166 ///
157 167 /// A key difference with the C `nodetree` is that we need to be
158 168 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
159 169 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
160 170 ///
161 171 /// Another related difference is that `NULL_REVISION` (-1) is not
162 172 /// represented at all, because we want an immutable empty nodetree
163 173 /// to be valid.
164 174
165 175 #[derive(Clone, PartialEq)]
166 176 pub struct Block([RawElement; 16]);
167 177
168 178 impl Block {
169 179 fn new() -> Self {
170 180 Block([-1; 16])
171 181 }
172 182
173 183 fn get(&self, nybble: u8) -> Element {
174 184 Element::from(RawElement::from_be(self.0[nybble as usize]))
175 185 }
176 186
177 187 fn set(&mut self, nybble: u8, element: Element) {
178 188 self.0[nybble as usize] = RawElement::to_be(element.into())
179 189 }
180 190 }
181 191
182 192 impl fmt::Debug for Block {
183 193 /// sparse representation for testing and debugging purposes
184 194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 195 f.debug_map()
186 196 .entries((0..16).filter_map(|i| match self.get(i) {
187 197 Element::None => None,
188 198 element => Some((i, element)),
189 199 }))
190 200 .finish()
191 201 }
192 202 }
193 203
194 204 /// A mutable 16-radix tree with the root block logically at the end
195 205 ///
196 206 /// Because of the append only nature of our node trees, we need to
197 207 /// keep the original untouched and store new blocks separately.
198 208 ///
199 209 /// The mutable root `Block` is kept apart so that we don't have to rebump
200 210 /// it on each insertion.
201 211 pub struct NodeTree {
202 212 readonly: Box<dyn Deref<Target = [Block]> + Send>,
203 213 growable: Vec<Block>,
204 214 root: Block,
205 215 }
206 216
207 217 impl Index<usize> for NodeTree {
208 218 type Output = Block;
209 219
210 220 fn index(&self, i: usize) -> &Block {
211 221 let ro_len = self.readonly.len();
212 222 if i < ro_len {
213 223 &self.readonly[i]
214 224 } else if i == ro_len + self.growable.len() {
215 225 &self.root
216 226 } else {
217 227 &self.growable[i - ro_len]
218 228 }
219 229 }
220 230 }
221 231
222 232 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
223 233 fn has_prefix_or_none<'p>(
224 234 idx: &impl RevlogIndex,
225 235 prefix: NodePrefixRef<'p>,
226 236 rev: Revision,
227 237 ) -> Result<Option<Revision>, NodeMapError> {
228 238 idx.node(rev)
229 239 .ok_or_else(|| NodeMapError::RevisionNotInIndex(rev))
230 240 .map(|node| {
231 241 if prefix.is_prefix_of(node) {
232 242 Some(rev)
233 243 } else {
234 244 None
235 245 }
236 246 })
237 247 }
238 248
239 249 impl NodeTree {
240 250 /// Initiate a NodeTree from an immutable slice-like of `Block`
241 251 ///
242 252 /// We keep `readonly` and clone its root block if it isn't empty.
243 253 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self {
244 254 let root = readonly
245 255 .last()
246 256 .map(|b| b.clone())
247 257 .unwrap_or_else(|| Block::new());
248 258 NodeTree {
249 259 readonly: readonly,
250 260 growable: Vec::new(),
251 261 root: root,
252 262 }
253 263 }
254 264
255 265 /// Total number of blocks
256 266 fn len(&self) -> usize {
257 267 self.readonly.len() + self.growable.len() + 1
258 268 }
259 269
260 270 /// Implemented for completeness
261 271 ///
262 272 /// A `NodeTree` always has at least the mutable root block.
263 273 #[allow(dead_code)]
264 274 fn is_empty(&self) -> bool {
265 275 false
266 276 }
267 277
268 278 /// Main working method for `NodeTree` searches
269 279 ///
270 280 /// This partial implementation lacks special cases for NULL_REVISION
271 281 fn lookup<'p>(
272 282 &self,
273 283 prefix: NodePrefixRef<'p>,
274 284 ) -> Result<Option<Revision>, NodeMapError> {
275 285 for visit_item in self.visit(prefix) {
276 286 if let Some(opt) = visit_item.final_revision() {
277 287 return Ok(opt);
278 288 }
279 289 }
280 290 Err(NodeMapError::MultipleResults)
281 291 }
282 292
283 293 fn visit<'n, 'p>(
284 294 &'n self,
285 295 prefix: NodePrefixRef<'p>,
286 296 ) -> NodeTreeVisitor<'n, 'p> {
287 297 NodeTreeVisitor {
288 298 nt: self,
289 299 prefix: prefix,
290 300 visit: self.len() - 1,
291 301 nybble_idx: 0,
292 302 done: false,
293 303 }
294 304 }
305 /// Return a mutable reference for `Block` at index `idx`.
306 ///
307 /// If `idx` lies in the immutable area, then the reference is to
308 /// a newly appended copy.
309 ///
310 /// Returns (new_idx, glen, mut_ref) where
311 ///
312 /// - `new_idx` is the index of the mutable `Block`
313 /// - `mut_ref` is a mutable reference to the mutable Block.
314 /// - `glen` is the new length of `self.growable`
315 ///
316 /// Note: the caller wouldn't be allowed to query `self.growable.len()`
317 /// itself because of the mutable borrow taken with the returned `Block`
318 fn mutable_block(&mut self, idx: usize) -> (usize, &mut Block, usize) {
319 let ro_blocks = &self.readonly;
320 let ro_len = ro_blocks.len();
321 let glen = self.growable.len();
322 if idx < ro_len {
323 // TODO OPTIM I think this makes two copies
324 self.growable.push(ro_blocks[idx].clone());
325 (glen + ro_len, &mut self.growable[glen], glen + 1)
326 } else if glen + ro_len == idx {
327 (idx, &mut self.root, glen)
328 } else {
329 (idx, &mut self.growable[idx - ro_len], glen)
330 }
331 }
332
333 /// Main insertion method
334 ///
335 /// This will dive in the node tree to find the deepest `Block` for
336 /// `node`, split it as much as needed and record `node` in there.
337 /// The method then backtracks, updating references in all the visited
338 /// blocks from the root.
339 ///
340 /// All the mutated `Block` are copied first to the growable part if
341 /// needed. That happens for those in the immutable part except the root.
342 pub fn insert<I: RevlogIndex>(
343 &mut self,
344 index: &I,
345 node: &Node,
346 rev: Revision,
347 ) -> Result<(), NodeMapError> {
348 let ro_len = &self.readonly.len();
349
350 let mut visit_steps: Vec<_> = self.visit(node.into()).collect();
351 let read_nybbles = visit_steps.len();
352 // visit_steps cannot be empty, since we always visit the root block
353 let deepest = visit_steps.pop().unwrap();
354
355 let (mut block_idx, mut block, mut glen) =
356 self.mutable_block(deepest.block_idx);
357
358 if let Element::Rev(old_rev) = deepest.element {
359 let old_node = index
360 .node(old_rev)
361 .ok_or_else(|| NodeMapError::RevisionNotInIndex(old_rev))?;
362 if old_node == node {
363 return Ok(()); // avoid creating lots of useless blocks
364 }
365
366 // Looping over the tail of nybbles in both nodes, creating
367 // new blocks until we find the difference
368 let mut new_block_idx = ro_len + glen;
369 let mut nybble = deepest.nybble;
370 for nybble_pos in read_nybbles..node.nybbles_len() {
371 block.set(nybble, Element::Block(new_block_idx));
372
373 let new_nybble = node.get_nybble(nybble_pos);
374 let old_nybble = old_node.get_nybble(nybble_pos);
375
376 if old_nybble == new_nybble {
377 self.growable.push(Block::new());
378 block = &mut self.growable[glen];
379 glen += 1;
380 new_block_idx += 1;
381 nybble = new_nybble;
382 } else {
383 let mut new_block = Block::new();
384 new_block.set(old_nybble, Element::Rev(old_rev));
385 new_block.set(new_nybble, Element::Rev(rev));
386 self.growable.push(new_block);
387 break;
388 }
389 }
390 } else {
391 // Free slot in the deepest block: no splitting has to be done
392 block.set(deepest.nybble, Element::Rev(rev));
393 }
394
395 // Backtrack over visit steps to update references
396 while let Some(visited) = visit_steps.pop() {
397 let to_write = Element::Block(block_idx);
398 if visit_steps.is_empty() {
399 self.root.set(visited.nybble, to_write);
400 break;
401 }
402 let (new_idx, block, _) = self.mutable_block(visited.block_idx);
403 if block.get(visited.nybble) == to_write {
404 break;
405 }
406 block.set(visited.nybble, to_write);
407 block_idx = new_idx;
408 }
409 Ok(())
410 }
295 411 }
296 412
297 413 struct NodeTreeVisitor<'n, 'p> {
298 414 nt: &'n NodeTree,
299 415 prefix: NodePrefixRef<'p>,
300 416 visit: usize,
301 417 nybble_idx: usize,
302 418 done: bool,
303 419 }
304 420
305 421 #[derive(Debug, PartialEq, Clone)]
306 422 struct NodeTreeVisitItem {
307 423 block_idx: usize,
308 424 nybble: u8,
309 425 element: Element,
310 426 }
311 427
312 428 impl<'n, 'p> Iterator for NodeTreeVisitor<'n, 'p> {
313 429 type Item = NodeTreeVisitItem;
314 430
315 431 fn next(&mut self) -> Option<Self::Item> {
316 432 if self.done || self.nybble_idx >= self.prefix.len() {
317 433 return None;
318 434 }
319 435
320 436 let nybble = self.prefix.get_nybble(self.nybble_idx);
321 437 self.nybble_idx += 1;
322 438
323 439 let visit = self.visit;
324 440 let element = self.nt[visit].get(nybble);
325 441 if let Element::Block(idx) = element {
326 442 self.visit = idx;
327 443 } else {
328 444 self.done = true;
329 445 }
330 446
331 447 Some(NodeTreeVisitItem {
332 448 block_idx: visit,
333 449 nybble: nybble,
334 450 element: element,
335 451 })
336 452 }
337 453 }
338 454
339 455 impl NodeTreeVisitItem {
340 456 // Return `Some(opt)` if this item is final, with `opt` being the
341 457 // `Revision` that it may represent.
342 458 //
343 459 // If the item is not terminal, return `None`
344 460 fn final_revision(&self) -> Option<Option<Revision>> {
345 461 match self.element {
346 462 Element::Block(_) => None,
347 463 Element::Rev(r) => Some(Some(r)),
348 464 Element::None => Some(None),
349 465 }
350 466 }
351 467 }
352 468
353 469 impl From<Vec<Block>> for NodeTree {
354 470 fn from(vec: Vec<Block>) -> Self {
355 471 Self::new(Box::new(vec))
356 472 }
357 473 }
358 474
359 475 impl fmt::Debug for NodeTree {
360 476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 477 let readonly: &[Block] = &*self.readonly;
362 478 write!(
363 479 f,
364 480 "readonly: {:?}, growable: {:?}, root: {:?}",
365 481 readonly, self.growable, self.root
366 482 )
367 483 }
368 484 }
369 485
486 impl Default for NodeTree {
487 /// Create a fully mutable empty NodeTree
488 fn default() -> Self {
489 NodeTree::new(Box::new(Vec::new()))
490 }
491 }
492
370 493 impl NodeMap for NodeTree {
371 494 fn find_bin<'a>(
372 495 &self,
373 496 idx: &impl RevlogIndex,
374 497 prefix: NodePrefixRef<'a>,
375 498 ) -> Result<Option<Revision>, NodeMapError> {
376 499 self.lookup(prefix.clone()).and_then(|opt| {
377 500 opt.map_or(Ok(None), |rev| has_prefix_or_none(idx, prefix, rev))
378 501 })
379 502 }
380 503 }
381 504
382 505 #[cfg(test)]
383 506 mod tests {
384 507 use super::NodeMapError::*;
385 508 use super::*;
386 509 use crate::revlog::node::{hex_pad_right, Node};
387 510 use std::collections::HashMap;
388 511
389 512 /// Creates a `Block` using a syntax close to the `Debug` output
390 513 macro_rules! block {
391 514 {$($nybble:tt : $variant:ident($val:tt)),*} => (
392 515 {
393 516 let mut block = Block::new();
394 517 $(block.set($nybble, Element::$variant($val)));*;
395 518 block
396 519 }
397 520 )
398 521 }
399 522
400 523 #[test]
401 524 fn test_block_debug() {
402 525 let mut block = Block::new();
403 526 block.set(1, Element::Rev(3));
404 527 block.set(10, Element::Block(0));
405 528 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
406 529 }
407 530
408 531 #[test]
409 532 fn test_block_macro() {
410 533 let block = block! {5: Block(2)};
411 534 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
412 535
413 536 let block = block! {13: Rev(15), 5: Block(2)};
414 537 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
415 538 }
416 539
417 540 #[test]
418 541 fn test_raw_block() {
419 542 let mut raw = [-1; 16];
420 543 raw[0] = 0;
421 544 raw[1] = RawElement::to_be(15);
422 545 raw[2] = RawElement::to_be(-2);
423 546 raw[3] = RawElement::to_be(-1);
424 547 raw[4] = RawElement::to_be(-3);
425 548 let block = Block(raw);
426 549 assert_eq!(block.get(0), Element::Block(0));
427 550 assert_eq!(block.get(1), Element::Block(15));
428 551 assert_eq!(block.get(3), Element::None);
429 552 assert_eq!(block.get(2), Element::Rev(0));
430 553 assert_eq!(block.get(4), Element::Rev(1));
431 554 }
432 555
433 556 type TestIndex = HashMap<Revision, Node>;
434 557
435 558 impl RevlogIndex for TestIndex {
436 559 fn node(&self, rev: Revision) -> Option<&Node> {
437 560 self.get(&rev)
438 561 }
439 562
440 563 fn len(&self) -> usize {
441 564 self.len()
442 565 }
443 566 }
444 567
445 /// Pad hexadecimal Node prefix with zeros on the right, then insert
568 /// Pad hexadecimal Node prefix with zeros on the right
446 569 ///
447 570 /// This avoids having to repeatedly write very long hexadecimal
448 571 /// strings for test data, and brings actual hash size independency.
572 #[cfg(test)]
573 fn pad_node(hex: &str) -> Node {
574 Node::from_hex(&hex_pad_right(hex)).unwrap()
575 }
576
577 /// Pad hexadecimal Node prefix with zeros on the right, then insert
449 578 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
450 idx.insert(rev, Node::from_hex(&hex_pad_right(hex)).unwrap());
579 idx.insert(rev, pad_node(hex));
451 580 }
452 581
453 582 fn sample_nodetree() -> NodeTree {
454 583 NodeTree::from(vec![
455 584 block![0: Rev(9)],
456 585 block![0: Rev(0), 1: Rev(9)],
457 586 block![0: Block(1), 1:Rev(1)],
458 587 ])
459 588 }
460 589
461 590 #[test]
462 591 fn test_nt_debug() {
463 592 let nt = sample_nodetree();
464 593 assert_eq!(
465 594 format!("{:?}", nt),
466 595 "readonly: \
467 596 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}], \
468 597 growable: [], \
469 598 root: {0: Block(1), 1: Rev(1)}",
470 599 );
471 600 }
472 601
473 602 #[test]
474 603 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
475 604 let mut idx: TestIndex = HashMap::new();
476 605 pad_insert(&mut idx, 1, "1234deadcafe");
477 606
478 607 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
479 608 assert_eq!(nt.find_hex(&idx, "1")?, Some(1));
480 609 assert_eq!(nt.find_hex(&idx, "12")?, Some(1));
481 610 assert_eq!(nt.find_hex(&idx, "1234de")?, Some(1));
482 611 assert_eq!(nt.find_hex(&idx, "1a")?, None);
483 612 assert_eq!(nt.find_hex(&idx, "ab")?, None);
484 613
485 614 // and with full binary Nodes
486 615 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
487 616 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
488 617 assert_eq!(nt.find_node(&idx, &unknown)?, None);
489 618 Ok(())
490 619 }
491 620
492 621 #[test]
493 622 fn test_immutable_find_one_jump() {
494 623 let mut idx = TestIndex::new();
495 624 pad_insert(&mut idx, 9, "012");
496 625 pad_insert(&mut idx, 0, "00a");
497 626
498 627 let nt = sample_nodetree();
499 628
500 629 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults));
501 630 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9)));
502 631 assert_eq!(nt.find_hex(&idx, "00"), Ok(Some(0)));
503 632 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0)));
504 633 }
505 634
506 635 #[test]
507 636 fn test_mutated_find() -> Result<(), NodeMapError> {
508 637 let mut idx = TestIndex::new();
509 638 pad_insert(&mut idx, 9, "012");
510 639 pad_insert(&mut idx, 0, "00a");
511 640 pad_insert(&mut idx, 2, "cafe");
512 641 pad_insert(&mut idx, 3, "15");
513 642 pad_insert(&mut idx, 1, "10");
514 643
515 644 let nt = NodeTree {
516 645 readonly: sample_nodetree().readonly,
517 646 growable: vec![block![0: Rev(1), 5: Rev(3)]],
518 647 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
519 648 };
520 649 assert_eq!(nt.find_hex(&idx, "10")?, Some(1));
521 650 assert_eq!(nt.find_hex(&idx, "c")?, Some(2));
522 651 assert_eq!(nt.find_hex(&idx, "00")?, Some(0));
523 652 assert_eq!(nt.find_hex(&idx, "01")?, Some(9));
524 653 Ok(())
525 654 }
655
656 struct TestNtIndex {
657 index: TestIndex,
658 nt: NodeTree,
659 }
660
661 impl TestNtIndex {
662 fn new() -> Self {
663 TestNtIndex {
664 index: HashMap::new(),
665 nt: NodeTree::default(),
666 }
667 }
668
669 fn insert(
670 &mut self,
671 rev: Revision,
672 hex: &str,
673 ) -> Result<(), NodeMapError> {
674 let node = pad_node(hex);
675 self.index.insert(rev, node.clone());
676 self.nt.insert(&self.index, &node, rev)?;
677 Ok(())
678 }
679
680 fn find_hex(
681 &self,
682 prefix: &str,
683 ) -> Result<Option<Revision>, NodeMapError> {
684 self.nt.find_hex(&self.index, prefix)
685 }
686
687 /// Drain `added` and restart a new one
688 fn commit(self) -> Self {
689 let mut as_vec: Vec<Block> =
690 self.nt.readonly.iter().map(|block| block.clone()).collect();
691 as_vec.extend(self.nt.growable);
692 as_vec.push(self.nt.root);
693
694 Self {
695 index: self.index,
696 nt: NodeTree::from(as_vec).into(),
697 }
698 }
699 }
700
701 #[test]
702 fn test_insert_full_mutable() -> Result<(), NodeMapError> {
703 let mut idx = TestNtIndex::new();
704 idx.insert(0, "1234")?;
705 assert_eq!(idx.find_hex("1")?, Some(0));
706 assert_eq!(idx.find_hex("12")?, Some(0));
707
708 // let's trigger a simple split
709 idx.insert(1, "1a34")?;
710 assert_eq!(idx.nt.growable.len(), 1);
711 assert_eq!(idx.find_hex("12")?, Some(0));
712 assert_eq!(idx.find_hex("1a")?, Some(1));
713
714 // reinserting is a no_op
715 idx.insert(1, "1a34")?;
716 assert_eq!(idx.nt.growable.len(), 1);
717 assert_eq!(idx.find_hex("12")?, Some(0));
718 assert_eq!(idx.find_hex("1a")?, Some(1));
719
720 idx.insert(2, "1a01")?;
721 assert_eq!(idx.nt.growable.len(), 2);
722 assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
723 assert_eq!(idx.find_hex("12")?, Some(0));
724 assert_eq!(idx.find_hex("1a3")?, Some(1));
725 assert_eq!(idx.find_hex("1a0")?, Some(2));
726 assert_eq!(idx.find_hex("1a12")?, None);
727
728 // now let's make it split and create more than one additional block
729 idx.insert(3, "1a345")?;
730 assert_eq!(idx.nt.growable.len(), 4);
731 assert_eq!(idx.find_hex("1a340")?, Some(1));
732 assert_eq!(idx.find_hex("1a345")?, Some(3));
733 assert_eq!(idx.find_hex("1a341")?, None);
734
735 Ok(())
736 }
737
738 #[test]
739 fn test_insert_extreme_splitting() -> Result<(), NodeMapError> {
740 // check that the splitting loop is long enough
741 let mut nt_idx = TestNtIndex::new();
742 let nt = &mut nt_idx.nt;
743 let idx = &mut nt_idx.index;
744
745 let node0_hex = hex_pad_right("444444");
746 let mut node1_hex = hex_pad_right("444444").clone();
747 node1_hex.pop();
748 node1_hex.push('5');
749 let node0 = Node::from_hex(&node0_hex).unwrap();
750 let node1 = Node::from_hex(&node1_hex).unwrap();
751
752 idx.insert(0, node0.clone());
753 nt.insert(idx, &node0, 0)?;
754 idx.insert(1, node1.clone());
755 nt.insert(idx, &node1, 1)?;
756
757 assert_eq!(nt.find_bin(idx, (&node0).into())?, Some(0));
758 assert_eq!(nt.find_bin(idx, (&node1).into())?, Some(1));
759 Ok(())
760 }
761
762 #[test]
763 fn test_insert_partly_immutable() -> Result<(), NodeMapError> {
764 let mut idx = TestNtIndex::new();
765 idx.insert(0, "1234")?;
766 idx.insert(1, "1235")?;
767 idx.insert(2, "131")?;
768 idx.insert(3, "cafe")?;
769 let mut idx = idx.commit();
770 assert_eq!(idx.find_hex("1234")?, Some(0));
771 assert_eq!(idx.find_hex("1235")?, Some(1));
772 assert_eq!(idx.find_hex("131")?, Some(2));
773 assert_eq!(idx.find_hex("cafe")?, Some(3));
774
775 idx.insert(4, "123A")?;
776 assert_eq!(idx.find_hex("1234")?, Some(0));
777 assert_eq!(idx.find_hex("1235")?, Some(1));
778 assert_eq!(idx.find_hex("131")?, Some(2));
779 assert_eq!(idx.find_hex("cafe")?, Some(3));
780 assert_eq!(idx.find_hex("123A")?, Some(4));
781
782 idx.insert(5, "c0")?;
783 assert_eq!(idx.find_hex("cafe")?, Some(3));
784 assert_eq!(idx.find_hex("c0")?, Some(5));
785 assert_eq!(idx.find_hex("c1")?, None);
786 assert_eq!(idx.find_hex("1234")?, Some(0));
787
788 Ok(())
789 }
526 790 }
General Comments 0
You need to be logged in to leave comments. Login now