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