@@ -320,6 +320,41 @@ fn format_string(value: &str, dst: &mut [u8]) -> usize {
320320 }
321321}
322322
323+ #[ inline( always) ]
324+ fn format_unquoted ( value : & str , dst : & mut [ u8 ] ) -> usize {
325+ #[ cfg( target_arch = "aarch64" ) ]
326+ {
327+ let has_neon = cfg ! ( target_os = "macos" ) || std:: arch:: is_aarch64_feature_detected!( "neon" ) ;
328+ if has_neon {
329+ unsafe { simd:: neon:: format_unquoted ( value, dst) }
330+ } else {
331+ simd:: v128:: format_string ( value, dst)
332+ }
333+ }
334+
335+ #[ cfg( any( target_arch = "x86" , target_arch = "x86_64" ) ) ]
336+ {
337+ #[ cfg( feature = "avx512" ) ]
338+ {
339+ if is_x86_feature_detected ! ( "avx512f" ) {
340+ return unsafe { simd:: avx512:: format_unquote ( value, dst) } ;
341+ }
342+ }
343+ if is_x86_feature_detected ! ( "avx2" ) {
344+ unsafe { simd:: avx2:: format_unquote ( value, dst) }
345+ } else if is_x86_feature_detected ! ( "sse2" ) {
346+ unsafe { simd:: sse2:: format_unquote ( value, dst) }
347+ } else {
348+ simd:: v128:: format_unquote ( value, dst)
349+ }
350+ }
351+
352+ #[ cfg( not( any( target_arch = "aarch64" , target_arch = "x86" , target_arch = "x86_64" ) ) ) ]
353+ {
354+ simd:: v128:: format_string ( value, dst)
355+ }
356+ }
357+
323358pub fn escape ( value : & str ) -> String {
324359 let capacity = value. len ( ) * 6 + 32 + 3 ;
325360 let mut buf = Vec :: with_capacity ( capacity) ;
@@ -332,9 +367,22 @@ pub fn escape(value: &str) -> String {
332367 unsafe { String :: from_utf8_unchecked ( buf) }
333368}
334369
370+ pub fn escape_unquote ( value : & str ) -> String {
371+ let capacity = value. len ( ) * 6 + 32 + 3 ;
372+ let mut buf = Vec :: with_capacity ( capacity) ;
373+ #[ allow( clippy:: uninit_vec) ]
374+ unsafe {
375+ buf. set_len ( capacity)
376+ } ;
377+ let cnt = format_unquoted ( value, & mut buf) ;
378+ unsafe { buf. set_len ( cnt) } ;
379+ unsafe { String :: from_utf8_unchecked ( buf) }
380+ }
381+
335382/// # Panics
336383///
337- /// Panics if the buffer is not large enough. Allocate enough capacity for dst.
384+ /// Panics if the buffer is not large enough. Allocate enough capacity for dst,
385+ /// x6 will be enough in the worst case.
338386pub fn escape_into < S : AsRef < str > > ( value : S , dst : & mut Vec < u8 > ) {
339387 let value = value. as_ref ( ) ;
340388 let old_len = dst. len ( ) ;
@@ -350,6 +398,26 @@ pub fn escape_into<S: AsRef<str>>(value: S, dst: &mut Vec<u8>) {
350398 }
351399}
352400
401+ /// Same as escape_into, just without open and close quotes.
402+ /// # Panics
403+ ///
404+ /// Panic if the buffer is not large enough. Allocation enough capacity for dst,
405+ /// x6 will be in the worst case.
406+ pub fn escape_into_unquote < S : AsRef < str > > ( value : S , dst : & mut Vec < u8 > ) {
407+ let value = value. as_ref ( ) ;
408+ let old_len = dst. len ( ) ;
409+
410+ // SAFETY: We've reserved enough capacity above, and format_string will
411+ // write valid UTF-8 bytes. We'll set the correct length after.
412+ unsafe {
413+ // Get a slice that includes the spare capacity
414+ let spare =
415+ std:: slice:: from_raw_parts_mut ( dst. as_mut_ptr ( ) . add ( old_len) , dst. capacity ( ) - old_len) ;
416+ let cnt = format_unquoted ( value, spare) ;
417+ dst. set_len ( old_len + cnt) ;
418+ }
419+ }
420+
353421#[ cfg( test) ]
354422mod tests {
355423 use std:: fs:: read_dir;
@@ -411,6 +479,12 @@ mod tests {
411479 assert_eq ! ( escape( "\r \n " ) , r#""\r\n""# ) ;
412480 }
413481
482+ #[ test]
483+ fn test_unquote ( ) {
484+ assert_eq ! ( escape_unquote( "abcd" ) , "abcd" ) ;
485+ assert_eq ! ( escape( "abcd" ) , r#""abcd""# ) ;
486+ }
487+
414488 #[ test]
415489 fn test_small_strings_16_bytes ( ) {
416490 // Exactly 16 bytes - SSE register boundary
0 commit comments