##// END OF EJS Templates
rust: replace Node::encode_hex with std::fmt::LowerHex...
Simon Sapin -
r47155:6380efb8 default
parent child Browse files
Show More
@@ -1,146 +1,146 b''
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 use clap::*;
6 use clap::*;
7 use hg::revlog::node::*;
7 use hg::revlog::node::*;
8 use hg::revlog::nodemap::*;
8 use hg::revlog::nodemap::*;
9 use hg::revlog::*;
9 use hg::revlog::*;
10 use memmap::MmapOptions;
10 use memmap::MmapOptions;
11 use rand::Rng;
11 use rand::Rng;
12 use std::fs::File;
12 use std::fs::File;
13 use std::io;
13 use std::io;
14 use std::io::Write;
14 use std::io::Write;
15 use std::path::{Path, PathBuf};
15 use std::path::{Path, PathBuf};
16 use std::str::FromStr;
16 use std::str::FromStr;
17 use std::time::Instant;
17 use std::time::Instant;
18
18
19 mod index;
19 mod index;
20 use index::Index;
20 use index::Index;
21
21
22 fn mmap_index(repo_path: &Path) -> Index {
22 fn mmap_index(repo_path: &Path) -> Index {
23 let mut path = PathBuf::from(repo_path);
23 let mut path = PathBuf::from(repo_path);
24 path.extend([".hg", "store", "00changelog.i"].iter());
24 path.extend([".hg", "store", "00changelog.i"].iter());
25 Index::load_mmap(path)
25 Index::load_mmap(path)
26 }
26 }
27
27
28 fn mmap_nodemap(path: &Path) -> NodeTree {
28 fn mmap_nodemap(path: &Path) -> NodeTree {
29 let file = File::open(path).unwrap();
29 let file = File::open(path).unwrap();
30 let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
30 let mmap = unsafe { MmapOptions::new().map(&file).unwrap() };
31 let len = mmap.len();
31 let len = mmap.len();
32 NodeTree::load_bytes(Box::new(mmap), len)
32 NodeTree::load_bytes(Box::new(mmap), len)
33 }
33 }
34
34
35 /// Scan the whole index and create the corresponding nodemap file at `path`
35 /// Scan the whole index and create the corresponding nodemap file at `path`
36 fn create(index: &Index, path: &Path) -> io::Result<()> {
36 fn create(index: &Index, path: &Path) -> io::Result<()> {
37 let mut file = File::create(path)?;
37 let mut file = File::create(path)?;
38 let start = Instant::now();
38 let start = Instant::now();
39 let mut nm = NodeTree::default();
39 let mut nm = NodeTree::default();
40 for rev in 0..index.len() {
40 for rev in 0..index.len() {
41 let rev = rev as Revision;
41 let rev = rev as Revision;
42 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
42 nm.insert(index, index.node(rev).unwrap(), rev).unwrap();
43 }
43 }
44 eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
44 eprintln!("Nodemap constructed in RAM in {:?}", start.elapsed());
45 file.write(&nm.into_readonly_and_added_bytes().1)?;
45 file.write(&nm.into_readonly_and_added_bytes().1)?;
46 eprintln!("Nodemap written to disk");
46 eprintln!("Nodemap written to disk");
47 Ok(())
47 Ok(())
48 }
48 }
49
49
50 fn query(index: &Index, nm: &NodeTree, prefix: &str) {
50 fn query(index: &Index, nm: &NodeTree, prefix: &str) {
51 let start = Instant::now();
51 let start = Instant::now();
52 let res = nm.find_hex(index, prefix);
52 let res = nm.find_hex(index, prefix);
53 println!("Result found in {:?}: {:?}", start.elapsed(), res);
53 println!("Result found in {:?}: {:?}", start.elapsed(), res);
54 }
54 }
55
55
56 fn bench(index: &Index, nm: &NodeTree, queries: usize) {
56 fn bench(index: &Index, nm: &NodeTree, queries: usize) {
57 let len = index.len() as u32;
57 let len = index.len() as u32;
58 let mut rng = rand::thread_rng();
58 let mut rng = rand::thread_rng();
59 let nodes: Vec<Node> = (0..queries)
59 let nodes: Vec<Node> = (0..queries)
60 .map(|_| {
60 .map(|_| {
61 index
61 index
62 .node((rng.gen::<u32>() % len) as Revision)
62 .node((rng.gen::<u32>() % len) as Revision)
63 .unwrap()
63 .unwrap()
64 .clone()
64 .clone()
65 })
65 })
66 .collect();
66 .collect();
67 if queries < 10 {
67 if queries < 10 {
68 let nodes_hex: Vec<String> =
68 let nodes_hex: Vec<String> =
69 nodes.iter().map(|n| n.encode_hex()).collect();
69 nodes.iter().map(|n| format!("{:x}", n)).collect();
70 println!("Nodes: {:?}", nodes_hex);
70 println!("Nodes: {:?}", nodes_hex);
71 }
71 }
72 let mut last: Option<Revision> = None;
72 let mut last: Option<Revision> = None;
73 let start = Instant::now();
73 let start = Instant::now();
74 for node in nodes.iter() {
74 for node in nodes.iter() {
75 last = nm.find_bin(index, node.into()).unwrap();
75 last = nm.find_bin(index, node.into()).unwrap();
76 }
76 }
77 let elapsed = start.elapsed();
77 let elapsed = start.elapsed();
78 println!(
78 println!(
79 "Did {} queries in {:?} (mean {:?}), last was {:?} with result {:?}",
79 "Did {} queries in {:?} (mean {:?}), last was {:x} with result {:?}",
80 queries,
80 queries,
81 elapsed,
81 elapsed,
82 elapsed / (queries as u32),
82 elapsed / (queries as u32),
83 nodes.last().unwrap().encode_hex(),
83 nodes.last().unwrap(),
84 last
84 last
85 );
85 );
86 }
86 }
87
87
88 fn main() {
88 fn main() {
89 let matches = App::new("Nodemap pure Rust example")
89 let matches = App::new("Nodemap pure Rust example")
90 .arg(
90 .arg(
91 Arg::with_name("REPOSITORY")
91 Arg::with_name("REPOSITORY")
92 .help("Path to the repository, always necessary for its index")
92 .help("Path to the repository, always necessary for its index")
93 .required(true),
93 .required(true),
94 )
94 )
95 .arg(
95 .arg(
96 Arg::with_name("NODEMAP_FILE")
96 Arg::with_name("NODEMAP_FILE")
97 .help("Path to the nodemap file, independent of REPOSITORY")
97 .help("Path to the nodemap file, independent of REPOSITORY")
98 .required(true),
98 .required(true),
99 )
99 )
100 .subcommand(
100 .subcommand(
101 SubCommand::with_name("create")
101 SubCommand::with_name("create")
102 .about("Create NODEMAP_FILE by scanning repository index"),
102 .about("Create NODEMAP_FILE by scanning repository index"),
103 )
103 )
104 .subcommand(
104 .subcommand(
105 SubCommand::with_name("query")
105 SubCommand::with_name("query")
106 .about("Query NODEMAP_FILE for PREFIX")
106 .about("Query NODEMAP_FILE for PREFIX")
107 .arg(Arg::with_name("PREFIX").required(true)),
107 .arg(Arg::with_name("PREFIX").required(true)),
108 )
108 )
109 .subcommand(
109 .subcommand(
110 SubCommand::with_name("bench")
110 SubCommand::with_name("bench")
111 .about(
111 .about(
112 "Perform #QUERIES random successful queries on NODEMAP_FILE")
112 "Perform #QUERIES random successful queries on NODEMAP_FILE")
113 .arg(Arg::with_name("QUERIES").required(true)),
113 .arg(Arg::with_name("QUERIES").required(true)),
114 )
114 )
115 .get_matches();
115 .get_matches();
116
116
117 let repo = matches.value_of("REPOSITORY").unwrap();
117 let repo = matches.value_of("REPOSITORY").unwrap();
118 let nm_path = matches.value_of("NODEMAP_FILE").unwrap();
118 let nm_path = matches.value_of("NODEMAP_FILE").unwrap();
119
119
120 let index = mmap_index(&Path::new(repo));
120 let index = mmap_index(&Path::new(repo));
121
121
122 if let Some(_) = matches.subcommand_matches("create") {
122 if let Some(_) = matches.subcommand_matches("create") {
123 println!("Creating nodemap file {} for repository {}", nm_path, repo);
123 println!("Creating nodemap file {} for repository {}", nm_path, repo);
124 create(&index, &Path::new(nm_path)).unwrap();
124 create(&index, &Path::new(nm_path)).unwrap();
125 return;
125 return;
126 }
126 }
127
127
128 let nm = mmap_nodemap(&Path::new(nm_path));
128 let nm = mmap_nodemap(&Path::new(nm_path));
129 if let Some(matches) = matches.subcommand_matches("query") {
129 if let Some(matches) = matches.subcommand_matches("query") {
130 let prefix = matches.value_of("PREFIX").unwrap();
130 let prefix = matches.value_of("PREFIX").unwrap();
131 println!(
131 println!(
132 "Querying {} in nodemap file {} of repository {}",
132 "Querying {} in nodemap file {} of repository {}",
133 prefix, nm_path, repo
133 prefix, nm_path, repo
134 );
134 );
135 query(&index, &nm, prefix);
135 query(&index, &nm, prefix);
136 }
136 }
137 if let Some(matches) = matches.subcommand_matches("bench") {
137 if let Some(matches) = matches.subcommand_matches("bench") {
138 let queries =
138 let queries =
139 usize::from_str(matches.value_of("QUERIES").unwrap()).unwrap();
139 usize::from_str(matches.value_of("QUERIES").unwrap()).unwrap();
140 println!(
140 println!(
141 "Doing {} random queries in nodemap file {} of repository {}",
141 "Doing {} random queries in nodemap file {} of repository {}",
142 queries, nm_path, repo
142 queries, nm_path, repo
143 );
143 );
144 bench(&index, &nm, queries);
144 bench(&index, &nm, queries);
145 }
145 }
146 }
146 }
@@ -1,457 +1,459 b''
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
1 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
2 //
2 //
3 // This software may be used and distributed according to the terms of the
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 //! Definitions and utilities for Revision nodes
6 //! Definitions and utilities for Revision nodes
7 //!
7 //!
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
8 //! In Mercurial code base, it is customary to call "a node" the binary SHA
9 //! of a revision.
9 //! of a revision.
10
10
11 use bytes_cast::BytesCast;
11 use bytes_cast::BytesCast;
12 use hex::{self, FromHex, FromHexError};
12 use hex::{self, FromHex, FromHexError};
13 use std::convert::TryFrom;
13 use std::convert::TryFrom;
14 use std::fmt;
14
15
15 /// The length in bytes of a `Node`
16 /// The length in bytes of a `Node`
16 ///
17 ///
17 /// This constant is meant to ease refactors of this module, and
18 /// This constant is meant to ease refactors of this module, and
18 /// are private so that calling code does not expect all nodes have
19 /// are private so that calling code does not expect all nodes have
19 /// the same size, should we support several formats concurrently in
20 /// the same size, should we support several formats concurrently in
20 /// the future.
21 /// the future.
21 pub const NODE_BYTES_LENGTH: usize = 20;
22 pub const NODE_BYTES_LENGTH: usize = 20;
22
23
23 /// Id of the null node.
24 /// Id of the null node.
24 ///
25 ///
25 /// Used to indicate the absence of node.
26 /// Used to indicate the absence of node.
26 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
27 pub const NULL_NODE_ID: [u8; NODE_BYTES_LENGTH] = [0u8; NODE_BYTES_LENGTH];
27
28
28 /// The length in bytes of a `Node`
29 /// The length in bytes of a `Node`
29 ///
30 ///
30 /// see also `NODES_BYTES_LENGTH` about it being private.
31 /// see also `NODES_BYTES_LENGTH` about it being private.
31 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
32 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
32
33
33 /// Private alias for readability and to ease future change
34 /// Private alias for readability and to ease future change
34 type NodeData = [u8; NODE_BYTES_LENGTH];
35 type NodeData = [u8; NODE_BYTES_LENGTH];
35
36
36 /// Binary revision SHA
37 /// Binary revision SHA
37 ///
38 ///
38 /// ## Future changes of hash size
39 /// ## Future changes of hash size
39 ///
40 ///
40 /// To accomodate future changes of hash size, Rust callers
41 /// To accomodate future changes of hash size, Rust callers
41 /// should use the conversion methods at the boundaries (FFI, actual
42 /// should use the conversion methods at the boundaries (FFI, actual
42 /// computation of hashes and I/O) only, and only if required.
43 /// computation of hashes and I/O) only, and only if required.
43 ///
44 ///
44 /// All other callers outside of unit tests should just handle `Node` values
45 /// All other callers outside of unit tests should just handle `Node` values
45 /// and never make any assumption on the actual length, using [`nybbles_len`]
46 /// and never make any assumption on the actual length, using [`nybbles_len`]
46 /// if they need a loop boundary.
47 /// if they need a loop boundary.
47 ///
48 ///
48 /// All methods that create a `Node` either take a type that enforces
49 /// All methods that create a `Node` either take a type that enforces
49 /// the size or fail immediately at runtime with [`ExactLengthRequired`].
50 /// the size or fail immediately at runtime with [`ExactLengthRequired`].
50 ///
51 ///
51 /// [`nybbles_len`]: #method.nybbles_len
52 /// [`nybbles_len`]: #method.nybbles_len
52 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
53 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
53 #[derive(Clone, Debug, PartialEq, BytesCast)]
54 #[derive(Clone, Debug, PartialEq, BytesCast)]
54 #[repr(transparent)]
55 #[repr(transparent)]
55 pub struct Node {
56 pub struct Node {
56 data: NodeData,
57 data: NodeData,
57 }
58 }
58
59
59 /// The node value for NULL_REVISION
60 /// The node value for NULL_REVISION
60 pub const NULL_NODE: Node = Node {
61 pub const NULL_NODE: Node = Node {
61 data: [0; NODE_BYTES_LENGTH],
62 data: [0; NODE_BYTES_LENGTH],
62 };
63 };
63
64
64 impl From<NodeData> for Node {
65 impl From<NodeData> for Node {
65 fn from(data: NodeData) -> Node {
66 fn from(data: NodeData) -> Node {
66 Node { data }
67 Node { data }
67 }
68 }
68 }
69 }
69
70
70 /// Return an error if the slice has an unexpected length
71 /// Return an error if the slice has an unexpected length
71 impl<'a> TryFrom<&'a [u8]> for &'a Node {
72 impl<'a> TryFrom<&'a [u8]> for &'a Node {
72 type Error = ();
73 type Error = ();
73
74
74 #[inline]
75 #[inline]
75 fn try_from(bytes: &'a [u8]) -> Result<&'a Node, Self::Error> {
76 fn try_from(bytes: &'a [u8]) -> Result<&'a Node, Self::Error> {
76 match Node::from_bytes(bytes) {
77 match Node::from_bytes(bytes) {
77 Ok((node, rest)) if rest.is_empty() => Ok(node),
78 Ok((node, rest)) if rest.is_empty() => Ok(node),
78 _ => Err(()),
79 _ => Err(()),
79 }
80 }
80 }
81 }
81 }
82 }
82
83
84 impl fmt::LowerHex for Node {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 for &byte in &self.data {
87 write!(f, "{:02x}", byte)?
88 }
89 Ok(())
90 }
91 }
92
83 #[derive(Debug, PartialEq)]
93 #[derive(Debug, PartialEq)]
84 pub enum NodeError {
94 pub enum NodeError {
85 ExactLengthRequired(usize, String),
95 ExactLengthRequired(usize, String),
86 PrefixTooLong(String),
96 PrefixTooLong(String),
87 HexError(FromHexError, String),
97 HexError(FromHexError, String),
88 }
98 }
89
99
90 /// Low level utility function, also for prefixes
100 /// Low level utility function, also for prefixes
91 fn get_nybble(s: &[u8], i: usize) -> u8 {
101 fn get_nybble(s: &[u8], i: usize) -> u8 {
92 if i % 2 == 0 {
102 if i % 2 == 0 {
93 s[i / 2] >> 4
103 s[i / 2] >> 4
94 } else {
104 } else {
95 s[i / 2] & 0x0f
105 s[i / 2] & 0x0f
96 }
106 }
97 }
107 }
98
108
99 impl Node {
109 impl Node {
100 /// Retrieve the `i`th half-byte of the binary data.
110 /// Retrieve the `i`th half-byte of the binary data.
101 ///
111 ///
102 /// This is also the `i`th hexadecimal digit in numeric form,
112 /// This is also the `i`th hexadecimal digit in numeric form,
103 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
113 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
104 pub fn get_nybble(&self, i: usize) -> u8 {
114 pub fn get_nybble(&self, i: usize) -> u8 {
105 get_nybble(&self.data, i)
115 get_nybble(&self.data, i)
106 }
116 }
107
117
108 /// Length of the data, in nybbles
118 /// Length of the data, in nybbles
109 pub fn nybbles_len(&self) -> usize {
119 pub fn nybbles_len(&self) -> usize {
110 // public exposure as an instance method only, so that we can
120 // public exposure as an instance method only, so that we can
111 // easily support several sizes of hashes if needed in the future.
121 // easily support several sizes of hashes if needed in the future.
112 NODE_NYBBLES_LENGTH
122 NODE_NYBBLES_LENGTH
113 }
123 }
114
124
115 /// Convert from hexadecimal string representation
125 /// Convert from hexadecimal string representation
116 ///
126 ///
117 /// Exact length is required.
127 /// Exact length is required.
118 ///
128 ///
119 /// To be used in FFI and I/O only, in order to facilitate future
129 /// To be used in FFI and I/O only, in order to facilitate future
120 /// changes of hash format.
130 /// changes of hash format.
121 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> {
131 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> {
122 Ok(NodeData::from_hex(hex.as_ref())
132 Ok(NodeData::from_hex(hex.as_ref())
123 .map_err(|e| NodeError::from((e, hex)))?
133 .map_err(|e| NodeError::from((e, hex)))?
124 .into())
134 .into())
125 }
135 }
126
136
127 /// Convert to hexadecimal string representation
128 ///
129 /// To be used in FFI and I/O only, in order to facilitate future
130 /// changes of hash format.
131 pub fn encode_hex(&self) -> String {
132 hex::encode(self.data)
133 }
134
135 /// Provide access to binary data
137 /// Provide access to binary data
136 ///
138 ///
137 /// This is needed by FFI layers, for instance to return expected
139 /// This is needed by FFI layers, for instance to return expected
138 /// binary values to Python.
140 /// binary values to Python.
139 pub fn as_bytes(&self) -> &[u8] {
141 pub fn as_bytes(&self) -> &[u8] {
140 &self.data
142 &self.data
141 }
143 }
142 }
144 }
143
145
144 impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError {
146 impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError {
145 fn from(err_offender: (FromHexError, T)) -> Self {
147 fn from(err_offender: (FromHexError, T)) -> Self {
146 let (err, offender) = err_offender;
148 let (err, offender) = err_offender;
147 let offender = String::from_utf8_lossy(offender.as_ref()).into_owned();
149 let offender = String::from_utf8_lossy(offender.as_ref()).into_owned();
148 match err {
150 match err {
149 FromHexError::InvalidStringLength => {
151 FromHexError::InvalidStringLength => {
150 NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender)
152 NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender)
151 }
153 }
152 _ => NodeError::HexError(err, offender),
154 _ => NodeError::HexError(err, offender),
153 }
155 }
154 }
156 }
155 }
157 }
156
158
157 /// The beginning of a binary revision SHA.
159 /// The beginning of a binary revision SHA.
158 ///
160 ///
159 /// Since it can potentially come from an hexadecimal representation with
161 /// Since it can potentially come from an hexadecimal representation with
160 /// odd length, it needs to carry around whether the last 4 bits are relevant
162 /// odd length, it needs to carry around whether the last 4 bits are relevant
161 /// or not.
163 /// or not.
162 #[derive(Debug, PartialEq)]
164 #[derive(Debug, PartialEq)]
163 pub struct NodePrefix {
165 pub struct NodePrefix {
164 buf: Vec<u8>,
166 buf: Vec<u8>,
165 is_odd: bool,
167 is_odd: bool,
166 }
168 }
167
169
168 impl NodePrefix {
170 impl NodePrefix {
169 /// Convert from hexadecimal string representation
171 /// Convert from hexadecimal string representation
170 ///
172 ///
171 /// Similarly to `hex::decode`, can be used with Unicode string types
173 /// Similarly to `hex::decode`, can be used with Unicode string types
172 /// (`String`, `&str`) as well as bytes.
174 /// (`String`, `&str`) as well as bytes.
173 ///
175 ///
174 /// To be used in FFI and I/O only, in order to facilitate future
176 /// To be used in FFI and I/O only, in order to facilitate future
175 /// changes of hash format.
177 /// changes of hash format.
176 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
178 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
177 let hex = hex.as_ref();
179 let hex = hex.as_ref();
178 let len = hex.len();
180 let len = hex.len();
179 if len > NODE_NYBBLES_LENGTH {
181 if len > NODE_NYBBLES_LENGTH {
180 return Err(NodeError::PrefixTooLong(
182 return Err(NodeError::PrefixTooLong(
181 String::from_utf8_lossy(hex).to_owned().to_string(),
183 String::from_utf8_lossy(hex).to_owned().to_string(),
182 ));
184 ));
183 }
185 }
184
186
185 let is_odd = len % 2 == 1;
187 let is_odd = len % 2 == 1;
186 let even_part = if is_odd { &hex[..len - 1] } else { hex };
188 let even_part = if is_odd { &hex[..len - 1] } else { hex };
187 let mut buf: Vec<u8> =
189 let mut buf: Vec<u8> =
188 Vec::from_hex(&even_part).map_err(|e| (e, hex))?;
190 Vec::from_hex(&even_part).map_err(|e| (e, hex))?;
189
191
190 if is_odd {
192 if is_odd {
191 let latest_char = char::from(hex[len - 1]);
193 let latest_char = char::from(hex[len - 1]);
192 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
194 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
193 (
195 (
194 FromHexError::InvalidHexCharacter {
196 FromHexError::InvalidHexCharacter {
195 c: latest_char,
197 c: latest_char,
196 index: len - 1,
198 index: len - 1,
197 },
199 },
198 hex,
200 hex,
199 )
201 )
200 })? as u8;
202 })? as u8;
201 buf.push(latest_nybble << 4);
203 buf.push(latest_nybble << 4);
202 }
204 }
203 Ok(NodePrefix { buf, is_odd })
205 Ok(NodePrefix { buf, is_odd })
204 }
206 }
205
207
206 pub fn borrow(&self) -> NodePrefixRef {
208 pub fn borrow(&self) -> NodePrefixRef {
207 NodePrefixRef {
209 NodePrefixRef {
208 buf: &self.buf,
210 buf: &self.buf,
209 is_odd: self.is_odd,
211 is_odd: self.is_odd,
210 }
212 }
211 }
213 }
212 }
214 }
213
215
214 #[derive(Clone, Debug, PartialEq)]
216 #[derive(Clone, Debug, PartialEq)]
215 pub struct NodePrefixRef<'a> {
217 pub struct NodePrefixRef<'a> {
216 buf: &'a [u8],
218 buf: &'a [u8],
217 is_odd: bool,
219 is_odd: bool,
218 }
220 }
219
221
220 impl<'a> NodePrefixRef<'a> {
222 impl<'a> NodePrefixRef<'a> {
221 pub fn len(&self) -> usize {
223 pub fn len(&self) -> usize {
222 if self.is_odd {
224 if self.is_odd {
223 self.buf.len() * 2 - 1
225 self.buf.len() * 2 - 1
224 } else {
226 } else {
225 self.buf.len() * 2
227 self.buf.len() * 2
226 }
228 }
227 }
229 }
228
230
229 pub fn is_empty(&self) -> bool {
231 pub fn is_empty(&self) -> bool {
230 self.len() == 0
232 self.len() == 0
231 }
233 }
232
234
233 pub fn is_prefix_of(&self, node: &Node) -> bool {
235 pub fn is_prefix_of(&self, node: &Node) -> bool {
234 if self.is_odd {
236 if self.is_odd {
235 let buf = self.buf;
237 let buf = self.buf;
236 let last_pos = buf.len() - 1;
238 let last_pos = buf.len() - 1;
237 node.data.starts_with(buf.split_at(last_pos).0)
239 node.data.starts_with(buf.split_at(last_pos).0)
238 && node.data[last_pos] >> 4 == buf[last_pos] >> 4
240 && node.data[last_pos] >> 4 == buf[last_pos] >> 4
239 } else {
241 } else {
240 node.data.starts_with(self.buf)
242 node.data.starts_with(self.buf)
241 }
243 }
242 }
244 }
243
245
244 /// Retrieve the `i`th half-byte from the prefix.
246 /// Retrieve the `i`th half-byte from the prefix.
245 ///
247 ///
246 /// This is also the `i`th hexadecimal digit in numeric form,
248 /// This is also the `i`th hexadecimal digit in numeric form,
247 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
249 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
248 pub fn get_nybble(&self, i: usize) -> u8 {
250 pub fn get_nybble(&self, i: usize) -> u8 {
249 assert!(i < self.len());
251 assert!(i < self.len());
250 get_nybble(self.buf, i)
252 get_nybble(self.buf, i)
251 }
253 }
252
254
253 /// Return the index first nybble that's different from `node`
255 /// Return the index first nybble that's different from `node`
254 ///
256 ///
255 /// If the return value is `None` that means that `self` is
257 /// If the return value is `None` that means that `self` is
256 /// a prefix of `node`, but the current method is a bit slower
258 /// a prefix of `node`, but the current method is a bit slower
257 /// than `is_prefix_of`.
259 /// than `is_prefix_of`.
258 ///
260 ///
259 /// Returned index is as in `get_nybble`, i.e., starting at 0.
261 /// Returned index is as in `get_nybble`, i.e., starting at 0.
260 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
262 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
261 let buf = self.buf;
263 let buf = self.buf;
262 let until = if self.is_odd {
264 let until = if self.is_odd {
263 buf.len() - 1
265 buf.len() - 1
264 } else {
266 } else {
265 buf.len()
267 buf.len()
266 };
268 };
267 for (i, item) in buf.iter().enumerate().take(until) {
269 for (i, item) in buf.iter().enumerate().take(until) {
268 if *item != node.data[i] {
270 if *item != node.data[i] {
269 return if *item & 0xf0 == node.data[i] & 0xf0 {
271 return if *item & 0xf0 == node.data[i] & 0xf0 {
270 Some(2 * i + 1)
272 Some(2 * i + 1)
271 } else {
273 } else {
272 Some(2 * i)
274 Some(2 * i)
273 };
275 };
274 }
276 }
275 }
277 }
276 if self.is_odd && buf[until] & 0xf0 != node.data[until] & 0xf0 {
278 if self.is_odd && buf[until] & 0xf0 != node.data[until] & 0xf0 {
277 Some(until * 2)
279 Some(until * 2)
278 } else {
280 } else {
279 None
281 None
280 }
282 }
281 }
283 }
282 }
284 }
283
285
284 /// A shortcut for full `Node` references
286 /// A shortcut for full `Node` references
285 impl<'a> From<&'a Node> for NodePrefixRef<'a> {
287 impl<'a> From<&'a Node> for NodePrefixRef<'a> {
286 fn from(node: &'a Node) -> Self {
288 fn from(node: &'a Node) -> Self {
287 NodePrefixRef {
289 NodePrefixRef {
288 buf: &node.data,
290 buf: &node.data,
289 is_odd: false,
291 is_odd: false,
290 }
292 }
291 }
293 }
292 }
294 }
293
295
294 impl PartialEq<Node> for NodePrefixRef<'_> {
296 impl PartialEq<Node> for NodePrefixRef<'_> {
295 fn eq(&self, other: &Node) -> bool {
297 fn eq(&self, other: &Node) -> bool {
296 !self.is_odd && self.buf == other.data
298 !self.is_odd && self.buf == other.data
297 }
299 }
298 }
300 }
299
301
300 #[cfg(test)]
302 #[cfg(test)]
301 mod tests {
303 mod tests {
302 use super::*;
304 use super::*;
303
305
304 fn sample_node() -> Node {
306 fn sample_node() -> Node {
305 let mut data = [0; NODE_BYTES_LENGTH];
307 let mut data = [0; NODE_BYTES_LENGTH];
306 data.copy_from_slice(&[
308 data.copy_from_slice(&[
307 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
309 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
308 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
310 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
309 ]);
311 ]);
310 data.into()
312 data.into()
311 }
313 }
312
314
313 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
315 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
314 ///check_hash
316 ///check_hash
315 /// The padding is made with zeros
317 /// The padding is made with zeros
316 pub fn hex_pad_right(hex: &str) -> String {
318 pub fn hex_pad_right(hex: &str) -> String {
317 let mut res = hex.to_string();
319 let mut res = hex.to_string();
318 while res.len() < NODE_NYBBLES_LENGTH {
320 while res.len() < NODE_NYBBLES_LENGTH {
319 res.push('0');
321 res.push('0');
320 }
322 }
321 res
323 res
322 }
324 }
323
325
324 fn sample_node_hex() -> String {
326 fn sample_node_hex() -> String {
325 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef")
327 hex_pad_right("0123456789abcdeffedcba9876543210deadbeef")
326 }
328 }
327
329
328 #[test]
330 #[test]
329 fn test_node_from_hex() {
331 fn test_node_from_hex() {
330 assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node()));
332 assert_eq!(Node::from_hex(&sample_node_hex()), Ok(sample_node()));
331
333
332 let mut short = hex_pad_right("0123");
334 let mut short = hex_pad_right("0123");
333 short.pop();
335 short.pop();
334 short.pop();
336 short.pop();
335 assert_eq!(
337 assert_eq!(
336 Node::from_hex(&short),
338 Node::from_hex(&short),
337 Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
339 Err(NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, short)),
338 );
340 );
339
341
340 let not_hex = hex_pad_right("012... oops");
342 let not_hex = hex_pad_right("012... oops");
341 assert_eq!(
343 assert_eq!(
342 Node::from_hex(&not_hex),
344 Node::from_hex(&not_hex),
343 Err(NodeError::HexError(
345 Err(NodeError::HexError(
344 FromHexError::InvalidHexCharacter { c: '.', index: 3 },
346 FromHexError::InvalidHexCharacter { c: '.', index: 3 },
345 not_hex,
347 not_hex,
346 )),
348 )),
347 );
349 );
348 }
350 }
349
351
350 #[test]
352 #[test]
351 fn test_node_encode_hex() {
353 fn test_node_encode_hex() {
352 assert_eq!(sample_node().encode_hex(), sample_node_hex());
354 assert_eq!(format!("{:x}", sample_node()), sample_node_hex());
353 }
355 }
354
356
355 #[test]
357 #[test]
356 fn test_prefix_from_hex() -> Result<(), NodeError> {
358 fn test_prefix_from_hex() -> Result<(), NodeError> {
357 assert_eq!(
359 assert_eq!(
358 NodePrefix::from_hex("0e1")?,
360 NodePrefix::from_hex("0e1")?,
359 NodePrefix {
361 NodePrefix {
360 buf: vec![14, 16],
362 buf: vec![14, 16],
361 is_odd: true
363 is_odd: true
362 }
364 }
363 );
365 );
364 assert_eq!(
366 assert_eq!(
365 NodePrefix::from_hex("0e1a")?,
367 NodePrefix::from_hex("0e1a")?,
366 NodePrefix {
368 NodePrefix {
367 buf: vec![14, 26],
369 buf: vec![14, 26],
368 is_odd: false
370 is_odd: false
369 }
371 }
370 );
372 );
371
373
372 // checking limit case
374 // checking limit case
373 let node_as_vec = sample_node().data.iter().cloned().collect();
375 let node_as_vec = sample_node().data.iter().cloned().collect();
374 assert_eq!(
376 assert_eq!(
375 NodePrefix::from_hex(sample_node_hex())?,
377 NodePrefix::from_hex(sample_node_hex())?,
376 NodePrefix {
378 NodePrefix {
377 buf: node_as_vec,
379 buf: node_as_vec,
378 is_odd: false
380 is_odd: false
379 }
381 }
380 );
382 );
381
383
382 Ok(())
384 Ok(())
383 }
385 }
384
386
385 #[test]
387 #[test]
386 fn test_prefix_from_hex_errors() {
388 fn test_prefix_from_hex_errors() {
387 assert_eq!(
389 assert_eq!(
388 NodePrefix::from_hex("testgr"),
390 NodePrefix::from_hex("testgr"),
389 Err(NodeError::HexError(
391 Err(NodeError::HexError(
390 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
392 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
391 "testgr".to_string()
393 "testgr".to_string()
392 ))
394 ))
393 );
395 );
394 let mut long = NULL_NODE.encode_hex();
396 let mut long = format!("{:x}", NULL_NODE);
395 long.push('c');
397 long.push('c');
396 match NodePrefix::from_hex(&long)
398 match NodePrefix::from_hex(&long)
397 .expect_err("should be refused as too long")
399 .expect_err("should be refused as too long")
398 {
400 {
399 NodeError::PrefixTooLong(s) => assert_eq!(s, long),
401 NodeError::PrefixTooLong(s) => assert_eq!(s, long),
400 err => panic!(format!("Should have been TooLong, got {:?}", err)),
402 err => panic!(format!("Should have been TooLong, got {:?}", err)),
401 }
403 }
402 }
404 }
403
405
404 #[test]
406 #[test]
405 fn test_is_prefix_of() -> Result<(), NodeError> {
407 fn test_is_prefix_of() -> Result<(), NodeError> {
406 let mut node_data = [0; NODE_BYTES_LENGTH];
408 let mut node_data = [0; NODE_BYTES_LENGTH];
407 node_data[0] = 0x12;
409 node_data[0] = 0x12;
408 node_data[1] = 0xca;
410 node_data[1] = 0xca;
409 let node = Node::from(node_data);
411 let node = Node::from(node_data);
410 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
412 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
411 assert!(!NodePrefix::from_hex("1a")?.borrow().is_prefix_of(&node));
413 assert!(!NodePrefix::from_hex("1a")?.borrow().is_prefix_of(&node));
412 assert!(NodePrefix::from_hex("12c")?.borrow().is_prefix_of(&node));
414 assert!(NodePrefix::from_hex("12c")?.borrow().is_prefix_of(&node));
413 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
415 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
414 Ok(())
416 Ok(())
415 }
417 }
416
418
417 #[test]
419 #[test]
418 fn test_get_nybble() -> Result<(), NodeError> {
420 fn test_get_nybble() -> Result<(), NodeError> {
419 let prefix = NodePrefix::from_hex("dead6789cafe")?;
421 let prefix = NodePrefix::from_hex("dead6789cafe")?;
420 assert_eq!(prefix.borrow().get_nybble(0), 13);
422 assert_eq!(prefix.borrow().get_nybble(0), 13);
421 assert_eq!(prefix.borrow().get_nybble(7), 9);
423 assert_eq!(prefix.borrow().get_nybble(7), 9);
422 Ok(())
424 Ok(())
423 }
425 }
424
426
425 #[test]
427 #[test]
426 fn test_first_different_nybble_even_prefix() {
428 fn test_first_different_nybble_even_prefix() {
427 let prefix = NodePrefix::from_hex("12ca").unwrap();
429 let prefix = NodePrefix::from_hex("12ca").unwrap();
428 let prefref = prefix.borrow();
430 let prefref = prefix.borrow();
429 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
431 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
430 assert_eq!(prefref.first_different_nybble(&node), Some(0));
432 assert_eq!(prefref.first_different_nybble(&node), Some(0));
431 node.data[0] = 0x13;
433 node.data[0] = 0x13;
432 assert_eq!(prefref.first_different_nybble(&node), Some(1));
434 assert_eq!(prefref.first_different_nybble(&node), Some(1));
433 node.data[0] = 0x12;
435 node.data[0] = 0x12;
434 assert_eq!(prefref.first_different_nybble(&node), Some(2));
436 assert_eq!(prefref.first_different_nybble(&node), Some(2));
435 node.data[1] = 0xca;
437 node.data[1] = 0xca;
436 // now it is a prefix
438 // now it is a prefix
437 assert_eq!(prefref.first_different_nybble(&node), None);
439 assert_eq!(prefref.first_different_nybble(&node), None);
438 }
440 }
439
441
440 #[test]
442 #[test]
441 fn test_first_different_nybble_odd_prefix() {
443 fn test_first_different_nybble_odd_prefix() {
442 let prefix = NodePrefix::from_hex("12c").unwrap();
444 let prefix = NodePrefix::from_hex("12c").unwrap();
443 let prefref = prefix.borrow();
445 let prefref = prefix.borrow();
444 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
446 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
445 assert_eq!(prefref.first_different_nybble(&node), Some(0));
447 assert_eq!(prefref.first_different_nybble(&node), Some(0));
446 node.data[0] = 0x13;
448 node.data[0] = 0x13;
447 assert_eq!(prefref.first_different_nybble(&node), Some(1));
449 assert_eq!(prefref.first_different_nybble(&node), Some(1));
448 node.data[0] = 0x12;
450 node.data[0] = 0x12;
449 assert_eq!(prefref.first_different_nybble(&node), Some(2));
451 assert_eq!(prefref.first_different_nybble(&node), Some(2));
450 node.data[1] = 0xca;
452 node.data[1] = 0xca;
451 // now it is a prefix
453 // now it is a prefix
452 assert_eq!(prefref.first_different_nybble(&node), None);
454 assert_eq!(prefref.first_different_nybble(&node), None);
453 }
455 }
454 }
456 }
455
457
456 #[cfg(test)]
458 #[cfg(test)]
457 pub use tests::hex_pad_right;
459 pub use tests::hex_pad_right;
General Comments 0
You need to be logged in to leave comments. Login now