##// END OF EJS Templates
rust-nodemap: mutable NodeTree data structure...
Georges Racinet -
r44646:a1933145 default
parent child Browse files
Show More
@@ -1,404 +1,461
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2018-2020 Georges Racinet <georges.racinet@octobus.net>
2 // and Mercurial contributors
2 // and Mercurial contributors
3 //
3 //
4 // This software may be used and distributed according to the terms of the
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.
5 // GNU General Public License version 2 or any later version.
6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
6 //! Indexing facilities for fast retrieval of `Revision` from `Node`
7 //!
7 //!
8 //! This provides a variation on the 16-ary radix tree that is
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
9 //! provided as "nodetree" in revlog.c, ready for append-only persistence
10 //! on disk.
10 //! on disk.
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, NodeError, NodePrefix, NodePrefixRef, Revision, RevlogIndex,
17 };
17 };
18 use std::fmt;
18 use std::fmt;
19 use std::ops::Deref;
19 use std::ops::Deref;
20 use std::ops::Index;
20 use std::ops::Index;
21
21
22 #[derive(Debug, PartialEq)]
22 #[derive(Debug, PartialEq)]
23 pub enum NodeMapError {
23 pub enum NodeMapError {
24 MultipleResults,
24 MultipleResults,
25 InvalidNodePrefix(NodeError),
25 InvalidNodePrefix(NodeError),
26 /// A `Revision` stored in the nodemap could not be found in the index
26 /// A `Revision` stored in the nodemap could not be found in the index
27 RevisionNotInIndex(Revision),
27 RevisionNotInIndex(Revision),
28 }
28 }
29
29
30 impl From<NodeError> for NodeMapError {
30 impl From<NodeError> for NodeMapError {
31 fn from(err: NodeError) -> Self {
31 fn from(err: NodeError) -> Self {
32 NodeMapError::InvalidNodePrefix(err)
32 NodeMapError::InvalidNodePrefix(err)
33 }
33 }
34 }
34 }
35
35
36 /// Mapping system from Mercurial nodes to revision numbers.
36 /// Mapping system from Mercurial nodes to revision numbers.
37 ///
37 ///
38 /// ## `RevlogIndex` and `NodeMap`
38 /// ## `RevlogIndex` and `NodeMap`
39 ///
39 ///
40 /// One way to think about their relationship is that
40 /// One way to think about their relationship is that
41 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
41 /// the `NodeMap` is a prefix-oriented reverse index of the `Node` information
42 /// carried by a [`RevlogIndex`].
42 /// carried by a [`RevlogIndex`].
43 ///
43 ///
44 /// Many of the methods in this trait take a `RevlogIndex` argument
44 /// Many of the methods in this trait take a `RevlogIndex` argument
45 /// which is used for validation of their results. This index must naturally
45 /// which is used for validation of their results. This index must naturally
46 /// be the one the `NodeMap` is about, and it must be consistent.
46 /// be the one the `NodeMap` is about, and it must be consistent.
47 ///
47 ///
48 /// Notably, the `NodeMap` must not store
48 /// Notably, the `NodeMap` must not store
49 /// information about more `Revision` values than there are in the index.
49 /// information about more `Revision` values than there are in the index.
50 /// In these methods, an encountered `Revision` is not in the index, a
50 /// In these methods, an encountered `Revision` is not in the index, a
51 /// [`RevisionNotInIndex`] error is returned.
51 /// [`RevisionNotInIndex`] error is returned.
52 ///
52 ///
53 /// In insert operations, the rule is thus that the `NodeMap` must always
53 /// In insert operations, the rule is thus that the `NodeMap` must always
54 /// be updated after the `RevlogIndex`
54 /// be updated after the `RevlogIndex`
55 /// be updated first, and the `NodeMap` second.
55 /// be updated first, and the `NodeMap` second.
56 ///
56 ///
57 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
57 /// [`RevisionNotInIndex`]: enum.NodeMapError.html#variant.RevisionNotInIndex
58 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
58 /// [`RevlogIndex`]: ../trait.RevlogIndex.html
59 pub trait NodeMap {
59 pub trait NodeMap {
60 /// Find the unique `Revision` having the given `Node`
60 /// Find the unique `Revision` having the given `Node`
61 ///
61 ///
62 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
62 /// If no Revision matches the given `Node`, `Ok(None)` is returned.
63 fn find_node(
63 fn find_node(
64 &self,
64 &self,
65 index: &impl RevlogIndex,
65 index: &impl RevlogIndex,
66 node: &Node,
66 node: &Node,
67 ) -> Result<Option<Revision>, NodeMapError> {
67 ) -> Result<Option<Revision>, NodeMapError> {
68 self.find_bin(index, node.into())
68 self.find_bin(index, node.into())
69 }
69 }
70
70
71 /// Find the unique Revision whose `Node` starts with a given binary prefix
71 /// Find the unique Revision whose `Node` starts with a given binary prefix
72 ///
72 ///
73 /// If no Revision matches the given prefix, `Ok(None)` is returned.
73 /// If no Revision matches the given prefix, `Ok(None)` is returned.
74 ///
74 ///
75 /// If several Revisions match the given prefix, a [`MultipleResults`]
75 /// If several Revisions match the given prefix, a [`MultipleResults`]
76 /// error is returned.
76 /// error is returned.
77 fn find_bin<'a>(
77 fn find_bin<'a>(
78 &self,
78 &self,
79 idx: &impl RevlogIndex,
79 idx: &impl RevlogIndex,
80 prefix: NodePrefixRef<'a>,
80 prefix: NodePrefixRef<'a>,
81 ) -> Result<Option<Revision>, NodeMapError>;
81 ) -> Result<Option<Revision>, NodeMapError>;
82
82
83 /// Find the unique Revision whose `Node` hexadecimal string representation
83 /// Find the unique Revision whose `Node` hexadecimal string representation
84 /// starts with a given prefix
84 /// starts with a given prefix
85 ///
85 ///
86 /// If no Revision matches the given prefix, `Ok(None)` is returned.
86 /// If no Revision matches the given prefix, `Ok(None)` is returned.
87 ///
87 ///
88 /// If several Revisions match the given prefix, a [`MultipleResults`]
88 /// If several Revisions match the given prefix, a [`MultipleResults`]
89 /// error is returned.
89 /// error is returned.
90 fn find_hex(
90 fn find_hex(
91 &self,
91 &self,
92 idx: &impl RevlogIndex,
92 idx: &impl RevlogIndex,
93 prefix: &str,
93 prefix: &str,
94 ) -> Result<Option<Revision>, NodeMapError> {
94 ) -> Result<Option<Revision>, NodeMapError> {
95 self.find_bin(idx, NodePrefix::from_hex(prefix)?.borrow())
95 self.find_bin(idx, NodePrefix::from_hex(prefix)?.borrow())
96 }
96 }
97 }
97 }
98
98
99 /// Low level NodeTree [`Blocks`] elements
99 /// Low level NodeTree [`Blocks`] elements
100 ///
100 ///
101 /// These are exactly as for instance on persistent storage.
101 /// These are exactly as for instance on persistent storage.
102 type RawElement = i32;
102 type RawElement = i32;
103
103
104 /// High level representation of values in NodeTree
104 /// High level representation of values in NodeTree
105 /// [`Blocks`](struct.Block.html)
105 /// [`Blocks`](struct.Block.html)
106 ///
106 ///
107 /// This is the high level representation that most algorithms should
107 /// This is the high level representation that most algorithms should
108 /// use.
108 /// use.
109 #[derive(Clone, Debug, Eq, PartialEq)]
109 #[derive(Clone, Debug, Eq, PartialEq)]
110 enum Element {
110 enum Element {
111 Rev(Revision),
111 Rev(Revision),
112 Block(usize),
112 Block(usize),
113 None,
113 None,
114 }
114 }
115
115
116 impl From<RawElement> for Element {
116 impl From<RawElement> for Element {
117 /// Conversion from low level representation, after endianness conversion.
117 /// Conversion from low level representation, after endianness conversion.
118 ///
118 ///
119 /// See [`Block`](struct.Block.html) for explanation about the encoding.
119 /// See [`Block`](struct.Block.html) for explanation about the encoding.
120 fn from(raw: RawElement) -> Element {
120 fn from(raw: RawElement) -> Element {
121 if raw >= 0 {
121 if raw >= 0 {
122 Element::Block(raw as usize)
122 Element::Block(raw as usize)
123 } else if raw == -1 {
123 } else if raw == -1 {
124 Element::None
124 Element::None
125 } else {
125 } else {
126 Element::Rev(-raw - 2)
126 Element::Rev(-raw - 2)
127 }
127 }
128 }
128 }
129 }
129 }
130
130
131 impl From<Element> for RawElement {
131 impl From<Element> for RawElement {
132 fn from(element: Element) -> RawElement {
132 fn from(element: Element) -> RawElement {
133 match element {
133 match element {
134 Element::None => 0,
134 Element::None => 0,
135 Element::Block(i) => i as RawElement,
135 Element::Block(i) => i as RawElement,
136 Element::Rev(rev) => -rev - 2,
136 Element::Rev(rev) => -rev - 2,
137 }
137 }
138 }
138 }
139 }
139 }
140
140
141 /// A logical block of the `NodeTree`, packed with a fixed size.
141 /// A logical block of the `NodeTree`, packed with a fixed size.
142 ///
142 ///
143 /// These are always used in container types implementing `Index<Block>`,
143 /// These are always used in container types implementing `Index<Block>`,
144 /// such as `&Block`
144 /// such as `&Block`
145 ///
145 ///
146 /// As an array of integers, its ith element encodes that the
146 /// As an array of integers, its ith element encodes that the
147 /// ith potential edge from the block, representing the ith hexadecimal digit
147 /// ith potential edge from the block, representing the ith hexadecimal digit
148 /// (nybble) `i` is either:
148 /// (nybble) `i` is either:
149 ///
149 ///
150 /// - absent (value -1)
150 /// - absent (value -1)
151 /// - another `Block` in the same indexable container (value β‰₯ 0)
151 /// - another `Block` in the same indexable container (value β‰₯ 0)
152 /// - a `Revision` leaf (value ≀ -2)
152 /// - a `Revision` leaf (value ≀ -2)
153 ///
153 ///
154 /// Endianness has to be fixed for consistency on shared storage across
154 /// Endianness has to be fixed for consistency on shared storage across
155 /// different architectures.
155 /// different architectures.
156 ///
156 ///
157 /// A key difference with the C `nodetree` is that we need to be
157 /// A key difference with the C `nodetree` is that we need to be
158 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
158 /// able to represent the [`Block`] at index 0, hence -1 is the empty marker
159 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
159 /// rather than 0 and the `Revision` range upper limit of -2 instead of -1.
160 ///
160 ///
161 /// Another related difference is that `NULL_REVISION` (-1) is not
161 /// Another related difference is that `NULL_REVISION` (-1) is not
162 /// represented at all, because we want an immutable empty nodetree
162 /// represented at all, because we want an immutable empty nodetree
163 /// to be valid.
163 /// to be valid.
164
164
165 #[derive(Clone, PartialEq)]
165 #[derive(Clone, PartialEq)]
166 pub struct Block([RawElement; 16]);
166 pub struct Block([RawElement; 16]);
167
167
168 impl Block {
168 impl Block {
169 fn new() -> Self {
169 fn new() -> Self {
170 Block([-1; 16])
170 Block([-1; 16])
171 }
171 }
172
172
173 fn get(&self, nybble: u8) -> Element {
173 fn get(&self, nybble: u8) -> Element {
174 Element::from(RawElement::from_be(self.0[nybble as usize]))
174 Element::from(RawElement::from_be(self.0[nybble as usize]))
175 }
175 }
176
176
177 fn set(&mut self, nybble: u8, element: Element) {
177 fn set(&mut self, nybble: u8, element: Element) {
178 self.0[nybble as usize] = RawElement::to_be(element.into())
178 self.0[nybble as usize] = RawElement::to_be(element.into())
179 }
179 }
180 }
180 }
181
181
182 impl fmt::Debug for Block {
182 impl fmt::Debug for Block {
183 /// sparse representation for testing and debugging purposes
183 /// sparse representation for testing and debugging purposes
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 f.debug_map()
185 f.debug_map()
186 .entries((0..16).filter_map(|i| match self.get(i) {
186 .entries((0..16).filter_map(|i| match self.get(i) {
187 Element::None => None,
187 Element::None => None,
188 element => Some((i, element)),
188 element => Some((i, element)),
189 }))
189 }))
190 .finish()
190 .finish()
191 }
191 }
192 }
192 }
193
193
194 /// A 16-radix tree with the root block at the end
194 /// A mutable 16-radix tree with the root block logically at the end
195 ///
196 /// Because of the append only nature of our node trees, we need to
197 /// keep the original untouched and store new blocks separately.
198 ///
199 /// The mutable root `Block` is kept apart so that we don't have to rebump
200 /// it on each insertion.
195 pub struct NodeTree {
201 pub struct NodeTree {
196 readonly: Box<dyn Deref<Target = [Block]> + Send>,
202 readonly: Box<dyn Deref<Target = [Block]> + Send>,
203 growable: Vec<Block>,
204 root: Block,
197 }
205 }
198
206
199 impl Index<usize> for NodeTree {
207 impl Index<usize> for NodeTree {
200 type Output = Block;
208 type Output = Block;
201
209
202 fn index(&self, i: usize) -> &Block {
210 fn index(&self, i: usize) -> &Block {
203 &self.readonly[i]
211 let ro_len = self.readonly.len();
212 if i < ro_len {
213 &self.readonly[i]
214 } else if i == ro_len + self.growable.len() {
215 &self.root
216 } else {
217 &self.growable[i - ro_len]
218 }
204 }
219 }
205 }
220 }
206
221
207 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
222 /// Return `None` unless the `Node` for `rev` has given prefix in `index`.
208 fn has_prefix_or_none<'p>(
223 fn has_prefix_or_none<'p>(
209 idx: &impl RevlogIndex,
224 idx: &impl RevlogIndex,
210 prefix: NodePrefixRef<'p>,
225 prefix: NodePrefixRef<'p>,
211 rev: Revision,
226 rev: Revision,
212 ) -> Result<Option<Revision>, NodeMapError> {
227 ) -> Result<Option<Revision>, NodeMapError> {
213 idx.node(rev)
228 idx.node(rev)
214 .ok_or_else(|| NodeMapError::RevisionNotInIndex(rev))
229 .ok_or_else(|| NodeMapError::RevisionNotInIndex(rev))
215 .map(|node| {
230 .map(|node| {
216 if prefix.is_prefix_of(node) {
231 if prefix.is_prefix_of(node) {
217 Some(rev)
232 Some(rev)
218 } else {
233 } else {
219 None
234 None
220 }
235 }
221 })
236 })
222 }
237 }
223
238
224 impl NodeTree {
239 impl NodeTree {
225 fn len(&self) -> usize {
240 /// Initiate a NodeTree from an immutable slice-like of `Block`
226 self.readonly.len()
241 ///
242 /// We keep `readonly` and clone its root block if it isn't empty.
243 fn new(readonly: Box<dyn Deref<Target = [Block]> + Send>) -> Self {
244 let root = readonly
245 .last()
246 .map(|b| b.clone())
247 .unwrap_or_else(|| Block::new());
248 NodeTree {
249 readonly: readonly,
250 growable: Vec::new(),
251 root: root,
252 }
227 }
253 }
228
254
255 /// Total number of blocks
256 fn len(&self) -> usize {
257 self.readonly.len() + self.growable.len() + 1
258 }
259
260 /// Implemented for completeness
261 ///
262 /// A `NodeTree` always has at least the mutable root block.
263 #[allow(dead_code)]
229 fn is_empty(&self) -> bool {
264 fn is_empty(&self) -> bool {
230 self.len() == 0
265 false
231 }
266 }
232
267
233 /// Main working method for `NodeTree` searches
268 /// Main working method for `NodeTree` searches
234 ///
269 ///
235 /// This partial implementation lacks special cases for NULL_REVISION
270 /// This partial implementation lacks special cases for NULL_REVISION
236 fn lookup<'p>(
271 fn lookup<'p>(
237 &self,
272 &self,
238 prefix: NodePrefixRef<'p>,
273 prefix: NodePrefixRef<'p>,
239 ) -> Result<Option<Revision>, NodeMapError> {
274 ) -> Result<Option<Revision>, NodeMapError> {
240 if self.is_empty() {
241 return Ok(None);
242 }
243 let mut visit = self.len() - 1;
275 let mut visit = self.len() - 1;
244 for i in 0..prefix.len() {
276 for i in 0..prefix.len() {
245 let nybble = prefix.get_nybble(i);
277 let nybble = prefix.get_nybble(i);
246 match self[visit].get(nybble) {
278 match self[visit].get(nybble) {
247 Element::None => return Ok(None),
279 Element::None => return Ok(None),
248 Element::Rev(r) => return Ok(Some(r)),
280 Element::Rev(r) => return Ok(Some(r)),
249 Element::Block(idx) => visit = idx,
281 Element::Block(idx) => visit = idx,
250 }
282 }
251 }
283 }
252 Err(NodeMapError::MultipleResults)
284 Err(NodeMapError::MultipleResults)
253 }
285 }
254 }
286 }
255
287
256 impl From<Vec<Block>> for NodeTree {
288 impl From<Vec<Block>> for NodeTree {
257 fn from(vec: Vec<Block>) -> Self {
289 fn from(vec: Vec<Block>) -> Self {
258 NodeTree {
290 Self::new(Box::new(vec))
259 readonly: Box::new(vec),
260 }
261 }
291 }
262 }
292 }
263
293
264 impl fmt::Debug for NodeTree {
294 impl fmt::Debug for NodeTree {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 let blocks: &[Block] = &*self.readonly;
296 let readonly: &[Block] = &*self.readonly;
267 write!(f, "readonly: {:?}", blocks)
297 write!(
298 f,
299 "readonly: {:?}, growable: {:?}, root: {:?}",
300 readonly, self.growable, self.root
301 )
268 }
302 }
269 }
303 }
270
304
271 impl NodeMap for NodeTree {
305 impl NodeMap for NodeTree {
272 fn find_bin<'a>(
306 fn find_bin<'a>(
273 &self,
307 &self,
274 idx: &impl RevlogIndex,
308 idx: &impl RevlogIndex,
275 prefix: NodePrefixRef<'a>,
309 prefix: NodePrefixRef<'a>,
276 ) -> Result<Option<Revision>, NodeMapError> {
310 ) -> Result<Option<Revision>, NodeMapError> {
277 self.lookup(prefix.clone()).and_then(|opt| {
311 self.lookup(prefix.clone()).and_then(|opt| {
278 opt.map_or(Ok(None), |rev| has_prefix_or_none(idx, prefix, rev))
312 opt.map_or(Ok(None), |rev| has_prefix_or_none(idx, prefix, rev))
279 })
313 })
280 }
314 }
281 }
315 }
282
316
283 #[cfg(test)]
317 #[cfg(test)]
284 mod tests {
318 mod tests {
285 use super::NodeMapError::*;
319 use super::NodeMapError::*;
286 use super::*;
320 use super::*;
287 use crate::revlog::node::{hex_pad_right, Node};
321 use crate::revlog::node::{hex_pad_right, Node};
288 use std::collections::HashMap;
322 use std::collections::HashMap;
289
323
290 /// Creates a `Block` using a syntax close to the `Debug` output
324 /// Creates a `Block` using a syntax close to the `Debug` output
291 macro_rules! block {
325 macro_rules! block {
292 {$($nybble:tt : $variant:ident($val:tt)),*} => (
326 {$($nybble:tt : $variant:ident($val:tt)),*} => (
293 {
327 {
294 let mut block = Block::new();
328 let mut block = Block::new();
295 $(block.set($nybble, Element::$variant($val)));*;
329 $(block.set($nybble, Element::$variant($val)));*;
296 block
330 block
297 }
331 }
298 )
332 )
299 }
333 }
300
334
301 #[test]
335 #[test]
302 fn test_block_debug() {
336 fn test_block_debug() {
303 let mut block = Block::new();
337 let mut block = Block::new();
304 block.set(1, Element::Rev(3));
338 block.set(1, Element::Rev(3));
305 block.set(10, Element::Block(0));
339 block.set(10, Element::Block(0));
306 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
340 assert_eq!(format!("{:?}", block), "{1: Rev(3), 10: Block(0)}");
307 }
341 }
308
342
309 #[test]
343 #[test]
310 fn test_block_macro() {
344 fn test_block_macro() {
311 let block = block! {5: Block(2)};
345 let block = block! {5: Block(2)};
312 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
346 assert_eq!(format!("{:?}", block), "{5: Block(2)}");
313
347
314 let block = block! {13: Rev(15), 5: Block(2)};
348 let block = block! {13: Rev(15), 5: Block(2)};
315 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
349 assert_eq!(format!("{:?}", block), "{5: Block(2), 13: Rev(15)}");
316 }
350 }
317
351
318 #[test]
352 #[test]
319 fn test_raw_block() {
353 fn test_raw_block() {
320 let mut raw = [-1; 16];
354 let mut raw = [-1; 16];
321 raw[0] = 0;
355 raw[0] = 0;
322 raw[1] = RawElement::to_be(15);
356 raw[1] = RawElement::to_be(15);
323 raw[2] = RawElement::to_be(-2);
357 raw[2] = RawElement::to_be(-2);
324 raw[3] = RawElement::to_be(-1);
358 raw[3] = RawElement::to_be(-1);
325 raw[4] = RawElement::to_be(-3);
359 raw[4] = RawElement::to_be(-3);
326 let block = Block(raw);
360 let block = Block(raw);
327 assert_eq!(block.get(0), Element::Block(0));
361 assert_eq!(block.get(0), Element::Block(0));
328 assert_eq!(block.get(1), Element::Block(15));
362 assert_eq!(block.get(1), Element::Block(15));
329 assert_eq!(block.get(3), Element::None);
363 assert_eq!(block.get(3), Element::None);
330 assert_eq!(block.get(2), Element::Rev(0));
364 assert_eq!(block.get(2), Element::Rev(0));
331 assert_eq!(block.get(4), Element::Rev(1));
365 assert_eq!(block.get(4), Element::Rev(1));
332 }
366 }
333
367
334 type TestIndex = HashMap<Revision, Node>;
368 type TestIndex = HashMap<Revision, Node>;
335
369
336 impl RevlogIndex for TestIndex {
370 impl RevlogIndex for TestIndex {
337 fn node(&self, rev: Revision) -> Option<&Node> {
371 fn node(&self, rev: Revision) -> Option<&Node> {
338 self.get(&rev)
372 self.get(&rev)
339 }
373 }
340
374
341 fn len(&self) -> usize {
375 fn len(&self) -> usize {
342 self.len()
376 self.len()
343 }
377 }
344 }
378 }
345
379
346 /// Pad hexadecimal Node prefix with zeros on the right, then insert
380 /// Pad hexadecimal Node prefix with zeros on the right, then insert
347 ///
381 ///
348 /// This avoids having to repeatedly write very long hexadecimal
382 /// This avoids having to repeatedly write very long hexadecimal
349 /// strings for test data, and brings actual hash size independency.
383 /// strings for test data, and brings actual hash size independency.
350 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
384 fn pad_insert(idx: &mut TestIndex, rev: Revision, hex: &str) {
351 idx.insert(rev, Node::from_hex(&hex_pad_right(hex)).unwrap());
385 idx.insert(rev, Node::from_hex(&hex_pad_right(hex)).unwrap());
352 }
386 }
353
387
354 fn sample_nodetree() -> NodeTree {
388 fn sample_nodetree() -> NodeTree {
355 NodeTree::from(vec![
389 NodeTree::from(vec![
356 block![0: Rev(9)],
390 block![0: Rev(9)],
357 block![0: Rev(0), 1: Rev(9)],
391 block![0: Rev(0), 1: Rev(9)],
358 block![0: Block(1), 1:Rev(1)],
392 block![0: Block(1), 1:Rev(1)],
359 ])
393 ])
360 }
394 }
361
395
362 #[test]
396 #[test]
363 fn test_nt_debug() {
397 fn test_nt_debug() {
364 let nt = sample_nodetree();
398 let nt = sample_nodetree();
365 assert_eq!(
399 assert_eq!(
366 format!("{:?}", nt),
400 format!("{:?}", nt),
367 "readonly: \
401 "readonly: \
368 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}]"
402 [{0: Rev(9)}, {0: Rev(0), 1: Rev(9)}, {0: Block(1), 1: Rev(1)}], \
403 growable: [], \
404 root: {0: Block(1), 1: Rev(1)}",
369 );
405 );
370 }
406 }
371
407
372 #[test]
408 #[test]
373 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
409 fn test_immutable_find_simplest() -> Result<(), NodeMapError> {
374 let mut idx: TestIndex = HashMap::new();
410 let mut idx: TestIndex = HashMap::new();
375 pad_insert(&mut idx, 1, "1234deadcafe");
411 pad_insert(&mut idx, 1, "1234deadcafe");
376
412
377 let nt = NodeTree::from(vec![block![1: Rev(1)]]);
413 let nt = NodeTree::from(vec![block! {1: Rev(1)}]);
378 assert_eq!(nt.find_hex(&idx, "1")?, Some(1));
414 assert_eq!(nt.find_hex(&idx, "1")?, Some(1));
379 assert_eq!(nt.find_hex(&idx, "12")?, Some(1));
415 assert_eq!(nt.find_hex(&idx, "12")?, Some(1));
380 assert_eq!(nt.find_hex(&idx, "1234de")?, Some(1));
416 assert_eq!(nt.find_hex(&idx, "1234de")?, Some(1));
381 assert_eq!(nt.find_hex(&idx, "1a")?, None);
417 assert_eq!(nt.find_hex(&idx, "1a")?, None);
382 assert_eq!(nt.find_hex(&idx, "ab")?, None);
418 assert_eq!(nt.find_hex(&idx, "ab")?, None);
383
419
384 // and with full binary Nodes
420 // and with full binary Nodes
385 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
421 assert_eq!(nt.find_node(&idx, idx.get(&1).unwrap())?, Some(1));
386 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
422 let unknown = Node::from_hex(&hex_pad_right("3d")).unwrap();
387 assert_eq!(nt.find_node(&idx, &unknown)?, None);
423 assert_eq!(nt.find_node(&idx, &unknown)?, None);
388 Ok(())
424 Ok(())
389 }
425 }
390
426
391 #[test]
427 #[test]
392 fn test_immutable_find_one_jump() {
428 fn test_immutable_find_one_jump() {
393 let mut idx = TestIndex::new();
429 let mut idx = TestIndex::new();
394 pad_insert(&mut idx, 9, "012");
430 pad_insert(&mut idx, 9, "012");
395 pad_insert(&mut idx, 0, "00a");
431 pad_insert(&mut idx, 0, "00a");
396
432
397 let nt = sample_nodetree();
433 let nt = sample_nodetree();
398
434
399 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults));
435 assert_eq!(nt.find_hex(&idx, "0"), Err(MultipleResults));
400 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9)));
436 assert_eq!(nt.find_hex(&idx, "01"), Ok(Some(9)));
401 assert_eq!(nt.find_hex(&idx, "00"), Ok(Some(0)));
437 assert_eq!(nt.find_hex(&idx, "00"), Ok(Some(0)));
402 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0)));
438 assert_eq!(nt.find_hex(&idx, "00a"), Ok(Some(0)));
403 }
439 }
440
441 #[test]
442 fn test_mutated_find() -> Result<(), NodeMapError> {
443 let mut idx = TestIndex::new();
444 pad_insert(&mut idx, 9, "012");
445 pad_insert(&mut idx, 0, "00a");
446 pad_insert(&mut idx, 2, "cafe");
447 pad_insert(&mut idx, 3, "15");
448 pad_insert(&mut idx, 1, "10");
449
450 let nt = NodeTree {
451 readonly: sample_nodetree().readonly,
452 growable: vec![block![0: Rev(1), 5: Rev(3)]],
453 root: block![0: Block(1), 1:Block(3), 12: Rev(2)],
454 };
455 assert_eq!(nt.find_hex(&idx, "10")?, Some(1));
456 assert_eq!(nt.find_hex(&idx, "c")?, Some(2));
457 assert_eq!(nt.find_hex(&idx, "00")?, Some(0));
458 assert_eq!(nt.find_hex(&idx, "01")?, Some(9));
459 Ok(())
460 }
404 }
461 }
General Comments 0
You need to be logged in to leave comments. Login now