From 21a9e05abcc6263c812852dc139f697de28b4c96 Mon Sep 17 00:00:00 2001 From: Kangzhi Shi Date: Sun, 11 Oct 2020 15:14:25 +0200 Subject: [PATCH] Reimplement bytes fromhex --- Lib/test/test_bytes.py | 2 -- vm/src/bytesinner.rs | 65 ++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 526b62390..0affea87c 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -377,8 +377,6 @@ class BaseBytesTest: self.assertNotIn(f(b"dab"), b) self.assertNotIn(f(b"abd"), b) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_fromhex(self): self.assertRaises(TypeError, self.type2test.fromhex) self.assertRaises(TypeError, self.type2test.fromhex, 1) diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index c6875776f..9a368c0ae 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -557,35 +557,46 @@ impl PyBytesInner { } pub fn fromhex(string: &str, vm: &VirtualMachine) -> PyResult> { - // first check for invalid character - for (i, c) in string.char_indices() { - if !c.is_digit(16) && !c.is_whitespace() { - return Err(vm.new_value_error(format!( - "non-hexadecimal number found in fromhex() arg at position {}", - i - ))); + let mut iter = string.bytes().enumerate(); + let mut bytes: Vec = Vec::with_capacity(string.len() / 2); + let i = loop { + let (i, b) = match iter.next() { + Some(val) => val, + None => { + return Ok(bytes); + } + }; + + if is_py_ascii_whitespace(b) { + continue; } - } - // strip white spaces - let stripped = string.split_whitespace().collect::(); + let top = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => 10 + b - b'a', + b'A'..=b'F' => 10 + b - b'A', + _ => break i, + }; - // Hex is evaluated on 2 digits - if stripped.len() % 2 != 0 { - return Err(vm.new_value_error(format!( - "non-hexadecimal number found in fromhex() arg at position {}", - stripped.len() - 1 - ))); - } + let (i, b) = match iter.next() { + Some(val) => val, + None => break i, + }; - // parse even string - Ok(stripped - .chars() - .collect::>() - .chunks(2) - .map(|x| x.to_vec().iter().collect::()) - .map(|x| u8::from_str_radix(&x, 16).unwrap()) - .collect::>()) + let bot = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => 10 + b - b'a', + b'A'..=b'F' => 10 + b - b'A', + _ => break i, + }; + + bytes.push((top << 4) + bot); + }; + + Err(vm.new_value_error(format!( + "non-hexadecimal number found in fromhex() arg at position {}", + i + ))) } #[inline] @@ -1330,3 +1341,7 @@ pub fn bytes_to_hex( Ok(hex_impl_no_sep(bytes)) } } + +const fn is_py_ascii_whitespace(b: u8) -> bool { + matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ' | b'\x0B') +}