##// END OF EJS Templates
rust-nodemap: building blocks for nodetree structures...
Georges Racinet -
r44600:63db6657 default
parent child Browse files
Show More
@@ -0,0 +1,160 b''
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 }
@@ -5,6 +5,8 b''
5 // GNU General Public License version 2 or any later version.
5 // GNU General Public License version 2 or any later version.
6 //! Mercurial concepts for handling revision history
6 //! Mercurial concepts for handling revision history
7
7
8 pub mod nodemap;
9
8 /// Mercurial revision numbers
10 /// Mercurial revision numbers
9 ///
11 ///
10 /// As noted in revlog.c, revision numbers are actually encoded in
12 /// As noted in revlog.c, revision numbers are actually encoded in
General Comments 0
You need to be logged in to leave comments. Login now