@@ -74,7 +74,7 @@ impl Tokenizer<'_> {
7474 }
7575
7676 // find_entity() only returns keys of TOKEN_NAMED_CHARS, so the lookup cannot miss
77- let entity_chars = TOKEN_NAMED_CHARS . get ( entity. as_str ( ) ) . copied ( ) . unwrap_or_default ( ) ;
77+ let entity_chars = TOKEN_NAMED_CHARS . get ( entity) . copied ( ) . unwrap_or_default ( ) ;
7878
7979 // Flush codepoints consumed as character reference
8080 for c in entity_chars. chars ( ) {
@@ -326,20 +326,27 @@ impl Tokenizer<'_> {
326326 }
327327
328328 /// Finds the longest entity from the current position in the stream. Returns the entity
329- /// replacement OR None when no entity has been found.
330- fn find_entity ( & mut self ) -> Option < String > {
329+ /// name OR None when no entity has been found.
330+ fn find_entity ( & mut self ) -> Option < & ' static str > {
331331 let chars = self . stream . get_slice ( * LONGEST_ENTITY_LENGTH ) ;
332332
333- for i in ( 0 ..=chars. len ( ) ) . rev ( ) {
334- if let Some ( slice) = chars. get ( 0 ..i) {
335- let entity: String = slice. iter ( ) . map ( std:: string:: ToString :: to_string) . collect ( ) ;
336- if TOKEN_NAMED_CHARS . contains_key ( entity. as_str ( ) ) {
337- return Some ( entity) ;
333+ // Entity names are ASCII alphanumerics with an optional trailing ';', so only a
334+ // prefix of that run can match.
335+ let mut candidate = String :: with_capacity ( chars. len ( ) ) ;
336+ for c in chars {
337+ match c {
338+ Ch ( ch) if ch. is_ascii_alphanumeric ( ) => candidate. push ( ch) ,
339+ Ch ( ';' ) => {
340+ candidate. push ( ';' ) ;
341+ break ;
338342 }
343+ _ => break ,
339344 }
340345 }
341346
342- None
347+ ( 1 ..=candidate. len ( ) )
348+ . rev ( )
349+ . find_map ( |i| TOKEN_NAMED_CHARS . get_key ( & candidate[ ..i] ) . copied ( ) )
343350 }
344351}
345352
@@ -419,6 +426,10 @@ mod tests {
419426 entity_114: ( "©&" , "©&" )
420427 entity_115: ( "©a " , "©a " )
421428 entity_116: ( "©X " , "©X " )
429+ entity_117: ( "©é" , "©é" )
430+ entity_118: ( "∳" , "∳" )
431+ entity_119: ( "&amp;" , "&" )
432+ entity_120: ( "Žn;" , "Žn;" )
422433
423434 // // ChatGPT generated tests
424435 entity_200: ( "©" , "©" )
0 commit comments