##// END OF EJS Templates
rust-node: handling binary Node prefix...
Georges Racinet -
r44643:9896a8d0 default
parent child Browse files
Show More
@@ -7,7 +7,7 b''
7
7
8 pub mod node;
8 pub mod node;
9 pub mod nodemap;
9 pub mod nodemap;
10 pub use node::{Node, NodeError};
10 pub use node::{Node, NodeError, NodePrefix, NodePrefixRef};
11
11
12 /// Mercurial revision numbers
12 /// Mercurial revision numbers
13 ///
13 ///
@@ -62,6 +62,7 b' impl From<NodeData> for Node {'
62 #[derive(Debug, PartialEq)]
62 #[derive(Debug, PartialEq)]
63 pub enum NodeError {
63 pub enum NodeError {
64 ExactLengthRequired(usize, String),
64 ExactLengthRequired(usize, String),
65 PrefixTooLong(String),
65 HexError(FromHexError, String),
66 HexError(FromHexError, String),
66 }
67 }
67
68
@@ -119,17 +120,119 b' impl Node {'
119 }
120 }
120 }
121 }
121
122
122 impl From<(FromHexError, &str)> for NodeError {
123 impl<T: AsRef<str>> From<(FromHexError, T)> for NodeError {
123 fn from(err_offender: (FromHexError, &str)) -> Self {
124 fn from(err_offender: (FromHexError, T)) -> Self {
124 let (err, offender) = err_offender;
125 let (err, offender) = err_offender;
125 match err {
126 match err {
126 FromHexError::InvalidStringLength => {
127 FromHexError::InvalidStringLength => {
127 NodeError::ExactLengthRequired(
128 NodeError::ExactLengthRequired(
128 NODE_NYBBLES_LENGTH,
129 NODE_NYBBLES_LENGTH,
129 offender.to_string(),
130 offender.as_ref().to_owned(),
130 )
131 )
131 }
132 }
132 _ => NodeError::HexError(err, offender.to_string()),
133 _ => NodeError::HexError(err, offender.as_ref().to_owned()),
134 }
135 }
136 }
137
138 /// The beginning of a binary revision SHA.
139 ///
140 /// Since it can potentially come from an hexadecimal representation with
141 /// odd length, it needs to carry around whether the last 4 bits are relevant
142 /// or not.
143 #[derive(Debug, PartialEq)]
144 pub struct NodePrefix {
145 buf: Vec<u8>,
146 is_odd: bool,
147 }
148
149 impl NodePrefix {
150 /// Convert from hexadecimal string representation
151 ///
152 /// Similarly to `hex::decode`, can be used with Unicode string types
153 /// (`String`, `&str`) as well as bytes.
154 ///
155 /// To be used in FFI and I/O only, in order to facilitate future
156 /// changes of hash format.
157 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, NodeError> {
158 let hex = hex.as_ref();
159 let len = hex.len();
160 if len > NODE_NYBBLES_LENGTH {
161 return Err(NodeError::PrefixTooLong(
162 String::from_utf8_lossy(hex).to_owned().to_string(),
163 ));
164 }
165
166 let is_odd = len % 2 == 1;
167 let even_part = if is_odd { &hex[..len - 1] } else { hex };
168 let mut buf: Vec<u8> = Vec::from_hex(&even_part)
169 .map_err(|e| (e, String::from_utf8_lossy(hex)))?;
170
171 if is_odd {
172 let latest_char = char::from(hex[len - 1]);
173 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| {
174 (
175 FromHexError::InvalidHexCharacter {
176 c: latest_char,
177 index: len - 1,
178 },
179 String::from_utf8_lossy(hex),
180 )
181 })? as u8;
182 buf.push(latest_nybble << 4);
183 }
184 Ok(NodePrefix { buf, is_odd })
185 }
186
187 pub fn borrow(&self) -> NodePrefixRef {
188 NodePrefixRef {
189 buf: &self.buf,
190 is_odd: self.is_odd,
191 }
192 }
193 }
194
195 #[derive(Clone, Debug, PartialEq)]
196 pub struct NodePrefixRef<'a> {
197 buf: &'a [u8],
198 is_odd: bool,
199 }
200
201 impl<'a> NodePrefixRef<'a> {
202 pub fn len(&self) -> usize {
203 if self.is_odd {
204 self.buf.len() * 2 - 1
205 } else {
206 self.buf.len() * 2
207 }
208 }
209
210 pub fn is_prefix_of(&self, node: &Node) -> bool {
211 if self.is_odd {
212 let buf = self.buf;
213 let last_pos = buf.len() - 1;
214 node.data.starts_with(buf.split_at(last_pos).0)
215 && node.data[last_pos] >> 4 == buf[last_pos] >> 4
216 } else {
217 node.data.starts_with(self.buf)
218 }
219 }
220
221 /// Retrieve the `i`th half-byte from the prefix.
222 ///
223 /// This is also the `i`th hexadecimal digit in numeric form,
224 /// also called a [nybble](https://en.wikipedia.org/wiki/Nibble).
225 pub fn get_nybble(&self, i: usize) -> u8 {
226 get_nybble(self.buf, i)
227 }
228 }
229
230 /// A shortcut for full `Node` references
231 impl<'a> From<&'a Node> for NodePrefixRef<'a> {
232 fn from(node: &'a Node) -> Self {
233 NodePrefixRef {
234 buf: &node.data,
235 is_odd: false,
133 }
236 }
134 }
237 }
135 }
238 }
@@ -188,4 +291,74 b' mod tests {'
188 fn test_node_encode_hex() {
291 fn test_node_encode_hex() {
189 assert_eq!(sample_node().encode_hex(), sample_node_hex());
292 assert_eq!(sample_node().encode_hex(), sample_node_hex());
190 }
293 }
294
295 #[test]
296 fn test_prefix_from_hex() -> Result<(), NodeError> {
297 assert_eq!(
298 NodePrefix::from_hex("0e1")?,
299 NodePrefix {
300 buf: vec![14, 16],
301 is_odd: true
302 }
303 );
304 assert_eq!(
305 NodePrefix::from_hex("0e1a")?,
306 NodePrefix {
307 buf: vec![14, 26],
308 is_odd: false
309 }
310 );
311
312 // checking limit case
313 let node_as_vec = sample_node().data.iter().cloned().collect();
314 assert_eq!(
315 NodePrefix::from_hex(sample_node_hex())?,
316 NodePrefix {
317 buf: node_as_vec,
318 is_odd: false
319 }
320 );
321
322 Ok(())
323 }
324
325 #[test]
326 fn test_prefix_from_hex_errors() {
327 assert_eq!(
328 NodePrefix::from_hex("testgr"),
329 Err(NodeError::HexError(
330 FromHexError::InvalidHexCharacter { c: 't', index: 0 },
331 "testgr".to_string()
332 ))
333 );
334 let mut long = NULL_NODE.encode_hex();
335 long.push('c');
336 match NodePrefix::from_hex(&long)
337 .expect_err("should be refused as too long")
338 {
339 NodeError::PrefixTooLong(s) => assert_eq!(s, long),
340 err => panic!(format!("Should have been TooLong, got {:?}", err)),
341 }
342 }
343
344 #[test]
345 fn test_is_prefix_of() -> Result<(), NodeError> {
346 let mut node_data = [0; NODE_BYTES_LENGTH];
347 node_data[0] = 0x12;
348 node_data[1] = 0xca;
349 let node = Node::from(node_data);
350 assert!(NodePrefix::from_hex("12")?.borrow().is_prefix_of(&node));
351 assert!(!NodePrefix::from_hex("1a")?.borrow().is_prefix_of(&node));
352 assert!(NodePrefix::from_hex("12c")?.borrow().is_prefix_of(&node));
353 assert!(!NodePrefix::from_hex("12d")?.borrow().is_prefix_of(&node));
354 Ok(())
355 }
356
357 #[test]
358 fn test_get_nybble() -> Result<(), NodeError> {
359 let prefix = NodePrefix::from_hex("dead6789cafe")?;
360 assert_eq!(prefix.borrow().get_nybble(0), 13);
361 assert_eq!(prefix.borrow().get_nybble(7), 9);
362 Ok(())
363 }
191 }
364 }
General Comments 0
You need to be logged in to leave comments. Login now