value_trait/
generator.rs

1// This is mostly taken from json-rust's codegen
2// as it seems to perform well and it makes sense to see
3// if we can adopt the approach
4//
5// https://github.com/maciejhirsz/json-rust/blob/master/src/codegen.rs
6
7macro_rules! stry {
8    ($e:expr) => {
9        match $e {
10            ::std::result::Result::Ok(val) => val,
11            ::std::result::Result::Err(err) => return ::std::result::Result::Err(err),
12        }
13    };
14}
15
16use std::io;
17use std::io::Write;
18use std::ptr;
19
20const QU: u8 = b'"';
21const BS: u8 = b'\\';
22const BB: u8 = b'b';
23const TT: u8 = b't';
24const NN: u8 = b'n';
25const FF: u8 = b'f';
26const RR: u8 = b'r';
27const UU: u8 = b'u';
28const __: u8 = 0;
29
30// Look up table for characters that need escaping in a product string
31pub(crate) static ESCAPED: [u8; 256] = [
32    // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
33    UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
34    UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
35    __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
36    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
37    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
38    __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
39    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
40    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
41    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
42    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
43    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
44    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
45    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
46    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
47    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
48    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
49];
50
51// taken from https://github.com/serde-rs/json/blob/4354fc3eb2232ee0ba9a9a23acce107a980a6dc0/src/ser.rs#L1790
52// This is called rarely
53#[inline(never)]
54fn u_encode<W>(w: &mut W, byte: u8) -> io::Result<()>
55where
56    W: Write,
57{
58    static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
59    let bytes = [
60        b'\\',
61        b'u',
62        b'0',
63        b'0',
64        HEX_DIGITS[(byte >> 4) as usize],
65        HEX_DIGITS[(byte & 0xF) as usize],
66    ];
67    w.write_all(&bytes)
68}
69
70/// Base generator trait
71pub trait BaseGenerator {
72    /// The writer
73    type T: Write;
74
75    /// returns the writer
76    fn get_writer(&mut self) -> &mut Self::T;
77
78    /// Write a slice
79    /// # Errors
80    /// if the write fails
81    #[inline]
82    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
83        self.get_writer().write_all(slice)
84    }
85
86    /// Write a char
87    /// # Errors
88    /// if the write fails
89    #[inline]
90    fn write_char(&mut self, ch: u8) -> io::Result<()> {
91        self.get_writer().write_all(&[ch])
92    }
93
94    /// write with minimum
95    /// # Errors
96    /// if the write fails
97    fn write_min(&mut self, slice: &[u8], min: u8) -> io::Result<()>;
98
99    /// writes new line
100    /// # Errors
101    /// if the write fails
102    #[inline]
103    fn new_line(&mut self) -> io::Result<()> {
104        Ok(())
105    }
106
107    /// indents one step
108    #[inline]
109    fn indent(&mut self) {}
110
111    /// dedents one step
112    #[inline]
113    fn dedent(&mut self) {}
114
115    /// writes a string
116    /// # Errors
117    /// if the write fails
118    #[inline]
119    fn write_string(&mut self, string: &str) -> io::Result<()> {
120        stry!(self.write_char(b'"'));
121        stry!(self.write_string_content(string));
122        self.write_char(b'"')
123    }
124
125    /// writes a string
126    /// # Errors
127    /// if the write fails
128    #[inline]
129    fn write_string_content(&mut self, string: &str) -> io::Result<()> {
130        let mut string = string.as_bytes();
131        unsafe {
132            // Looking at the table above the lower 5 bits are entirely
133            // quote characters that gives us a bitmask of 0x1f for that
134            // region, only quote (`"`) and backslash (`\`) are not in
135            // this range.
136            stry!(self.write_str_simd(&mut string));
137        }
138        write_string_rust(self.get_writer(), &mut string)
139    }
140
141    /// writes a simple string (usually short and non escaped)
142    /// This means we can skip the simd accelerated writing which is
143    /// expensive on short strings.
144    /// # Errors
145    /// if the write fails
146    #[inline]
147    fn write_simple_string(&mut self, string: &str) -> io::Result<()> {
148        self.write(br#"""#)?;
149        write_string_rust(self.get_writer(), &mut string.as_bytes())?;
150        self.write(br#"""#)
151    }
152    /// writes a simple string content  (usually short and non escaped)
153    /// This means we can skip the simd accelerated writing which is
154    /// expensive on short strings.
155    /// # Errors
156    /// if the write fails
157    #[inline]
158    fn write_simple_str_content(&mut self, string: &str) -> io::Result<()> {
159        let mut string = string.as_bytes();
160        // Legacy code to handle the remainder of the code
161        write_string_rust(self.get_writer(), &mut string)
162    }
163
164    /// writes a float value
165    /// # Errors
166    /// if the write fails
167    #[inline]
168    fn write_float(&mut self, num: f64) -> io::Result<()> {
169        let mut buffer = ryu::Buffer::new();
170        let s = buffer.format_finite(num);
171        self.get_writer().write_all(s.as_bytes())
172    }
173
174    /// writes an integer value
175    /// # Errors
176    /// if the write fails
177    #[inline]
178    fn write_int<I: itoa::Integer>(&mut self, num: I) -> io::Result<()> {
179        let mut buffer = itoa::Buffer::new();
180        let s = buffer.format(num);
181        self.get_writer().write_all(s.as_bytes())
182    }
183
184    /// # Safety
185    /// This function is unsafe because it may use simd instructions
186    /// # Errors
187    ///  if the write fails
188    #[cfg(all(
189        feature = "runtime-detection",
190        any(target_arch = "x86_64", target_arch = "x86"),
191    ))]
192    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
193        write_str_simd_fastest(self.get_writer(), string)
194    }
195    #[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))]
196    #[inline]
197    /// Writes a string with simd-acceleration
198    /// # Safety
199    /// This function is unsafe because it uses simd instructions
200    /// # Errors
201    ///  if the write fails
202    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
203        write_str_simd_avx2(self.get_writer(), string)
204    }
205
206    #[cfg(all(
207        target_feature = "sse2",
208        not(target_feature = "avx2"),
209        not(feature = "runtime-detection")
210    ))]
211    #[inline]
212    /// Writes a string with simd-acceleration
213    /// # Safety
214    /// This function is unsafe because it uses simd instructions
215    /// # Errors
216    ///  if the write fails
217    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
218        write_str_simd_sse42(self.get_writer(), string)
219    }
220
221    #[cfg(not(any(
222        all(
223            feature = "runtime-detection",
224            any(target_arch = "x86_64", target_arch = "x86")
225        ),
226        feature = "portable",
227        target_feature = "avx2",
228        target_feature = "sse4.2",
229        target_feature = "simd128",
230        target_arch = "aarch64",
231    )))]
232    #[inline]
233    /// Writes a string with simd-acceleration (not really, as the architecture doesn't support it)
234    /// # Safety
235    /// This function is unsafe because it uses simd instructions
236    /// # Errors
237    ///  if the write fails
238    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
239        write_string_rust(self.get_writer(), string)?;
240        *string = &string[string.len()..];
241        Ok(())
242    }
243
244    #[cfg(target_arch = "aarch64")]
245    #[inline]
246    /// Writes a string with simd-acceleration
247    /// # Safety
248    /// This function is unsafe because it uses simd instructions
249    /// # Errors
250    ///  if the write fails
251    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
252        use std::arch::aarch64::{
253            uint8x16_t, vandq_u8, vceqq_u8, vdupq_n_u8, veorq_u8, vgetq_lane_u16, vld1q_u8,
254            vorrq_u8, vpaddq_u8, vreinterpretq_u16_u8,
255        };
256        use std::mem;
257
258        #[inline]
259        unsafe fn bit_mask() -> uint8x16_t {
260            unsafe {
261                mem::transmute([
262                    0x01_u8, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10,
263                    0x20, 0x40, 0x80,
264                ])
265            }
266        }
267
268        #[inline]
269        unsafe fn neon_movemask(input: uint8x16_t) -> u16 {
270            unsafe {
271                let simd_input: uint8x16_t = vandq_u8(input, bit_mask());
272                let tmp: uint8x16_t = vpaddq_u8(simd_input, simd_input);
273                let tmp = vpaddq_u8(tmp, tmp);
274                let tmp = vpaddq_u8(tmp, tmp);
275
276                vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0)
277            }
278        }
279
280        let writer = self.get_writer();
281        // The case where we have a 16+ byte block
282        // we repeate the same logic as above but with
283        // only 16 bytes
284        let mut idx = 0;
285        unsafe {
286            let zero = vdupq_n_u8(0);
287            let lower_quote_range = vdupq_n_u8(0x1F);
288            let quote = vdupq_n_u8(b'"');
289            let backslash = vdupq_n_u8(b'\\');
290            while string.len() - idx > 16 {
291                // Load 16 bytes of data;
292                let data: uint8x16_t = vld1q_u8(string.as_ptr().add(idx));
293                // Test the data against being backslash and quote.
294                let bs_or_quote = vorrq_u8(vceqq_u8(data, backslash), vceqq_u8(data, quote));
295                // Now mask the data with the quote range (0x1F).
296                let in_quote_range = vandq_u8(data, lower_quote_range);
297                // then test of the data is unchanged. aka: xor it with the
298                // Any field that was inside the quote range it will be zero
299                // now.
300                let is_unchanged = veorq_u8(data, in_quote_range);
301                let in_range = vceqq_u8(is_unchanged, zero);
302                let quote_bits = neon_movemask(vorrq_u8(bs_or_quote, in_range));
303                if quote_bits == 0 {
304                    idx += 16;
305                } else {
306                    let quote_dist = quote_bits.trailing_zeros() as usize;
307                    stry!(writer.write_all(&string[0..idx + quote_dist]));
308                    let ch = string[idx + quote_dist];
309                    match ESCAPED[ch as usize] {
310                        b'u' => stry!(u_encode(writer, ch)),
311                        escape => stry!(writer.write_all(&[b'\\', escape])),
312                    }
313
314                    *string = &string[idx + quote_dist + 1..];
315                    idx = 0;
316                }
317            }
318        }
319        stry!(writer.write_all(&string[0..idx]));
320        *string = &string[idx..];
321        Ok(())
322    }
323
324    #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
325    #[inline]
326    /// Writes a string with simd-acceleration
327    /// # Safety
328    /// This function is unsafe because it uses simd instructions
329    /// # Errors
330    ///  if the write fails
331    unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
332        let writer = self.get_writer();
333        use std::arch::wasm32::{
334            u8x16_bitmask, u8x16_eq, u8x16_splat, v128, v128_and, v128_load, v128_or, v128_xor,
335        };
336
337        // The case where we have a 16+ byte block
338        // we repeat the same logic as above but with
339        // only 16 bytes
340        let mut idx = 0;
341        let zero = u8x16_splat(0);
342        let lower_quote_range = u8x16_splat(0x1F);
343        let quote = u8x16_splat(b'"');
344        let backslash = u8x16_splat(b'\\');
345        while string.len() - idx > 16 {
346            // Load 16 bytes of data;
347            let data = v128_load(string.as_ptr().add(idx).cast::<v128>());
348            // Test the data against being backslash and quote.
349            let bs_or_quote = v128_or(u8x16_eq(data, backslash), u8x16_eq(data, quote));
350            // Now mask the data with the quote range (0x1F).
351            let in_quote_range = v128_and(data, lower_quote_range);
352            // then test of the data is unchanged. aka: xor it with the
353            // Any field that was inside the quote range it will be zero
354            // now.
355            let is_unchanged = v128_xor(data, in_quote_range);
356            let in_range = u8x16_eq(is_unchanged, zero);
357            let quote_bits = u8x16_bitmask(v128_or(bs_or_quote, in_range));
358            if quote_bits == 0 {
359                idx += 16;
360            } else {
361                let quote_dist = quote_bits.trailing_zeros() as usize;
362                stry!(writer.write_all(&string[0..idx + quote_dist]));
363                let ch = string[idx + quote_dist];
364                match ESCAPED[ch as usize] {
365                    b'u' => stry!(u_encode(writer, ch)),
366                    escape => stry!(writer.write_all(&[b'\\', escape])),
367                }
368
369                *string = &string[idx + quote_dist + 1..];
370                idx = 0;
371            }
372        }
373        stry!(writer.write_all(&string[0..idx]));
374        *string = &string[idx..];
375        Ok(())
376    }
377}
378
379#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
380unsafe fn write_str_simd_fastest<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
381where
382    W: Write,
383{
384    // This is not possible right now since we can't get `W` to be part of the static
385    // use std::sync::atomic::{AtomicPtr, Ordering};
386    // type FnRaw = *mut ();
387
388    // type WriteStrFn<T> = for<'a, 'b, 'c> unsafe fn(&'a mut T, &'b mut &'c [u8]) -> io::Result<()>;
389    // static FN: AtomicPtr<()> = AtomicPtr::new(get_fastest::<W> as FnRaw);
390    // #[inline]
391    // fn get_fastest_available_implementation<W>() -> WriteStrFn<W>
392    // where
393    //     W: Write,
394    // {
395    //     if std::is_x86_feature_detected!("avx2") {
396    //         write_str_simd_avx2
397    //     } else if std::is_x86_feature_detected!("sse4.2") {
398    //         write_str_simd_sse42
399    //     } else {
400    //         write_str_simd_rust
401    //     }
402    // }
403
404    // #[inline]
405    // unsafe fn get_fastest<'invoke, 'de, W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
406    // where
407    //     W: Write,
408    //     'de: 'invoke,
409    // {
410    //     let fun = get_fastest_available_implementation();
411    //     FN.store(fun as FnRaw, Ordering::Relaxed);
412    //     (fun)(writer, string)
413    // }
414    // let fun = FN.load(Ordering::Relaxed);
415    // mem::transmute::<FnRaw, WriteStrFn>(fun)(writer, string)
416
417    if std::is_x86_feature_detected!("avx2") {
418        write_str_simd_avx2(writer, string)
419    } else if std::is_x86_feature_detected!("sse4.2") {
420        write_str_simd_sse42(writer, string)
421    } else {
422        #[cfg(not(feature = "portable"))]
423        return write_string_rust(writer, string);
424        #[cfg(feature = "portable")]
425        return write_str_simd_portable(writer, string);
426    }
427}
428#[inline]
429fn write_string_container<W>(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()>
430where
431    W: Write,
432{
433    stry!(writer.write_all(&string[..start]));
434
435    for (index, ch) in string.iter().enumerate().skip(start) {
436        let escape = ESCAPED[*ch as usize];
437        if escape > 0 {
438            stry!(writer.write_all(&string[start..index]));
439            if escape == b'u' {
440                stry!(u_encode(writer, *ch));
441            } else {
442                stry!(writer.write_all(&[b'\\', escape]));
443            }
444            start = index + 1;
445        }
446    }
447    writer.write_all(&string[start..])
448}
449
450#[inline]
451fn write_string_rust<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
452where
453    W: Write,
454{
455    // Legacy code to handle the remainder of the code
456    for (index, ch) in string.iter().enumerate() {
457        if ESCAPED[*ch as usize] > 0 {
458            return write_string_container(writer, string, index);
459        }
460    }
461    writer.write_all(string)
462}
463
464#[cfg(feature = "portable")]
465#[inline]
466/// Writes a string with simd-acceleration
467/// # Safety
468/// This function is unsafe because it uses simd instructions
469/// # Errors
470///  if the write fails
471unsafe fn write_str_simd_portable<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
472where
473    W: Write,
474{
475    use std::simd::{SimdPartialEq, ToBitMask, u8x32};
476
477    let mut idx = 0;
478    let zero = u8x32::splat(0);
479    let lower_quote_range = u8x32::splat(0x1F_u8);
480    let quote = u8x32::splat(b'"');
481    let backslash = u8x32::splat(b'\\');
482    while string.len() - idx >= 32 {
483        // Load 32 bytes of data;
484        let data = u8x32::from_slice(&string[idx..]);
485        // Test the data against being backslash and quote.
486        let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote);
487        // Now mask the data with the quote range (0x1F).
488        let in_quote_range = data & lower_quote_range;
489        // then test of the data is unchanged. aka: xor it with the
490        // Any field that was inside the quote range it will be zero
491        // now.
492        let is_unchanged = data ^ in_quote_range;
493        let in_range = is_unchanged.simd_eq(zero);
494        let quote_bits = (bs_or_quote | in_range).to_bitmask();
495        if quote_bits == 0 {
496            idx += 32;
497        } else {
498            let quote_dist = quote_bits.trailing_zeros() as usize;
499            stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
500
501            let ch = string[idx + quote_dist];
502            match ESCAPED[ch as usize] {
503                b'u' => stry!(u_encode(writer, ch)),
504                escape => stry!(writer.write_all(&[b'\\', escape])),
505            };
506
507            *string = string.get_unchecked(idx + quote_dist + 1..);
508            idx = 0;
509        }
510    }
511    stry!(writer.write_all(&string[0..idx]));
512    *string = string.get_unchecked(idx..);
513    Ok(())
514}
515
516#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
517#[target_feature(enable = "avx2")]
518#[inline]
519/// Writes a string with simd-acceleration
520/// # Safety
521/// This function is unsafe because it uses simd instructions
522/// # Errors
523///  if the write fails
524unsafe fn write_str_simd_avx2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
525where
526    W: Write,
527{
528    #[cfg(target_arch = "x86")]
529    use std::arch::x86::{
530        __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
531        _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
532    };
533    #[cfg(target_arch = "x86_64")]
534    use std::arch::x86_64::{
535        __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
536        _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
537    };
538
539    let mut idx = 0;
540    let zero = _mm256_set1_epi8(0);
541    let lower_quote_range = _mm256_set1_epi8(0x1F_i8);
542    #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine
543    let quote = _mm256_set1_epi8(b'"' as i8);
544    #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine
545    let backslash = _mm256_set1_epi8(b'\\' as i8);
546    while string.len() - idx >= 32 {
547        // Load 32 bytes of data; _mm256_loadu_si256 does not require alignment
548        #[allow(clippy::cast_ptr_alignment)]
549        let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>());
550        // Test the data against being backslash and quote.
551        let bs_or_quote = _mm256_or_si256(
552            _mm256_cmpeq_epi8(data, backslash),
553            _mm256_cmpeq_epi8(data, quote),
554        );
555        // Now mask the data with the quote range (0x1F).
556        let in_quote_range = _mm256_and_si256(data, lower_quote_range);
557        // then test of the data is unchanged. aka: xor it with the
558        // Any field that was inside the quote range it will be zero
559        // now.
560        let is_unchanged = _mm256_xor_si256(data, in_quote_range);
561        let in_range = _mm256_cmpeq_epi8(is_unchanged, zero);
562        let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range));
563        if quote_bits == 0 {
564            idx += 32;
565        } else {
566            let quote_dist = quote_bits.trailing_zeros() as usize;
567            stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
568
569            let ch = string[idx + quote_dist];
570            match ESCAPED[ch as usize] {
571                b'u' => stry!(u_encode(writer, ch)),
572                escape => stry!(writer.write_all(&[b'\\', escape])),
573            };
574
575            *string = string.get_unchecked(idx + quote_dist + 1..);
576            idx = 0;
577        }
578    }
579    stry!(writer.write_all(&string[0..idx]));
580    *string = string.get_unchecked(idx..);
581    Ok(())
582}
583
584#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
585#[target_feature(enable = "sse4.2")]
586#[inline]
587/// Writes a string with simd-acceleration
588/// # Safety
589/// This function is unsafe because it uses simd instructions
590/// # Errors
591///  if the write fails
592unsafe fn write_str_simd_sse42<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
593where
594    W: Write,
595{
596    #[cfg(target_arch = "x86")]
597    use std::arch::x86::{
598        __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
599        _mm_set1_epi8, _mm_xor_si128,
600    };
601    #[cfg(target_arch = "x86_64")]
602    use std::arch::x86_64::{
603        __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
604        _mm_set1_epi8, _mm_xor_si128,
605    };
606
607    let mut idx = 0;
608    let zero = _mm_set1_epi8(0);
609    let lower_quote_range = _mm_set1_epi8(0x1F_i8);
610    #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine
611    let quote = _mm_set1_epi8(b'"' as i8);
612    #[allow(clippy::cast_possible_wrap)] // it's a const, it's fine
613    let backslash = _mm_set1_epi8(b'\\' as i8);
614    while string.len() - idx > 16 {
615        // Load 16 bytes of data; _mm_loadu_si128 does not require alignment
616        #[allow(clippy::cast_ptr_alignment)]
617        let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>());
618        // Test the data against being backslash and quote.
619        let bs_or_quote =
620            _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote));
621        // Now mask the data with the quote range (0x1F).
622        let in_quote_range = _mm_and_si128(data, lower_quote_range);
623        // then test of the data is unchanged. aka: xor it with the
624        // Any field that was inside the quote range it will be zero
625        // now.
626        let is_unchanged = _mm_xor_si128(data, in_quote_range);
627        let in_range = _mm_cmpeq_epi8(is_unchanged, zero);
628        let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range));
629        if quote_bits == 0 {
630            idx += 16;
631        } else {
632            let quote_dist = quote_bits.trailing_zeros() as usize;
633            stry!(writer.write_all(&string[0..idx + quote_dist]));
634
635            let ch = string[idx + quote_dist];
636            match ESCAPED[ch as usize] {
637                b'u' => stry!(u_encode(writer, ch)),
638                escape => stry!(writer.write_all(&[b'\\', escape])),
639            }
640
641            *string = &string[idx + quote_dist + 1..];
642            idx = 0;
643        }
644    }
645    stry!(writer.write_all(&string[0..idx]));
646    *string = &string[idx..];
647    Ok(())
648}
649
650///  Simple dump Generator
651pub struct DumpGenerator {
652    code: Vec<u8>,
653}
654
655impl Default for DumpGenerator {
656    fn default() -> Self {
657        Self {
658            code: Vec::with_capacity(1024),
659        }
660    }
661}
662
663impl DumpGenerator {
664    /// Creates a new generator
665    #[must_use]
666    pub fn new() -> Self {
667        Self::default()
668    }
669
670    /// Returns the data as a String
671    #[must_use]
672    pub fn consume(self) -> String {
673        // Original strings were unicode, numbers are all ASCII,
674        // therefore this is safe.
675        unsafe { String::from_utf8_unchecked(self.code) }
676    }
677}
678
679impl BaseGenerator for DumpGenerator {
680    type T = Vec<u8>;
681
682    #[inline]
683    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
684        extend_from_slice(&mut self.code, slice);
685        Ok(())
686    }
687    #[inline]
688    fn write_char(&mut self, ch: u8) -> io::Result<()> {
689        self.code.push(ch);
690        Ok(())
691    }
692
693    #[inline]
694    fn get_writer(&mut self) -> &mut Vec<u8> {
695        &mut self.code
696    }
697
698    #[inline]
699    fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
700        self.code.push(min);
701        Ok(())
702    }
703}
704
705/// Pretty Generator
706pub struct PrettyGenerator {
707    code: Vec<u8>,
708    dent: u16,
709    spaces_per_indent: u16,
710}
711
712impl PrettyGenerator {
713    /// Creates a new pretty printing generator
714    #[must_use]
715    pub fn new(spaces: u16) -> Self {
716        Self {
717            code: Vec::with_capacity(1024),
718            dent: 0,
719            spaces_per_indent: spaces,
720        }
721    }
722
723    /// Returns the data as a String
724    #[must_use]
725    pub fn consume(self) -> String {
726        unsafe { String::from_utf8_unchecked(self.code) }
727    }
728}
729
730impl BaseGenerator for PrettyGenerator {
731    type T = Vec<u8>;
732    #[inline]
733    fn write(&mut self, slice: &[u8]) -> io::Result<()> {
734        extend_from_slice(&mut self.code, slice);
735        Ok(())
736    }
737
738    #[inline]
739    fn write_char(&mut self, ch: u8) -> io::Result<()> {
740        self.code.push(ch);
741        Ok(())
742    }
743
744    #[inline]
745    fn get_writer(&mut self) -> &mut Vec<u8> {
746        &mut self.code
747    }
748
749    #[inline]
750    fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
751        extend_from_slice(&mut self.code, slice);
752        Ok(())
753    }
754
755    fn new_line(&mut self) -> io::Result<()> {
756        self.code.push(b'\n');
757        self.code.resize(
758            self.code.len() + (self.dent * self.spaces_per_indent) as usize,
759            b' ',
760        );
761        Ok(())
762    }
763
764    fn indent(&mut self) {
765        self.dent += 1;
766    }
767
768    fn dedent(&mut self) {
769        self.dent -= 1;
770    }
771}
772
773/// Writer Generator
774pub struct WriterGenerator<'w, W: 'w + Write> {
775    writer: &'w mut W,
776}
777
778impl<'w, W> WriterGenerator<'w, W>
779where
780    W: 'w + Write,
781{
782    /// Creates a new generator
783    pub fn new(writer: &'w mut W) -> Self {
784        WriterGenerator { writer }
785    }
786}
787
788impl<W> BaseGenerator for WriterGenerator<'_, W>
789where
790    W: Write,
791{
792    type T = W;
793
794    #[inline]
795    fn get_writer(&mut self) -> &mut W {
796        self.writer
797    }
798
799    #[inline]
800    fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
801        self.writer.write_all(&[min])
802    }
803}
804
805/// Pretty Writer Generator
806pub struct PrettyWriterGenerator<'w, W>
807where
808    W: 'w + Write,
809{
810    writer: &'w mut W,
811    dent: u16,
812    spaces_per_indent: u16,
813}
814
815impl<'w, W> PrettyWriterGenerator<'w, W>
816where
817    W: 'w + Write,
818{
819    /// Creates a new generator
820    pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self {
821        PrettyWriterGenerator {
822            writer,
823            dent: 0,
824            spaces_per_indent,
825        }
826    }
827}
828
829impl<W> BaseGenerator for PrettyWriterGenerator<'_, W>
830where
831    W: Write,
832{
833    type T = W;
834
835    #[inline]
836    fn get_writer(&mut self) -> &mut W {
837        self.writer
838    }
839
840    #[inline]
841    fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
842        self.writer.write_all(slice)
843    }
844
845    fn new_line(&mut self) -> io::Result<()> {
846        stry!(self.write_char(b'\n'));
847        for _ in 0..(self.dent * self.spaces_per_indent) {
848            stry!(self.write_char(b' '));
849        }
850        Ok(())
851    }
852
853    fn indent(&mut self) {
854        self.dent += 1;
855    }
856
857    fn dedent(&mut self) {
858        self.dent -= 1;
859    }
860}
861
862// From: https://github.com/dtolnay/fastwrite/blob/master/src/lib.rs#L68
863//
864// LLVM is not able to lower `Vec::extend_from_slice` into a memcpy, so this
865// helps eke out that last bit of performance.
866#[inline]
867pub(crate) fn extend_from_slice(dst: &mut Vec<u8>, src: &[u8]) {
868    let dst_len = dst.len();
869    let src_len = src.len();
870
871    dst.reserve(src_len);
872
873    unsafe {
874        // We would have failed if `reserve` overflowed\
875        ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len);
876        dst.set_len(dst_len + src_len);
877    }
878}
879
880#[cfg(test)]
881mod tests {
882
883    #[test]
884    fn test_write_string_rust() {
885        let mut writer = Vec::new();
886        let mut string = "Hello, World!".as_bytes();
887        super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
888        assert_eq!(writer, "Hello, World!".as_bytes());
889    }
890    #[test]
891    fn test_write_string_rust2() {
892        let mut writer = Vec::new();
893        let mut string = "Hello, \"World!\"".as_bytes();
894        super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
895        assert_eq!(writer, "Hello, \\\"World!\\\"".as_bytes());
896    }
897}