@@ -312,6 +312,16 @@ impl<'a> Tokenizer<'a> {
312312 unsafe { self . input . get_unchecked ( range. start . 0 ..range. end . 0 ) }
313313 }
314314
315+ #[ inline]
316+ pub ( crate ) fn byte_slice ( & self , range : Range < usize > ) -> & ' a [ u8 ] {
317+ & self . input . as_bytes ( ) [ range]
318+ }
319+
320+ #[ inline]
321+ pub ( crate ) fn byte_slice_from ( & self , start : usize ) -> & ' a [ u8 ] {
322+ self . byte_slice ( start..self . position )
323+ }
324+
315325 pub fn current_source_line ( & self ) -> & ' a str {
316326 let current = self . position ( ) ;
317327 let start = self
@@ -365,6 +375,26 @@ impl<'a> Tokenizer<'a> {
365375 self . position += n
366376 }
367377
378+ /// Equivalent to calling advance() for runs of bytes for which `matches` returns true.
379+ /// Returns the byte slice advanced over.
380+ fn advance_while ( & mut self , mut matches : impl FnMut ( u8 ) -> bool ) -> & [ u8 ] {
381+ let start = self . position ;
382+ let mut position = start;
383+
384+ let bytes = & self . input . as_bytes ( ) [ start..] ;
385+ for b in bytes {
386+ if !matches ( * b) {
387+ break ;
388+ }
389+ position += 1 ;
390+ }
391+
392+ // Equivalent to self.position = position, but with advance()'s debug_assert!s
393+ self . advance ( position - self . position ) ;
394+
395+ self . byte_slice_from ( start)
396+ }
397+
368398 // Assumes non-EOF
369399 #[ inline]
370400 fn next_byte_unchecked ( & self ) -> u8 {
@@ -917,15 +947,26 @@ fn consume_ident_like<'a>(tokenizer: &mut Tokenizer<'a>) -> Token<'a> {
917947}
918948
919949fn consume_name < ' a > ( tokenizer : & mut Tokenizer < ' a > ) -> CowRcStr < ' a > {
950+ // These are the overwhelmingly common bytes, that we can just skip over in a tight loop.
951+ static IS_SIMPLE_NAME_BYTE : [ bool ; 256 ] = {
952+ let mut table = [ false ; 256 ] ;
953+ let mut i = 0 ;
954+ while i < 256 {
955+ table[ i as usize ] = matches ! ( i as u8 , b'a' ..=b'z' | b'A' ..=b'Z' | b'0' ..=b'9' | b'_' | b'-' | b'\xC0' ..=b'\xEF' ) ;
956+ i += 1 ;
957+ }
958+ table
959+ } ;
960+
920961 // start_pos is the end of the previous token, therefore at a code point boundary
921962 let start_pos = tokenizer. position ( ) ;
922963 let mut value_bytes;
923964 loop {
965+ tokenizer. advance_while ( |b| IS_SIMPLE_NAME_BYTE [ b as usize ] ) ;
924966 if tokenizer. is_eof ( ) {
925967 return tokenizer. slice_from ( start_pos) . into ( ) ;
926968 }
927969 match_byte ! { tokenizer. next_byte_unchecked( ) ,
928- b'a' ..=b'z' | b'A' ..=b'Z' | b'0' ..=b'9' | b'_' | b'-' => tokenizer. advance( 1 ) ,
929970 b'\\' | b'\0' => {
930971 // * The tokenizer’s input is UTF-8 since it’s `&str`.
931972 // * start_pos is at a code point boundary
@@ -936,21 +977,20 @@ fn consume_name<'a>(tokenizer: &mut Tokenizer<'a>) -> CowRcStr<'a> {
936977 break
937978 }
938979 b'\x80' ..=b'\xBF' => { tokenizer. consume_continuation_byte( ) ; }
939- b'\xC0' ..=b'\xEF' => { tokenizer. advance( 1 ) ; }
940980 b'\xF0' ..=b'\xFF' => { tokenizer. consume_4byte_intro( ) ; }
941981 _b => {
942982 return tokenizer. slice_from( start_pos) . into( ) ;
943983 }
944984 }
945985 }
946986
947- while !tokenizer. is_eof ( ) {
987+ loop {
988+ value_bytes. extend ( tokenizer. advance_while ( |b| IS_SIMPLE_NAME_BYTE [ b as usize ] ) ) ;
989+ if tokenizer. is_eof ( ) {
990+ break ;
991+ }
948992 let b = tokenizer. next_byte_unchecked ( ) ;
949993 match_byte ! { b,
950- b'a' ..=b'z' | b'A' ..=b'Z' | b'0' ..=b'9' | b'_' | b'-' => {
951- tokenizer. advance( 1 ) ;
952- value_bytes. push( b) // ASCII
953- }
954994 b'\\' => {
955995 if tokenizer. has_newline_at( 1 ) { break }
956996 tokenizer. advance( 1 ) ;
@@ -962,17 +1002,9 @@ fn consume_name<'a>(tokenizer: &mut Tokenizer<'a>) -> CowRcStr<'a> {
9621002 value_bytes. extend( "\u{FFFD} " . as_bytes( ) ) ;
9631003 } ,
9641004 b'\x80' ..=b'\xBF' => {
965- // This byte *is* part of a multi-byte code point,
966- // we’ll end up copying the whole code point before this loop does something else.
9671005 tokenizer. consume_continuation_byte( ) ;
9681006 value_bytes. push( b)
9691007 }
970- b'\xC0' ..=b'\xEF' => {
971- // This byte *is* part of a multi-byte code point,
972- // we’ll end up copying the whole code point before this loop does something else.
973- tokenizer. advance( 1 ) ;
974- value_bytes. push( b)
975- }
9761008 b'\xF0' ..=b'\xFF' => {
9771009 tokenizer. consume_4byte_intro( ) ;
9781010 value_bytes. push( b)
0 commit comments