@@ -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