##// END OF EJS Templates
node: manually implement Debug...
Augie Fackler -
r50070:34decbaf default
parent child Browse files
Show More
@@ -1,421 +1,430 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 crate::errors::HgError;
11 use crate::errors::HgError;
12 use bytes_cast::BytesCast;
12 use bytes_cast::BytesCast;
13 use std::convert::{TryFrom, TryInto};
13 use std::convert::{TryFrom, TryInto};
14 use std::fmt;
14 use std::fmt;
15
15
16 /// The length in bytes of a `Node`
16 /// The length in bytes of a `Node`
17 ///
17 ///
18 /// This constant is meant to ease refactors of this module, and
18 /// This constant is meant to ease refactors of this module, and
19 /// 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
20 /// the same size, should we support several formats concurrently in
20 /// the same size, should we support several formats concurrently in
21 /// the future.
21 /// the future.
22 pub const NODE_BYTES_LENGTH: usize = 20;
22 pub const NODE_BYTES_LENGTH: usize = 20;
23
23
24 /// Id of the null node.
24 /// Id of the null node.
25 ///
25 ///
26 /// Used to indicate the absence of node.
26 /// Used to indicate the absence of node.
27 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];
28
28
29 /// The length in bytes of a `Node`
29 /// The length in bytes of a `Node`
30 ///
30 ///
31 /// see also `NODES_BYTES_LENGTH` about it being private.
31 /// see also `NODES_BYTES_LENGTH` about it being private.
32 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
32 const NODE_NYBBLES_LENGTH: usize = 2 * NODE_BYTES_LENGTH;
33
33
34 /// Default for UI presentation
34 /// Default for UI presentation
35 const SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH: u8 = 12;
35 const SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH: u8 = 12;
36
36
37 /// Private alias for readability and to ease future change
37 /// Private alias for readability and to ease future change
38 type NodeData = [u8; NODE_BYTES_LENGTH];
38 type NodeData = [u8; NODE_BYTES_LENGTH];
39
39
40 /// Binary revision SHA
40 /// Binary revision SHA
41 ///
41 ///
42 /// ## Future changes of hash size
42 /// ## Future changes of hash size
43 ///
43 ///
44 /// To accomodate future changes of hash size, Rust callers
44 /// To accomodate future changes of hash size, Rust callers
45 /// should use the conversion methods at the boundaries (FFI, actual
45 /// should use the conversion methods at the boundaries (FFI, actual
46 /// computation of hashes and I/O) only, and only if required.
46 /// computation of hashes and I/O) only, and only if required.
47 ///
47 ///
48 /// All other callers outside of unit tests should just handle `Node` values
48 /// All other callers outside of unit tests should just handle `Node` values
49 /// and never make any assumption on the actual length, using [`nybbles_len`]
49 /// and never make any assumption on the actual length, using [`nybbles_len`]
50 /// if they need a loop boundary.
50 /// if they need a loop boundary.
51 ///
51 ///
52 /// All methods that create a `Node` either take a type that enforces
52 /// All methods that create a `Node` either take a type that enforces
53 /// the size or return an error at runtime.
53 /// the size or return an error at runtime.
54 ///
54 ///
55 /// [`nybbles_len`]: #method.nybbles_len
55 /// [`nybbles_len`]: #method.nybbles_len
56 #[derive(Copy, Clone, Debug, PartialEq, BytesCast, derive_more::From)]
56 #[derive(Copy, Clone, PartialEq, BytesCast, derive_more::From)]
57 #[repr(transparent)]
57 #[repr(transparent)]
58 pub struct Node {
58 pub struct Node {
59 data: NodeData,
59 data: NodeData,
60 }
60 }
61
61
62 impl fmt::Debug for Node {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 let n = format!("{:x?}", self.data);
65 // We're using debug_tuple because it makes the output a little
66 // more compact without losing data.
67 f.debug_tuple("Node").field(&n).finish()
68 }
69 }
70
62 /// The node value for NULL_REVISION
71 /// The node value for NULL_REVISION
63 pub const NULL_NODE: Node = Node {
72 pub const NULL_NODE: Node = Node {
64 data: [0; NODE_BYTES_LENGTH],
73 data: [0; NODE_BYTES_LENGTH],
65 };
74 };
66
75
67 /// Return an error if the slice has an unexpected length
76 /// Return an error if the slice has an unexpected length
68 impl<'a> TryFrom<&'a [u8]> for &'a Node {
77 impl<'a> TryFrom<&'a [u8]> for &'a Node {
69 type Error = ();
78 type Error = ();
70
79
71 #[inline]
80 #[inline]
72 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
81 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
73 match Node::from_bytes(bytes) {
82 match Node::from_bytes(bytes) {
74 Ok((node, rest)) if rest.is_empty() => Ok(node),
83 Ok((node, rest)) if rest.is_empty() => Ok(node),
75 _ => Err(()),
84 _ => Err(()),
76 }
85 }
77 }
86 }
78 }
87 }
79
88
80 /// Return an error if the slice has an unexpected length
89 /// Return an error if the slice has an unexpected length
81 impl TryFrom<&'_ [u8]> for Node {
90 impl TryFrom<&'_ [u8]> for Node {
82 type Error = std::array::TryFromSliceError;
91 type Error = std::array::TryFromSliceError;
83
92
84 #[inline]
93 #[inline]
85 fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
94 fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
86 let data = bytes.try_into()?;
95 let data = bytes.try_into()?;
87 Ok(Self { data })
96 Ok(Self { data })
88 }
97 }
89 }
98 }
90
99
91 impl From<&'_ NodeData> for Node {
100 impl From<&'_ NodeData> for Node {
92 #[inline]
101 #[inline]
93 fn from(data: &'_ NodeData) -> Self {
102 fn from(data: &'_ NodeData) -> Self {
94 Self { data: *data }
103 Self { data: *data }
95 }
104 }
96 }
105 }
97
106
98 impl fmt::LowerHex for Node {
107 impl fmt::LowerHex for Node {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 for &byte in &self.data {
109 for &byte in &self.data {
101 write!(f, "{:02x}", byte)?
110 write!(f, "{:02x}", byte)?
102 }
111 }
103 Ok(())
112 Ok(())
104 }
113 }
105 }
114 }
106
115
107 #[derive(Debug)]
116 #[derive(Debug)]
108 pub struct FromHexError;
117 pub struct FromHexError;
109
118
110 /// Low level utility function, also for prefixes
119 /// Low level utility function, also for prefixes
111 fn get_nybble(s: &[u8], i: usize) -> u8 {
120 fn get_nybble(s: &[u8], i: usize) -> u8 {
112 if i % 2 == 0 {
121 if i % 2 == 0 {
113 s[i / 2] >> 4
122 s[i / 2] >> 4
114 } else {
123 } else {
115 s[i / 2] & 0x0f
124 s[i / 2] & 0x0f
116 }
125 }
117 }
126 }
118
127
119 impl Node {
128 impl Node {
120 /// Retrieve the `i`th half-byte of the binary data.
129 /// Retrieve the `i`th half-byte of the binary data.
121 ///
130 ///
122 /// This is also the `i`th hexadecimal digit in numeric form,
131 /// This is also the `i`th hexadecimal digit in numeric form,
123 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
132 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
124 pub fn get_nybble(&self, i: usize) -> u8 {
133 pub fn get_nybble(&self, i: usize) -> u8 {
125 get_nybble(&self.data, i)
134 get_nybble(&self.data, i)
126 }
135 }
127
136
128 /// Length of the data, in nybbles
137 /// Length of the data, in nybbles
129 pub fn nybbles_len(&self) -> usize {
138 pub fn nybbles_len(&self) -> usize {
130 // public exposure as an instance method only, so that we can
139 // public exposure as an instance method only, so that we can
131 // easily support several sizes of hashes if needed in the future.
140 // easily support several sizes of hashes if needed in the future.
132 NODE_NYBBLES_LENGTH
141 NODE_NYBBLES_LENGTH
133 }
142 }
134
143
135 /// Convert from hexadecimal string representation
144 /// Convert from hexadecimal string representation
136 ///
145 ///
137 /// Exact length is required.
146 /// Exact length is required.
138 ///
147 ///
139 /// To be used in FFI and I/O only, in order to facilitate future
148 /// To be used in FFI and I/O only, in order to facilitate future
140 /// changes of hash format.
149 /// changes of hash format.
141 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
150 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, FromHexError> {
142 let prefix = NodePrefix::from_hex(hex)?;
151 let prefix = NodePrefix::from_hex(hex)?;
143 if prefix.nybbles_len() == NODE_NYBBLES_LENGTH {
152 if prefix.nybbles_len() == NODE_NYBBLES_LENGTH {
144 Ok(Self { data: prefix.data })
153 Ok(Self { data: prefix.data })
145 } else {
154 } else {
146 Err(FromHexError)
155 Err(FromHexError)
147 }
156 }
148 }
157 }
149
158
150 /// `from_hex`, but for input from an internal file of the repository such
159 /// `from_hex`, but for input from an internal file of the repository such
151 /// as a changelog or manifest entry.
160 /// as a changelog or manifest entry.
152 ///
161 ///
153 /// An error is treated as repository corruption.
162 /// An error is treated as repository corruption.
154 pub fn from_hex_for_repo(hex: impl AsRef<[u8]>) -> Result<Node, HgError> {
163 pub fn from_hex_for_repo(hex: impl AsRef<[u8]>) -> Result<Node, HgError> {
155 Self::from_hex(hex.as_ref()).map_err(|FromHexError| {
164 Self::from_hex(hex.as_ref()).map_err(|FromHexError| {
156 HgError::CorruptedRepository(format!(
165 HgError::CorruptedRepository(format!(
157 "Expected a full hexadecimal node ID, found {}",
166 "Expected a full hexadecimal node ID, found {}",
158 String::from_utf8_lossy(hex.as_ref())
167 String::from_utf8_lossy(hex.as_ref())
159 ))
168 ))
160 })
169 })
161 }
170 }
162
171
163 /// Provide access to binary data
172 /// Provide access to binary data
164 ///
173 ///
165 /// This is needed by FFI layers, for instance to return expected
174 /// This is needed by FFI layers, for instance to return expected
166 /// binary values to Python.
175 /// binary values to Python.
167 pub fn as_bytes(&self) -> &[u8] {
176 pub fn as_bytes(&self) -> &[u8] {
168 &self.data
177 &self.data
169 }
178 }
170
179
171 pub fn short(&self) -> NodePrefix {
180 pub fn short(&self) -> NodePrefix {
172 NodePrefix {
181 NodePrefix {
173 nybbles_len: SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH,
182 nybbles_len: SHORT_PREFIX_DEFAULT_NYBBLES_LENGTH,
174 data: self.data,
183 data: self.data,
175 }
184 }
176 }
185 }
177
186
178 pub fn pad_to_256_bits(&self) -> [u8; 32] {
187 pub fn pad_to_256_bits(&self) -> [u8; 32] {
179 let mut bits = [0; 32];
188 let mut bits = [0; 32];
180 bits[..NODE_BYTES_LENGTH].copy_from_slice(&self.data);
189 bits[..NODE_BYTES_LENGTH].copy_from_slice(&self.data);
181 bits
190 bits
182 }
191 }
183 }
192 }
184
193
185 /// The beginning of a binary revision SHA.
194 /// The beginning of a binary revision SHA.
186 ///
195 ///
187 /// Since it can potentially come from an hexadecimal representation with
196 /// Since it can potentially come from an hexadecimal representation with
188 /// odd length, it needs to carry around whether the last 4 bits are relevant
197 /// odd length, it needs to carry around whether the last 4 bits are relevant
189 /// or not.
198 /// or not.
190 #[derive(Debug, PartialEq, Copy, Clone)]
199 #[derive(Debug, PartialEq, Copy, Clone)]
191 pub struct NodePrefix {
200 pub struct NodePrefix {
192 /// In `1..=NODE_NYBBLES_LENGTH`
201 /// In `1..=NODE_NYBBLES_LENGTH`
193 nybbles_len: u8,
202 nybbles_len: u8,
194 /// The first `4 * length_in_nybbles` bits are used (considering bits
203 /// The first `4 * length_in_nybbles` bits are used (considering bits
195 /// within a bytes in big-endian: most significant first), the rest
204 /// within a bytes in big-endian: most significant first), the rest
196 /// are zero.
205 /// are zero.
197 data: NodeData,
206 data: NodeData,
198 }
207 }
199
208
200 impl NodePrefix {
209 impl NodePrefix {
201 /// Convert from hexadecimal string representation
210 /// Convert from hexadecimal string representation
202 ///
211 ///
203 /// Similarly to `hex::decode`, can be used with Unicode string types
212 /// Similarly to `hex::decode`, can be used with Unicode string types
204 /// (`String`, `&str`) as well as bytes.
213 /// (`String`, `&str`) as well as bytes.
205 ///
214 ///
206 /// To be used in FFI and I/O only, in order to facilitate future
215 /// To be used in FFI and I/O only, in order to facilitate future
207 /// changes of hash format.
216 /// changes of hash format.
208 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
217 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, FromHexError> {
209 let hex = hex.as_ref();
218 let hex = hex.as_ref();
210 let len = hex.len();
219 let len = hex.len();
211 if len > NODE_NYBBLES_LENGTH || len == 0 {
220 if len > NODE_NYBBLES_LENGTH || len == 0 {
212 return Err(FromHexError);
221 return Err(FromHexError);
213 }
222 }
214
223
215 let mut data = [0; NODE_BYTES_LENGTH];
224 let mut data = [0; NODE_BYTES_LENGTH];
216 let mut nybbles_len = 0;
225 let mut nybbles_len = 0;
217 for &ascii_byte in hex {
226 for &ascii_byte in hex {
218 let nybble = match char::from(ascii_byte).to_digit(16) {
227 let nybble = match char::from(ascii_byte).to_digit(16) {
219 Some(digit) => digit as u8,
228 Some(digit) => digit as u8,
220 None => return Err(FromHexError),
229 None => return Err(FromHexError),
221 };
230 };
222 // Fill in the upper half of a byte first, then the lower half.
231 // Fill in the upper half of a byte first, then the lower half.
223 let shift = if nybbles_len % 2 == 0 { 4 } else { 0 };
232 let shift = if nybbles_len % 2 == 0 { 4 } else { 0 };
224 data[nybbles_len as usize / 2] |= nybble << shift;
233 data[nybbles_len as usize / 2] |= nybble << shift;
225 nybbles_len += 1;
234 nybbles_len += 1;
226 }
235 }
227 Ok(Self { data, nybbles_len })
236 Ok(Self { data, nybbles_len })
228 }
237 }
229
238
230 pub fn nybbles_len(&self) -> usize {
239 pub fn nybbles_len(&self) -> usize {
231 self.nybbles_len as _
240 self.nybbles_len as _
232 }
241 }
233
242
234 pub fn is_prefix_of(&self, node: &Node) -> bool {
243 pub fn is_prefix_of(&self, node: &Node) -> bool {
235 let full_bytes = self.nybbles_len() / 2;
244 let full_bytes = self.nybbles_len() / 2;
236 if self.data[..full_bytes] != node.data[..full_bytes] {
245 if self.data[..full_bytes] != node.data[..full_bytes] {
237 return false;
246 return false;
238 }
247 }
239 if self.nybbles_len() % 2 == 0 {
248 if self.nybbles_len() % 2 == 0 {
240 return true;
249 return true;
241 }
250 }
242 let last = self.nybbles_len() - 1;
251 let last = self.nybbles_len() - 1;
243 self.get_nybble(last) == node.get_nybble(last)
252 self.get_nybble(last) == node.get_nybble(last)
244 }
253 }
245
254
246 /// Retrieve the `i`th half-byte from the prefix.
255 /// Retrieve the `i`th half-byte from the prefix.
247 ///
256 ///
248 /// This is also the `i`th hexadecimal digit in numeric form,
257 /// This is also the `i`th hexadecimal digit in numeric form,
249 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
258 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
250 pub fn get_nybble(&self, i: usize) -> u8 {
259 pub fn get_nybble(&self, i: usize) -> u8 {
251 assert!(i < self.nybbles_len());
260 assert!(i < self.nybbles_len());
252 get_nybble(&self.data, i)
261 get_nybble(&self.data, i)
253 }
262 }
254
263
255 fn iter_nybbles(&self) -> impl Iterator<Item = u8> + '_ {
264 fn iter_nybbles(&self) -> impl Iterator<Item = u8> + '_ {
256 (0..self.nybbles_len()).map(move |i| get_nybble(&self.data, i))
265 (0..self.nybbles_len()).map(move |i| get_nybble(&self.data, i))
257 }
266 }
258
267
259 /// Return the index first nybble that's different from `node`
268 /// Return the index first nybble that's different from `node`
260 ///
269 ///
261 /// If the return value is `None` that means that `self` is
270 /// If the return value is `None` that means that `self` is
262 /// a prefix of `node`, but the current method is a bit slower
271 /// a prefix of `node`, but the current method is a bit slower
263 /// than `is_prefix_of`.
272 /// than `is_prefix_of`.
264 ///
273 ///
265 /// Returned index is as in `get_nybble`, i.e., starting at 0.
274 /// Returned index is as in `get_nybble`, i.e., starting at 0.
266 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
275 pub fn first_different_nybble(&self, node: &Node) -> Option<usize> {
267 self.iter_nybbles()
276 self.iter_nybbles()
268 .zip(NodePrefix::from(*node).iter_nybbles())
277 .zip(NodePrefix::from(*node).iter_nybbles())
269 .position(|(a, b)| a != b)
278 .position(|(a, b)| a != b)
270 }
279 }
271 }
280 }
272
281
273 impl fmt::LowerHex for NodePrefix {
282 impl fmt::LowerHex for NodePrefix {
274 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275 let full_bytes = self.nybbles_len() / 2;
284 let full_bytes = self.nybbles_len() / 2;
276 for &byte in &self.data[..full_bytes] {
285 for &byte in &self.data[..full_bytes] {
277 write!(f, "{:02x}", byte)?
286 write!(f, "{:02x}", byte)?
278 }
287 }
279 if self.nybbles_len() % 2 == 1 {
288 if self.nybbles_len() % 2 == 1 {
280 let last = self.nybbles_len() - 1;
289 let last = self.nybbles_len() - 1;
281 write!(f, "{:x}", self.get_nybble(last))?
290 write!(f, "{:x}", self.get_nybble(last))?
282 }
291 }
283 Ok(())
292 Ok(())
284 }
293 }
285 }
294 }
286
295
287 /// A shortcut for full `Node` references
296 /// A shortcut for full `Node` references
288 impl From<&'_ Node> for NodePrefix {
297 impl From<&'_ Node> for NodePrefix {
289 fn from(node: &'_ Node) -> Self {
298 fn from(node: &'_ Node) -> Self {
290 NodePrefix {
299 NodePrefix {
291 nybbles_len: node.nybbles_len() as _,
300 nybbles_len: node.nybbles_len() as _,
292 data: node.data,
301 data: node.data,
293 }
302 }
294 }
303 }
295 }
304 }
296
305
297 /// A shortcut for full `Node` references
306 /// A shortcut for full `Node` references
298 impl From<Node> for NodePrefix {
307 impl From<Node> for NodePrefix {
299 fn from(node: Node) -> Self {
308 fn from(node: Node) -> Self {
300 NodePrefix {
309 NodePrefix {
301 nybbles_len: node.nybbles_len() as _,
310 nybbles_len: node.nybbles_len() as _,
302 data: node.data,
311 data: node.data,
303 }
312 }
304 }
313 }
305 }
314 }
306
315
307 impl PartialEq<Node> for NodePrefix {
316 impl PartialEq<Node> for NodePrefix {
308 fn eq(&self, other: &Node) -> bool {
317 fn eq(&self, other: &Node) -> bool {
309 Self::from(*other) == *self
318 Self::from(*other) == *self
310 }
319 }
311 }
320 }
312
321
313 #[cfg(test)]
322 #[cfg(test)]
314 mod tests {
323 mod tests {
315 use super::*;
324 use super::*;
316
325
317 const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef";
326 const SAMPLE_NODE_HEX: &str = "0123456789abcdeffedcba9876543210deadbeef";
318 const SAMPLE_NODE: Node = Node {
327 const SAMPLE_NODE: Node = Node {
319 data: [
328 data: [
320 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
329 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba,
321 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
330 0x98, 0x76, 0x54, 0x32, 0x10, 0xde, 0xad, 0xbe, 0xef,
322 ],
331 ],
323 };
332 };
324
333
325 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
334 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH`
326 /// The padding is made with zeros.
335 /// The padding is made with zeros.
327 pub fn hex_pad_right(hex: &str) -> String {
336 pub fn hex_pad_right(hex: &str) -> String {
328 let mut res = hex.to_string();
337 let mut res = hex.to_string();
329 while res.len() < NODE_NYBBLES_LENGTH {
338 while res.len() < NODE_NYBBLES_LENGTH {
330 res.push('0');
339 res.push('0');
331 }
340 }
332 res
341 res
333 }
342 }
334
343
335 #[test]
344 #[test]
336 fn test_node_from_hex() {
345 fn test_node_from_hex() {
337 let not_hex = "012... oops";
346 let not_hex = "012... oops";
338 let too_short = "0123";
347 let too_short = "0123";
339 let too_long = format!("{}0", SAMPLE_NODE_HEX);
348 let too_long = format!("{}0", SAMPLE_NODE_HEX);
340 assert_eq!(Node::from_hex(SAMPLE_NODE_HEX).unwrap(), SAMPLE_NODE);
349 assert_eq!(Node::from_hex(SAMPLE_NODE_HEX).unwrap(), SAMPLE_NODE);
341 assert!(Node::from_hex(not_hex).is_err());
350 assert!(Node::from_hex(not_hex).is_err());
342 assert!(Node::from_hex(too_short).is_err());
351 assert!(Node::from_hex(too_short).is_err());
343 assert!(Node::from_hex(&too_long).is_err());
352 assert!(Node::from_hex(&too_long).is_err());
344 }
353 }
345
354
346 #[test]
355 #[test]
347 fn test_node_encode_hex() {
356 fn test_node_encode_hex() {
348 assert_eq!(format!("{:x}", SAMPLE_NODE), SAMPLE_NODE_HEX);
357 assert_eq!(format!("{:x}", SAMPLE_NODE), SAMPLE_NODE_HEX);
349 }
358 }
350
359
351 #[test]
360 #[test]
352 fn test_prefix_from_to_hex() -> Result<(), FromHexError> {
361 fn test_prefix_from_to_hex() -> Result<(), FromHexError> {
353 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1")?), "0e1");
362 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1")?), "0e1");
354 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1a")?), "0e1a");
363 assert_eq!(format!("{:x}", NodePrefix::from_hex("0e1a")?), "0e1a");
355 assert_eq!(
364 assert_eq!(
356 format!("{:x}", NodePrefix::from_hex(SAMPLE_NODE_HEX)?),
365 format!("{:x}", NodePrefix::from_hex(SAMPLE_NODE_HEX)?),
357 SAMPLE_NODE_HEX
366 SAMPLE_NODE_HEX
358 );
367 );
359 Ok(())
368 Ok(())
360 }
369 }
361
370
362 #[test]
371 #[test]
363 fn test_prefix_from_hex_errors() {
372 fn test_prefix_from_hex_errors() {
364 assert!(NodePrefix::from_hex("testgr").is_err());
373 assert!(NodePrefix::from_hex("testgr").is_err());
365 let mut long = format!("{:x}", NULL_NODE);
374 let mut long = format!("{:x}", NULL_NODE);
366 long.push('c');
375 long.push('c');
367 assert!(NodePrefix::from_hex(&long).is_err())
376 assert!(NodePrefix::from_hex(&long).is_err())
368 }
377 }
369
378
370 #[test]
379 #[test]
371 fn test_is_prefix_of() -> Result<(), FromHexError> {
380 fn test_is_prefix_of() -> Result<(), FromHexError> {
372 let mut node_data = [0; NODE_BYTES_LENGTH];
381 let mut node_data = [0; NODE_BYTES_LENGTH];
373 node_data[0] = 0x12;
382 node_data[0] = 0x12;
374 node_data[1] = 0xca;
383 node_data[1] = 0xca;
375 let node = Node::from(node_data);
384 let node = Node::from(node_data);
376 assert!(NodePrefix::from_hex("12")?.is_prefix_of(&node));
385 assert!(NodePrefix::from_hex("12")?.is_prefix_of(&node));
377 assert!(!NodePrefix::from_hex("1a")?.is_prefix_of(&node));
386 assert!(!NodePrefix::from_hex("1a")?.is_prefix_of(&node));
378 assert!(NodePrefix::from_hex("12c")?.is_prefix_of(&node));
387 assert!(NodePrefix::from_hex("12c")?.is_prefix_of(&node));
379 assert!(!NodePrefix::from_hex("12d")?.is_prefix_of(&node));
388 assert!(!NodePrefix::from_hex("12d")?.is_prefix_of(&node));
380 Ok(())
389 Ok(())
381 }
390 }
382
391
383 #[test]
392 #[test]
384 fn test_get_nybble() -> Result<(), FromHexError> {
393 fn test_get_nybble() -> Result<(), FromHexError> {
385 let prefix = NodePrefix::from_hex("dead6789cafe")?;
394 let prefix = NodePrefix::from_hex("dead6789cafe")?;
386 assert_eq!(prefix.get_nybble(0), 13);
395 assert_eq!(prefix.get_nybble(0), 13);
387 assert_eq!(prefix.get_nybble(7), 9);
396 assert_eq!(prefix.get_nybble(7), 9);
388 Ok(())
397 Ok(())
389 }
398 }
390
399
391 #[test]
400 #[test]
392 fn test_first_different_nybble_even_prefix() {
401 fn test_first_different_nybble_even_prefix() {
393 let prefix = NodePrefix::from_hex("12ca").unwrap();
402 let prefix = NodePrefix::from_hex("12ca").unwrap();
394 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
403 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
395 assert_eq!(prefix.first_different_nybble(&node), Some(0));
404 assert_eq!(prefix.first_different_nybble(&node), Some(0));
396 node.data[0] = 0x13;
405 node.data[0] = 0x13;
397 assert_eq!(prefix.first_different_nybble(&node), Some(1));
406 assert_eq!(prefix.first_different_nybble(&node), Some(1));
398 node.data[0] = 0x12;
407 node.data[0] = 0x12;
399 assert_eq!(prefix.first_different_nybble(&node), Some(2));
408 assert_eq!(prefix.first_different_nybble(&node), Some(2));
400 node.data[1] = 0xca;
409 node.data[1] = 0xca;
401 // now it is a prefix
410 // now it is a prefix
402 assert_eq!(prefix.first_different_nybble(&node), None);
411 assert_eq!(prefix.first_different_nybble(&node), None);
403 }
412 }
404
413
405 #[test]
414 #[test]
406 fn test_first_different_nybble_odd_prefix() {
415 fn test_first_different_nybble_odd_prefix() {
407 let prefix = NodePrefix::from_hex("12c").unwrap();
416 let prefix = NodePrefix::from_hex("12c").unwrap();
408 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
417 let mut node = Node::from([0; NODE_BYTES_LENGTH]);
409 assert_eq!(prefix.first_different_nybble(&node), Some(0));
418 assert_eq!(prefix.first_different_nybble(&node), Some(0));
410 node.data[0] = 0x13;
419 node.data[0] = 0x13;
411 assert_eq!(prefix.first_different_nybble(&node), Some(1));
420 assert_eq!(prefix.first_different_nybble(&node), Some(1));
412 node.data[0] = 0x12;
421 node.data[0] = 0x12;
413 assert_eq!(prefix.first_different_nybble(&node), Some(2));
422 assert_eq!(prefix.first_different_nybble(&node), Some(2));
414 node.data[1] = 0xca;
423 node.data[1] = 0xca;
415 // now it is a prefix
424 // now it is a prefix
416 assert_eq!(prefix.first_different_nybble(&node), None);
425 assert_eq!(prefix.first_different_nybble(&node), None);
417 }
426 }
418 }
427 }
419
428
420 #[cfg(test)]
429 #[cfg(test)]
421 pub use tests::hex_pad_right;
430 pub use tests::hex_pad_right;
General Comments 0
You need to be logged in to leave comments. Login now