1macro_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
30pub(crate) static ESCAPED: [u8; 256] = [
32 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ];
50
51#[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
70pub trait BaseGenerator {
72 type T: Write;
74
75 fn get_writer(&mut self) -> &mut Self::T;
77
78 #[inline]
82 fn write(&mut self, slice: &[u8]) -> io::Result<()> {
83 self.get_writer().write_all(slice)
84 }
85
86 #[inline]
90 fn write_char(&mut self, ch: u8) -> io::Result<()> {
91 self.get_writer().write_all(&[ch])
92 }
93
94 fn write_min(&mut self, slice: &[u8], min: u8) -> io::Result<()>;
98
99 #[inline]
103 fn new_line(&mut self) -> io::Result<()> {
104 Ok(())
105 }
106
107 #[inline]
109 fn indent(&mut self) {}
110
111 #[inline]
113 fn dedent(&mut self) {}
114
115 #[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 #[inline]
129 fn write_string_content(&mut self, string: &str) -> io::Result<()> {
130 let mut string = string.as_bytes();
131 unsafe {
132 stry!(self.write_str_simd(&mut string));
137 }
138 write_string_rust(self.get_writer(), &mut string)
139 }
140
141 #[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 #[inline]
158 fn write_simple_str_content(&mut self, string: &str) -> io::Result<()> {
159 let mut string = string.as_bytes();
160 write_string_rust(self.get_writer(), &mut string)
162 }
163
164 #[inline]
168 fn write_float(&mut self, num: f64) -> io::Result<()> {
169 if !num.is_finite() {
171 return self.write(b"null");
172 }
173 let mut buffer = ryu::Buffer::new();
174 let s = buffer.format_finite(num);
175 self.get_writer().write_all(s.as_bytes())
176 }
177
178 #[inline]
182 fn write_int<I: itoa::Integer>(&mut self, num: I) -> io::Result<()> {
183 let mut buffer = itoa::Buffer::new();
184 let s = buffer.format(num);
185 self.get_writer().write_all(s.as_bytes())
186 }
187
188 #[cfg(all(
193 feature = "runtime-detection",
194 any(target_arch = "x86_64", target_arch = "x86"),
195 ))]
196 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
197 unsafe { write_str_simd_fastest(self.get_writer(), string) }
198 }
199 #[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))]
200 #[inline]
201 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
207 write_str_simd_avx2(self.get_writer(), string)
208 }
209
210 #[cfg(all(
211 target_feature = "sse2",
212 not(target_feature = "avx2"),
213 not(feature = "runtime-detection")
214 ))]
215 #[inline]
216 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
222 write_str_simd_sse2(self.get_writer(), string)
223 }
224
225 #[cfg(not(any(
226 all(
227 feature = "runtime-detection",
228 any(target_arch = "x86_64", target_arch = "x86")
229 ),
230 feature = "portable",
231 target_feature = "avx2",
232 target_feature = "sse2",
233 target_feature = "simd128",
234 target_arch = "aarch64",
235 )))]
236 #[inline]
237 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
243 write_string_rust(self.get_writer(), string)
244 }
245
246 #[cfg(target_arch = "aarch64")]
247 #[inline]
248 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
254 use std::arch::aarch64::{
255 uint8x16_t, vandq_u8, vceqq_u8, vdupq_n_u8, veorq_u8, vgetq_lane_u16, vld1q_u8,
256 vorrq_u8, vpaddq_u8, vreinterpretq_u16_u8,
257 };
258 use std::mem;
259
260 #[inline]
261 unsafe fn bit_mask() -> uint8x16_t {
262 unsafe {
263 mem::transmute([
264 0x01_u8, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10,
265 0x20, 0x40, 0x80,
266 ])
267 }
268 }
269
270 #[inline]
271 unsafe fn neon_movemask(input: uint8x16_t) -> u16 {
272 unsafe {
273 let simd_input: uint8x16_t = vandq_u8(input, bit_mask());
274 let tmp: uint8x16_t = vpaddq_u8(simd_input, simd_input);
275 let tmp = vpaddq_u8(tmp, tmp);
276 let tmp = vpaddq_u8(tmp, tmp);
277
278 vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0)
279 }
280 }
281
282 let writer = self.get_writer();
283 let mut idx = 0;
287 unsafe {
288 let zero = vdupq_n_u8(0);
289 let lower_quote_range = vdupq_n_u8(0x1F);
290 let quote = vdupq_n_u8(b'"');
291 let backslash = vdupq_n_u8(b'\\');
292 while string.len() - idx > 16 {
293 let data: uint8x16_t = vld1q_u8(string.as_ptr().add(idx));
295 let bs_or_quote = vorrq_u8(vceqq_u8(data, backslash), vceqq_u8(data, quote));
297 let in_quote_range = vandq_u8(data, lower_quote_range);
299 let is_unchanged = veorq_u8(data, in_quote_range);
303 let in_range = vceqq_u8(is_unchanged, zero);
304 let quote_bits = neon_movemask(vorrq_u8(bs_or_quote, in_range));
305 if quote_bits == 0 {
306 idx += 16;
307 } else {
308 let quote_dist = quote_bits.trailing_zeros() as usize;
309 stry!(writer.write_all(&string[0..idx + quote_dist]));
310 let ch = string[idx + quote_dist];
311 match ESCAPED[ch as usize] {
312 b'u' => stry!(u_encode(writer, ch)),
313 escape => stry!(writer.write_all(&[b'\\', escape])),
314 }
315
316 *string = &string[idx + quote_dist + 1..];
317 idx = 0;
318 }
319 }
320 }
321 stry!(writer.write_all(&string[0..idx]));
322 *string = &string[idx..];
323 Ok(())
324 }
325
326 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
327 #[inline]
328 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
334 let writer = self.get_writer();
335 use std::arch::wasm32::{
336 u8x16_bitmask, u8x16_eq, u8x16_splat, v128, v128_and, v128_load, v128_or, v128_xor,
337 };
338
339 let mut idx = 0;
343 let zero = u8x16_splat(0);
344 let lower_quote_range = u8x16_splat(0x1F);
345 let quote = u8x16_splat(b'"');
346 let backslash = u8x16_splat(b'\\');
347 while string.len() - idx > 16 {
348 let data = v128_load(string.as_ptr().add(idx).cast::<v128>());
350 let bs_or_quote = v128_or(u8x16_eq(data, backslash), u8x16_eq(data, quote));
352 let in_quote_range = v128_and(data, lower_quote_range);
354 let is_unchanged = v128_xor(data, in_quote_range);
358 let in_range = u8x16_eq(is_unchanged, zero);
359 let quote_bits = u8x16_bitmask(v128_or(bs_or_quote, in_range));
360 if quote_bits == 0 {
361 idx += 16;
362 } else {
363 let quote_dist = quote_bits.trailing_zeros() as usize;
364 stry!(writer.write_all(&string[0..idx + quote_dist]));
365 let ch = string[idx + quote_dist];
366 match ESCAPED[ch as usize] {
367 b'u' => stry!(u_encode(writer, ch)),
368 escape => stry!(writer.write_all(&[b'\\', escape])),
369 }
370
371 *string = &string[idx + quote_dist + 1..];
372 idx = 0;
373 }
374 }
375 stry!(writer.write_all(&string[0..idx]));
376 *string = &string[idx..];
377 Ok(())
378 }
379}
380
381#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
382unsafe fn write_str_simd_fastest<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
383where
384 W: Write,
385{
386 if std::is_x86_feature_detected!("avx2") {
420 unsafe { write_str_simd_avx2(writer, string) }
421 } else if std::is_x86_feature_detected!("sse2") {
422 unsafe { write_str_simd_sse2(writer, string) }
423 } else {
424 #[cfg(not(feature = "portable"))]
425 return write_string_rust(writer, string);
426 #[cfg(feature = "portable")]
427 return write_str_simd_portable(writer, string);
428 }
429}
430#[inline]
431fn write_string_container<W>(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()>
432where
433 W: Write,
434{
435 stry!(writer.write_all(&string[..start]));
436
437 for (index, ch) in string.iter().enumerate().skip(start) {
438 let escape = ESCAPED[*ch as usize];
439 if escape > 0 {
440 stry!(writer.write_all(&string[start..index]));
441 if escape == b'u' {
442 stry!(u_encode(writer, *ch));
443 } else {
444 stry!(writer.write_all(&[b'\\', escape]));
445 }
446 start = index + 1;
447 }
448 }
449 writer.write_all(&string[start..])
450}
451
452#[inline]
453fn write_string_rust<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
454where
455 W: Write,
456{
457 for (index, ch) in string.iter().enumerate() {
459 if ESCAPED[*ch as usize] > 0 {
460 write_string_container(writer, string, index)?;
461 *string = &string[string.len()..];
462 return Ok(());
463 }
464 }
465 writer.write_all(string)?;
466 *string = &string[string.len()..];
467 Ok(())
468}
469
470#[cfg(feature = "portable")]
471#[inline]
472unsafe fn write_str_simd_portable<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
478where
479 W: Write,
480{
481 use std::simd::{SimdPartialEq, ToBitMask, u8x32};
482
483 let mut idx = 0;
484 let zero = u8x32::splat(0);
485 let lower_quote_range = u8x32::splat(0x1F_u8);
486 let quote = u8x32::splat(b'"');
487 let backslash = u8x32::splat(b'\\');
488 while string.len() - idx >= 32 {
489 let data = u8x32::from_slice(&string[idx..]);
491 let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote);
493 let in_quote_range = data & lower_quote_range;
495 let is_unchanged = data ^ in_quote_range;
499 let in_range = is_unchanged.simd_eq(zero);
500 let quote_bits = (bs_or_quote | in_range).to_bitmask();
501 if quote_bits == 0 {
502 idx += 32;
503 } else {
504 let quote_dist = quote_bits.trailing_zeros() as usize;
505 stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
506
507 let ch = string[idx + quote_dist];
508 match ESCAPED[ch as usize] {
509 b'u' => stry!(u_encode(writer, ch)),
510 escape => stry!(writer.write_all(&[b'\\', escape])),
511 };
512
513 *string = string.get_unchecked(idx + quote_dist + 1..);
514 idx = 0;
515 }
516 }
517 stry!(writer.write_all(&string[0..idx]));
518 *string = string.get_unchecked(idx..);
519 Ok(())
520}
521
522#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
523#[target_feature(enable = "avx2")]
524#[inline]
525unsafe fn write_str_simd_avx2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
531where
532 W: Write,
533{
534 #[cfg(target_arch = "x86")]
535 use std::arch::x86::{
536 __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
537 _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
538 };
539 #[cfg(target_arch = "x86_64")]
540 use std::arch::x86_64::{
541 __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
542 _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
543 };
544
545 unsafe {
546 let mut idx = 0;
547 let zero = _mm256_set1_epi8(0);
548 let lower_quote_range = _mm256_set1_epi8(0x1F_i8);
549 #[allow(clippy::cast_possible_wrap)] let quote = _mm256_set1_epi8(b'"' as i8);
551 #[allow(clippy::cast_possible_wrap)] let backslash = _mm256_set1_epi8(b'\\' as i8);
553 while string.len() - idx >= 32 {
554 #[allow(clippy::cast_ptr_alignment)]
556 let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>());
557 let bs_or_quote = _mm256_or_si256(
559 _mm256_cmpeq_epi8(data, backslash),
560 _mm256_cmpeq_epi8(data, quote),
561 );
562 let in_quote_range = _mm256_and_si256(data, lower_quote_range);
564 let is_unchanged = _mm256_xor_si256(data, in_quote_range);
568 let in_range = _mm256_cmpeq_epi8(is_unchanged, zero);
569 let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range));
570 if quote_bits == 0 {
571 idx += 32;
572 } else {
573 let quote_dist = quote_bits.trailing_zeros() as usize;
574 stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
575
576 let ch = string[idx + quote_dist];
577 match ESCAPED[ch as usize] {
578 b'u' => stry!(u_encode(writer, ch)),
579 escape => stry!(writer.write_all(&[b'\\', escape])),
580 }
581
582 *string = string.get_unchecked(idx + quote_dist + 1..);
583 idx = 0;
584 }
585 }
586 stry!(writer.write_all(&string[0..idx]));
587 *string = string.get_unchecked(idx..);
588 }
589
590 Ok(())
591}
592
593#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
594#[target_feature(enable = "sse2")]
595#[inline]
596unsafe fn write_str_simd_sse2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
602where
603 W: Write,
604{
605 #[cfg(target_arch = "x86")]
606 use std::arch::x86::{
607 __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
608 _mm_set1_epi8, _mm_xor_si128,
609 };
610 #[cfg(target_arch = "x86_64")]
611 use std::arch::x86_64::{
612 __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
613 _mm_set1_epi8, _mm_xor_si128,
614 };
615
616 unsafe {
617 let mut idx = 0;
618 let zero = _mm_set1_epi8(0);
619 let lower_quote_range = _mm_set1_epi8(0x1F_i8);
620 #[allow(clippy::cast_possible_wrap)] let quote = _mm_set1_epi8(b'"' as i8);
622 #[allow(clippy::cast_possible_wrap)] let backslash = _mm_set1_epi8(b'\\' as i8);
624 while string.len() - idx > 16 {
625 #[allow(clippy::cast_ptr_alignment)]
627 let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>());
628 let bs_or_quote =
630 _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote));
631 let in_quote_range = _mm_and_si128(data, lower_quote_range);
633 let is_unchanged = _mm_xor_si128(data, in_quote_range);
637 let in_range = _mm_cmpeq_epi8(is_unchanged, zero);
638 let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range));
639 if quote_bits == 0 {
640 idx += 16;
641 } else {
642 let quote_dist = quote_bits.trailing_zeros() as usize;
643 stry!(writer.write_all(&string[0..idx + quote_dist]));
644
645 let ch = string[idx + quote_dist];
646 match ESCAPED[ch as usize] {
647 b'u' => stry!(u_encode(writer, ch)),
648 escape => stry!(writer.write_all(&[b'\\', escape])),
649 }
650
651 *string = &string[idx + quote_dist + 1..];
652 idx = 0;
653 }
654 }
655 stry!(writer.write_all(&string[0..idx]));
656 *string = &string[idx..];
657 }
658
659 Ok(())
660}
661
662pub struct DumpGenerator {
664 code: Vec<u8>,
665}
666
667impl Default for DumpGenerator {
668 fn default() -> Self {
669 Self {
670 code: Vec::with_capacity(1024),
671 }
672 }
673}
674
675impl DumpGenerator {
676 #[must_use]
678 pub fn new() -> Self {
679 Self::default()
680 }
681
682 #[must_use]
684 pub fn consume(self) -> String {
685 unsafe { String::from_utf8_unchecked(self.code) }
688 }
689}
690
691impl BaseGenerator for DumpGenerator {
692 type T = Vec<u8>;
693
694 #[inline]
695 fn write(&mut self, slice: &[u8]) -> io::Result<()> {
696 extend_from_slice(&mut self.code, slice);
697 Ok(())
698 }
699 #[inline]
700 fn write_char(&mut self, ch: u8) -> io::Result<()> {
701 self.code.push(ch);
702 Ok(())
703 }
704
705 #[inline]
706 fn get_writer(&mut self) -> &mut Vec<u8> {
707 &mut self.code
708 }
709
710 #[inline]
711 fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
712 self.code.push(min);
713 Ok(())
714 }
715}
716
717pub struct PrettyGenerator {
719 code: Vec<u8>,
720 dent: u16,
721 spaces_per_indent: u16,
722}
723
724impl PrettyGenerator {
725 #[must_use]
727 pub fn new(spaces: u16) -> Self {
728 Self {
729 code: Vec::with_capacity(1024),
730 dent: 0,
731 spaces_per_indent: spaces,
732 }
733 }
734
735 #[must_use]
737 pub fn consume(self) -> String {
738 unsafe { String::from_utf8_unchecked(self.code) }
739 }
740}
741
742impl BaseGenerator for PrettyGenerator {
743 type T = Vec<u8>;
744 #[inline]
745 fn write(&mut self, slice: &[u8]) -> io::Result<()> {
746 extend_from_slice(&mut self.code, slice);
747 Ok(())
748 }
749
750 #[inline]
751 fn write_char(&mut self, ch: u8) -> io::Result<()> {
752 self.code.push(ch);
753 Ok(())
754 }
755
756 #[inline]
757 fn get_writer(&mut self) -> &mut Vec<u8> {
758 &mut self.code
759 }
760
761 #[inline]
762 fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
763 extend_from_slice(&mut self.code, slice);
764 Ok(())
765 }
766
767 fn new_line(&mut self) -> io::Result<()> {
768 self.code.push(b'\n');
769 self.code.resize(
770 self.code.len() + (self.dent * self.spaces_per_indent) as usize,
771 b' ',
772 );
773 Ok(())
774 }
775
776 fn indent(&mut self) {
777 self.dent += 1;
778 }
779
780 fn dedent(&mut self) {
781 self.dent -= 1;
782 }
783}
784
785pub struct WriterGenerator<'w, W: 'w + Write> {
787 writer: &'w mut W,
788}
789
790impl<'w, W> WriterGenerator<'w, W>
791where
792 W: 'w + Write,
793{
794 pub fn new(writer: &'w mut W) -> Self {
796 WriterGenerator { writer }
797 }
798}
799
800impl<W> BaseGenerator for WriterGenerator<'_, W>
801where
802 W: Write,
803{
804 type T = W;
805
806 #[inline]
807 fn get_writer(&mut self) -> &mut W {
808 self.writer
809 }
810
811 #[inline]
812 fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
813 self.writer.write_all(&[min])
814 }
815}
816
817pub struct PrettyWriterGenerator<'w, W>
819where
820 W: 'w + Write,
821{
822 writer: &'w mut W,
823 dent: u16,
824 spaces_per_indent: u16,
825}
826
827impl<'w, W> PrettyWriterGenerator<'w, W>
828where
829 W: 'w + Write,
830{
831 pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self {
833 PrettyWriterGenerator {
834 writer,
835 dent: 0,
836 spaces_per_indent,
837 }
838 }
839}
840
841impl<W> BaseGenerator for PrettyWriterGenerator<'_, W>
842where
843 W: Write,
844{
845 type T = W;
846
847 #[inline]
848 fn get_writer(&mut self) -> &mut W {
849 self.writer
850 }
851
852 #[inline]
853 fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
854 self.writer.write_all(slice)
855 }
856
857 fn new_line(&mut self) -> io::Result<()> {
858 stry!(self.write_char(b'\n'));
859 for _ in 0..(self.dent * self.spaces_per_indent) {
860 stry!(self.write_char(b' '));
861 }
862 Ok(())
863 }
864
865 fn indent(&mut self) {
866 self.dent += 1;
867 }
868
869 fn dedent(&mut self) {
870 self.dent -= 1;
871 }
872}
873
874#[inline]
879pub(crate) fn extend_from_slice(dst: &mut Vec<u8>, src: &[u8]) {
880 let dst_len = dst.len();
881 let src_len = src.len();
882
883 dst.reserve(src_len);
884
885 unsafe {
886 ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len);
888 dst.set_len(dst_len + src_len);
889 }
890}
891
892#[cfg(test)]
893mod tests {
894
895 #[test]
896 fn test_write_string_rust() {
897 let mut writer = Vec::new();
898 let mut string = "Hello, World!".as_bytes();
899 super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
900 assert_eq!(writer, "Hello, World!".as_bytes());
901 assert!(string.is_empty(), "write_string_rust should advance string");
902 }
903 #[test]
904 fn test_write_string_rust2() {
905 let mut writer = Vec::new();
906 let mut string = "Hello, \"World!\"".as_bytes();
907 super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
908 assert_eq!(writer, "Hello, \\\"World!\\\"".as_bytes());
909 assert!(string.is_empty(), "write_string_rust should advance string");
910 }
911
912 #[test]
913 fn test_write_string_long_with_escapes() {
914 use super::{BaseGenerator, DumpGenerator};
915 let input = "abcdefghijklmnopqrstuvwxyz012345\"escape here\"";
919 let mut g = DumpGenerator::new();
920 g.write_string(input).expect("write failed");
921 assert_eq!(
922 g.consume(),
923 "\"abcdefghijklmnopqrstuvwxyz012345\\\"escape here\\\"\""
924 );
925 }
926 #[test]
927 fn test_write_float_non_finite_is_null() {
928 use super::BaseGenerator;
929 for f in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
930 let mut g = super::DumpGenerator::new();
931 g.write_float(f).expect("failed to write float");
932 assert_eq!(g.consume(), "null", "{f}");
933 }
934 }
935}