@@ -330,7 +330,22 @@ impl<'a> PreAllocatedString<'a> {
330330 }
331331}
332332
333- fn escape_json_string < S : AsRef < str > > ( s : S ) -> String {
333+ pub fn escape_json_string < S : AsRef < str > > ( s : S ) -> String {
334+ let s = s. as_ref ( ) ;
335+
336+ // Use AVX512 acceleration if available on x86_64
337+ #[ cfg( target_arch = "x86_64" ) ]
338+ {
339+ if is_x86_feature_detected ! ( "avx512f" ) && is_x86_feature_detected ! ( "avx512bw" ) {
340+ return unsafe { escape_json_string_avx512 ( s) } ;
341+ }
342+ }
343+
344+ // Fallback to serde_json implementation
345+ escape_json_string_fallback ( s)
346+ }
347+
348+ pub fn escape_json_string_fallback < S : AsRef < str > > ( s : S ) -> String {
334349 let s = s. as_ref ( ) ;
335350 let mut escaped_buf = Vec :: with_capacity ( s. len ( ) * 2 + 2 ) ;
336351 // This call is infallible as only error it can return is if the writer errors.
@@ -340,6 +355,81 @@ fn escape_json_string<S: AsRef<str>>(s: S) -> String {
340355 unsafe { String :: from_utf8_unchecked ( escaped_buf) }
341356}
342357
358+ #[ cfg( target_arch = "x86_64" ) ]
359+ #[ target_feature( enable = "avx512f,avx512bw" ) ]
360+ unsafe fn escape_json_string_avx512 ( s : & str ) -> String {
361+ use std:: arch:: x86_64:: * ;
362+
363+ let bytes = s. as_bytes ( ) ;
364+ let mut result = Vec :: with_capacity ( s. len ( ) * 2 + 2 ) ;
365+
366+ // Add opening quote
367+ result. push ( b'"' ) ;
368+
369+ let mut i = 0 ;
370+
371+ // Process 64-byte chunks with AVX512
372+ while i + 64 <= bytes. len ( ) {
373+ unsafe {
374+ let chunk = _mm512_loadu_si512 ( bytes. as_ptr ( ) . add ( i) as * const __m512i ) ;
375+
376+ // Check for characters that need escaping
377+ // ASCII control characters (0x00-0x1F), quote (0x22), backslash (0x5C)
378+ let control_mask = _mm512_cmplt_epu8_mask ( chunk, _mm512_set1_epi8 ( 0x20 ) ) ;
379+ let quote_mask = _mm512_cmpeq_epu8_mask ( chunk, _mm512_set1_epi8 ( b'"' as i8 ) ) ;
380+ let backslash_mask = _mm512_cmpeq_epu8_mask ( chunk, _mm512_set1_epi8 ( b'\\' as i8 ) ) ;
381+
382+ let escape_mask = control_mask | quote_mask | backslash_mask;
383+
384+ if escape_mask == 0 {
385+ // No characters need escaping, copy directly
386+ result. extend_from_slice ( & bytes[ i..i + 64 ] ) ;
387+ i += 64 ;
388+ } else {
389+ // Process byte by byte for this chunk due to escaping needed
390+ for j in 0 ..64 {
391+ let byte = bytes[ i + j] ;
392+ append_escaped_byte ( & mut result, byte) ;
393+ }
394+ i += 64 ;
395+ }
396+ }
397+ }
398+
399+ // Process remaining bytes
400+ while i < bytes. len ( ) {
401+ append_escaped_byte ( & mut result, bytes[ i] ) ;
402+ i += 1 ;
403+ }
404+
405+ // Add closing quote
406+ result. push ( b'"' ) ;
407+
408+ // Safety: We only write valid UTF-8 sequences
409+ unsafe { String :: from_utf8_unchecked ( result) }
410+ }
411+
412+ #[ inline]
413+ fn append_escaped_byte ( result : & mut Vec < u8 > , byte : u8 ) {
414+ match byte {
415+ b'"' => result. extend_from_slice ( b"\\ \" " ) ,
416+ b'\\' => result. extend_from_slice ( b"\\ \\ " ) ,
417+ b'\x08' => result. extend_from_slice ( b"\\ b" ) ,
418+ b'\x0C' => result. extend_from_slice ( b"\\ f" ) ,
419+ b'\n' => result. extend_from_slice ( b"\\ n" ) ,
420+ b'\r' => result. extend_from_slice ( b"\\ r" ) ,
421+ b'\t' => result. extend_from_slice ( b"\\ t" ) ,
422+ 0x00 ..=0x1F => {
423+ // Other control characters as unicode escapes
424+ result. extend_from_slice ( b"\\ u00" ) ;
425+ let hex_chars = b"0123456789abcdef" ;
426+ result. push ( hex_chars[ ( byte >> 4 ) as usize ] ) ;
427+ result. push ( hex_chars[ ( byte & 0x0F ) as usize ] ) ;
428+ }
429+ _ => result. push ( byte) ,
430+ }
431+ }
432+
343433#[ test]
344434fn test_escape_json_string ( ) {
345435 const FIXTURES : & [ ( char , & str ) ] = & [
@@ -364,6 +454,61 @@ fn test_escape_json_string() {
364454 }
365455}
366456
457+ #[ test]
458+ fn test_avx512_vs_fallback_consistency ( ) {
459+ let long_x = "x" . repeat ( 100 ) ;
460+ let long_quotes = "\" " . repeat ( 100 ) ;
461+ let long_control = "\n " . repeat ( 100 ) ;
462+
463+ let test_strings = vec ! [
464+ "" ,
465+ "simple" ,
466+ "with\" quotes" ,
467+ "with\\ backslashes" ,
468+ "with\n control\t chars\r " ,
469+ "🚀 unicode 虎" ,
470+ & long_x, // Test longer strings to trigger AVX512 path
471+ & long_quotes, // Many quotes to test escaping
472+ & long_control, // Many control chars
473+ "mixed content with \" quotes\" , \\ backslashes, and \n control\t chars" ,
474+ ] ;
475+
476+ for test_str in test_strings {
477+ let fallback_result = escape_json_string_fallback ( test_str) ;
478+ let main_result = escape_json_string ( test_str) ;
479+ assert_eq ! (
480+ fallback_result, main_result,
481+ "Results differ for input: {:?}" ,
482+ test_str
483+ ) ;
484+ }
485+ }
486+
487+ #[ test]
488+ #[ cfg( target_arch = "x86_64" ) ]
489+ fn test_avx512_direct ( ) {
490+ // Test the AVX512 function directly if AVX512 is available
491+ if is_x86_feature_detected ! ( "avx512f" ) && is_x86_feature_detected ! ( "avx512bw" ) {
492+ let long_a = "a" . repeat ( 128 ) ;
493+ let test_strings = vec ! [
494+ "simple test" ,
495+ "with \" quotes\" " ,
496+ "with\n control\r chars\t " ,
497+ long_a. as_str( ) , // Longer than one AVX512 chunk
498+ ] ;
499+
500+ for test_str in test_strings {
501+ let avx512_result = unsafe { escape_json_string_avx512 ( test_str) } ;
502+ let fallback_result = escape_json_string_fallback ( test_str) ;
503+ assert_eq ! (
504+ avx512_result, fallback_result,
505+ "AVX512 and fallback results differ for: {:?}" ,
506+ test_str
507+ ) ;
508+ }
509+ }
510+ }
511+
367512#[ test]
368513fn test_encode ( ) {
369514 let input = r#"{
0 commit comments