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