Skip to content

Commit 5ffabb4

Browse files
authored
Merge pull request #2282 from qingshi163/dev2
Reimplement bytes fromhex
2 parents b2c0a69 + d14b0cf commit 5ffabb4

2 files changed

Lines changed: 40 additions & 27 deletions

File tree

Lib/test/test_bytes.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,6 @@ def test_contains(self):
377377
self.assertNotIn(f(b"dab"), b)
378378
self.assertNotIn(f(b"abd"), b)
379379

380-
# TODO: RUSTPYTHON
381-
@unittest.expectedFailure
382380
def test_fromhex(self):
383381
self.assertRaises(TypeError, self.type2test.fromhex)
384382
self.assertRaises(TypeError, self.type2test.fromhex, 1)

vm/src/bytesinner.rs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -557,35 +557,46 @@ impl PyBytesInner {
557557
}
558558

559559
pub fn fromhex(string: &str, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
560-
// first check for invalid character
561-
for (i, c) in string.char_indices() {
562-
if !c.is_digit(16) && !c.is_whitespace() {
563-
return Err(vm.new_value_error(format!(
564-
"non-hexadecimal number found in fromhex() arg at position {}",
565-
i
566-
)));
560+
let mut iter = string.bytes().enumerate();
561+
let mut bytes: Vec<u8> = Vec::with_capacity(string.len() / 2);
562+
let i = loop {
563+
let (i, b) = match iter.next() {
564+
Some(val) => val,
565+
None => {
566+
return Ok(bytes);
567+
}
568+
};
569+
570+
if is_py_ascii_whitespace(b) {
571+
continue;
567572
}
568-
}
569573

570-
// strip white spaces
571-
let stripped = string.split_whitespace().collect::<String>();
574+
let top = match b {
575+
b'0'..=b'9' => b - b'0',
576+
b'a'..=b'f' => 10 + b - b'a',
577+
b'A'..=b'F' => 10 + b - b'A',
578+
_ => break i,
579+
};
572580

573-
// Hex is evaluated on 2 digits
574-
if stripped.len() % 2 != 0 {
575-
return Err(vm.new_value_error(format!(
576-
"non-hexadecimal number found in fromhex() arg at position {}",
577-
stripped.len() - 1
578-
)));
579-
}
581+
let (i, b) = match iter.next() {
582+
Some(val) => val,
583+
None => break i + 1,
584+
};
580585

581-
// parse even string
582-
Ok(stripped
583-
.chars()
584-
.collect::<Vec<char>>()
585-
.chunks(2)
586-
.map(|x| x.to_vec().iter().collect::<String>())
587-
.map(|x| u8::from_str_radix(&x, 16).unwrap())
588-
.collect::<Vec<u8>>())
586+
let bot = match b {
587+
b'0'..=b'9' => b - b'0',
588+
b'a'..=b'f' => 10 + b - b'a',
589+
b'A'..=b'F' => 10 + b - b'A',
590+
_ => break i,
591+
};
592+
593+
bytes.push((top << 4) + bot);
594+
};
595+
596+
Err(vm.new_value_error(format!(
597+
"non-hexadecimal number found in fromhex() arg at position {}",
598+
i
599+
)))
589600
}
590601

591602
#[inline]
@@ -1330,3 +1341,7 @@ pub fn bytes_to_hex(
13301341
Ok(hex_impl_no_sep(bytes))
13311342
}
13321343
}
1344+
1345+
const fn is_py_ascii_whitespace(b: u8) -> bool {
1346+
matches!(b, b'\t' | b'\n' | b'\x0C' | b'\r' | b' ' | b'\x0B')
1347+
}

0 commit comments

Comments
 (0)