Mercurial > hg
comparison rust/hg-core/src/revlog/node.rs @ 46033:88e741bf2d93
rust: use NodePrefix::from_hex instead of hex::decode directly
This adds support for prefixes with an odd number of hex digits.
Differential Revision: https://phab.mercurial-scm.org/D9490
author | Simon Sapin <simon-commits@exyr.org> |
---|---|
date | Wed, 02 Dec 2020 15:00:49 +0100 |
parents | b0d6309ff50c |
children | cfb6c10c08c2 |
comparison
equal
deleted
inserted
replaced
46032:8d6164098782 | 46033:88e741bf2d93 |
---|---|
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 hex::{self, FromHex, FromHexError}; | 11 use hex::{self, FromHex, FromHexError}; |
12 use std::convert::{TryFrom, TryInto}; | |
12 | 13 |
13 /// The length in bytes of a `Node` | 14 /// The length in bytes of a `Node` |
14 /// | 15 /// |
15 /// This constant is meant to ease refactors of this module, and | 16 /// This constant is meant to ease refactors of this module, and |
16 /// are private so that calling code does not expect all nodes have | 17 /// are private so that calling code does not expect all nodes have |
63 fn from(data: NodeData) -> Node { | 64 fn from(data: NodeData) -> Node { |
64 Node { data } | 65 Node { data } |
65 } | 66 } |
66 } | 67 } |
67 | 68 |
69 /// Return an error if the slice has an unexpected length | |
70 impl<'a> TryFrom<&'a [u8]> for &'a Node { | |
71 type Error = std::array::TryFromSliceError; | |
72 | |
73 #[inline] | |
74 fn try_from(bytes: &'a [u8]) -> Result<&'a Node, Self::Error> { | |
75 let data = bytes.try_into()?; | |
76 // Safety: `#[repr(transparent)]` makes it ok to "wrap" the target | |
77 // of a reference to the type of the single field. | |
78 Ok(unsafe { std::mem::transmute::<&NodeData, &Node>(data) }) | |
79 } | |
80 } | |
81 | |
68 #[derive(Debug, PartialEq)] | 82 #[derive(Debug, PartialEq)] |
69 pub enum NodeError { | 83 pub enum NodeError { |
70 ExactLengthRequired(usize, String), | 84 ExactLengthRequired(usize, String), |
71 PrefixTooLong(String), | 85 PrefixTooLong(String), |
72 HexError(FromHexError, String), | 86 HexError(FromHexError, String), |
101 /// | 115 /// |
102 /// Exact length is required. | 116 /// Exact length is required. |
103 /// | 117 /// |
104 /// To be used in FFI and I/O only, in order to facilitate future | 118 /// To be used in FFI and I/O only, in order to facilitate future |
105 /// changes of hash format. | 119 /// changes of hash format. |
106 pub fn from_hex(hex: &str) -> Result<Node, NodeError> { | 120 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Node, NodeError> { |
107 Ok(NodeData::from_hex(hex) | 121 Ok(NodeData::from_hex(hex.as_ref()) |
108 .map_err(|e| NodeError::from((e, hex)))? | 122 .map_err(|e| NodeError::from((e, hex)))? |
109 .into()) | 123 .into()) |
110 } | 124 } |
111 | 125 |
112 /// Convert to hexadecimal string representation | 126 /// Convert to hexadecimal string representation |
124 pub fn as_bytes(&self) -> &[u8] { | 138 pub fn as_bytes(&self) -> &[u8] { |
125 &self.data | 139 &self.data |
126 } | 140 } |
127 } | 141 } |
128 | 142 |
129 impl<T: AsRef<str>> From<(FromHexError, T)> for NodeError { | 143 impl<T: AsRef<[u8]>> From<(FromHexError, T)> for NodeError { |
130 fn from(err_offender: (FromHexError, T)) -> Self { | 144 fn from(err_offender: (FromHexError, T)) -> Self { |
131 let (err, offender) = err_offender; | 145 let (err, offender) = err_offender; |
146 let offender = String::from_utf8_lossy(offender.as_ref()).into_owned(); | |
132 match err { | 147 match err { |
133 FromHexError::InvalidStringLength => { | 148 FromHexError::InvalidStringLength => { |
134 NodeError::ExactLengthRequired( | 149 NodeError::ExactLengthRequired(NODE_NYBBLES_LENGTH, offender) |
135 NODE_NYBBLES_LENGTH, | |
136 offender.as_ref().to_owned(), | |
137 ) | |
138 } | 150 } |
139 _ => NodeError::HexError(err, offender.as_ref().to_owned()), | 151 _ => NodeError::HexError(err, offender), |
140 } | 152 } |
141 } | 153 } |
142 } | 154 } |
143 | 155 |
144 /// The beginning of a binary revision SHA. | 156 /// The beginning of a binary revision SHA. |
169 )); | 181 )); |
170 } | 182 } |
171 | 183 |
172 let is_odd = len % 2 == 1; | 184 let is_odd = len % 2 == 1; |
173 let even_part = if is_odd { &hex[..len - 1] } else { hex }; | 185 let even_part = if is_odd { &hex[..len - 1] } else { hex }; |
174 let mut buf: Vec<u8> = Vec::from_hex(&even_part) | 186 let mut buf: Vec<u8> = |
175 .map_err(|e| (e, String::from_utf8_lossy(hex)))?; | 187 Vec::from_hex(&even_part).map_err(|e| (e, hex))?; |
176 | 188 |
177 if is_odd { | 189 if is_odd { |
178 let latest_char = char::from(hex[len - 1]); | 190 let latest_char = char::from(hex[len - 1]); |
179 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| { | 191 let latest_nybble = latest_char.to_digit(16).ok_or_else(|| { |
180 ( | 192 ( |
181 FromHexError::InvalidHexCharacter { | 193 FromHexError::InvalidHexCharacter { |
182 c: latest_char, | 194 c: latest_char, |
183 index: len - 1, | 195 index: len - 1, |
184 }, | 196 }, |
185 String::from_utf8_lossy(hex), | 197 hex, |
186 ) | 198 ) |
187 })? as u8; | 199 })? as u8; |
188 buf.push(latest_nybble << 4); | 200 buf.push(latest_nybble << 4); |
189 } | 201 } |
190 Ok(NodePrefix { buf, is_odd }) | 202 Ok(NodePrefix { buf, is_odd }) |
276 is_odd: false, | 288 is_odd: false, |
277 } | 289 } |
278 } | 290 } |
279 } | 291 } |
280 | 292 |
293 impl PartialEq<Node> for NodePrefixRef<'_> { | |
294 fn eq(&self, other: &Node) -> bool { | |
295 !self.is_odd && self.buf == other.data | |
296 } | |
297 } | |
298 | |
281 #[cfg(test)] | 299 #[cfg(test)] |
282 mod tests { | 300 mod tests { |
283 use super::*; | 301 use super::*; |
284 | 302 |
285 fn sample_node() -> Node { | 303 fn sample_node() -> Node { |
290 ]); | 308 ]); |
291 data.into() | 309 data.into() |
292 } | 310 } |
293 | 311 |
294 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH` | 312 /// Pad an hexadecimal string to reach `NODE_NYBBLES_LENGTH` |
295 /// | 313 ///check_hash |
296 /// The padding is made with zeros | 314 /// The padding is made with zeros |
297 pub fn hex_pad_right(hex: &str) -> String { | 315 pub fn hex_pad_right(hex: &str) -> String { |
298 let mut res = hex.to_string(); | 316 let mut res = hex.to_string(); |
299 while res.len() < NODE_NYBBLES_LENGTH { | 317 while res.len() < NODE_NYBBLES_LENGTH { |
300 res.push('0'); | 318 res.push('0'); |