|
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net> |
|
2 // and Mercurial contributors |
|
3 // |
|
4 // This software may be used and distributed according to the terms of the |
|
5 // GNU General Public License version 2 or any later version. |
|
6 //! Indexing facilities for fast retrieval of `Revision` from `Node` |
|
7 //! |
|
8 //! This provides a variation on the 16-ary radix tree that is |
|
9 //! provided as "nodetree" in revlog.c, ready for append-only persistence |
|
10 //! on disk. |
|
11 //! |
|
12 //! Following existing implicit conventions, the "nodemap" terminology |
|
13 //! is used in a more abstract context. |
|
14 |
|
15 use super::Revision; |
|
16 use std::fmt; |
|
17 |
|
18 /// Low level NodeTree [`Blocks`] elements |
|
19 /// |
|
20 /// These are exactly as for instance on persistent storage. |
|
21 type RawElement = i32; |
|
22 |
|
23 /// High level representation of values in NodeTree |
|
24 /// [`Blocks`](struct.Block.html) |
|
25 /// |
|
26 /// This is the high level representation that most algorithms should |
|
27 /// use. |
|
28 #[derive(Clone, Debug, Eq, PartialEq)] |
|
29 enum Element { |
|
30 Rev(Revision), |
|
31 Block(usize), |
|
32 None, |
|
33 } |
|
34 |
|
35 impl From<RawElement> for Element { |
|
36 /// Conversion from low level representation, after endianness conversion. |
|
37 /// |
|
38 /// See [`Block`](struct.Block.html) for explanation about the encoding. |
|
39 fn from(raw: RawElement) -> Element { |
|
40 if raw >= 0 { |
|
41 Element::Block(raw as usize) |
|
42 } else if raw == -1 { |
|
43 Element::None |
|
44 } else { |
|
45 Element::Rev(-raw - 2) |
|
46 } |
|
47 } |
|
48 } |
|
49 |
|
50 impl From<Element> for RawElement { |
|
51 fn from(element: Element) -> RawElement { |
|
52 match element { |
|
53 Element::None => 0, |
|
54 Element::Block(i) => i as RawElement, |
|
55 Element::Rev(rev) => -rev - 2, |
|
56 } |
|
57 } |
|
58 } |
|
59 |
|
60 /// A logical block of the `NodeTree`, packed with a fixed size. |
|
61 /// |
|
62 /// These are always used in container types implementing `Index<Block>`, |
|
63 /// such as `&Block` |
|
64 /// |
|
65 /// As an array of integers, its ith element encodes that the |
|
66 /// ith potential edge from the block, representing the ith hexadecimal digit |
|
67 /// (nybble) `i` is either: |
|
68 /// |
|
69 /// - absent (value -1) |
|
70 /// - another `Block` in the same indexable container (value ≥ 0) |
|
71 /// - a `Revision` leaf (value ≤ -2) |
|
72 /// |
|
73 /// Endianness has to be fixed for consistency on shared storage across |
|
74 /// different architectures. |
|
75 /// |
|
76 /// A key difference with the C `nodetree` is that we need to be |
|
77 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker |
|
78 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1. |
|
79 /// |
|
80 /// Another related difference is that `NULL_REVISION` (-1) is not |
|
81 /// represented at all, because we want an immutable empty nodetree |
|
82 /// to be valid. |
|
83 |
|
84 #[derive(Clone, PartialEq)] |
|
85 pub struct Block([RawElement; 16]); |
|
86 |
|
87 impl Block { |
|
88 fn new() -> Self { |
|
89 Block([-1; 16]) |
|
90 } |
|
91 |
|
92 fn get(&self, nybble: u8) -> Element { |
|
93 Element::from(RawElement::from_be(self.0[nybble as usize])) |
|
94 } |
|
95 |
|
96 fn set(&mut self, nybble: u8, element: Element) { |
|
97 self.0[nybble as usize] = RawElement::to_be(element.into()) |
|
98 } |
|
99 } |
|
100 |
|
101 impl fmt::Debug for Block { |
|
102 /// sparse representation for testing and debugging purposes |
|
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|
104 f.debug_map() |
|
105 .entries((0..16).filter_map(|i| match self.get(i) { |
|
106 Element::None => None, |
|
107 element => Some((i, element)), |
|
108 })) |
|
109 .finish() |
|
110 } |
|
111 } |
|
112 |
|
113 #[cfg(test)] |
|
114 mod tests { |
|
115 use super::*; |
|
116 |
|
117 /// Creates a `Block` using a syntax close to the `Debug` output |
|
118 macro_rules! block { |
|
119 {$($nybble:tt : $variant:ident($val:tt)),*} => ( |
|
120 { |
|
121 let mut block = Block::new(); |
|
122 $(block.set($nybble, Element::$variant($val)));*; |
|
123 block |
|
124 } |
|
125 ) |
|
126 } |
|
127 |
|
128 #[test] |
|
129 fn test_block_debug() { |
|
130 let mut block = Block::new(); |
|
131 block.set(1, Element::Rev(3)); |
|
132 block.set(10, Element::Block(0)); |
|
133 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}"); |
|
134 } |
|
135 |
|
136 #[test] |
|
137 fn test_block_macro() { |
|
138 let block = block! {5: Block(2)}; |
|
139 assert_eq!(format!("{:?}", block), "{5: Block(2)}"); |
|
140 |
|
141 let block = block! {13: Rev(15), 5: Block(2)}; |
|
142 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}"); |
|
143 } |
|
144 |
|
145 #[test] |
|
146 fn test_raw_block() { |
|
147 let mut raw = [-1; 16]; |
|
148 raw[0] = 0; |
|
149 raw[1] = RawElement::to_be(15); |
|
150 raw[2] = RawElement::to_be(-2); |
|
151 raw[3] = RawElement::to_be(-1); |
|
152 raw[4] = RawElement::to_be(-3); |
|
153 let block = Block(raw); |
|
154 assert_eq!(block.get(0), Element::Block(0)); |
|
155 assert_eq!(block.get(1), Element::Block(15)); |
|
156 assert_eq!(block.get(3), Element::None); |
|
157 assert_eq!(block.get(2), Element::Rev(0)); |
|
158 assert_eq!(block.get(4), Element::Rev(1)); |
|
159 } |
|
160 } |