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 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 #[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 #[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 unsafe { write_str_simd_fastest(self.get_writer(), string) }
194 }
195 #[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))]
196 #[inline]
197 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 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
218 write_str_simd_sse2(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 = "sse2",
229 target_feature = "simd128",
230 target_arch = "aarch64",
231 )))]
232 #[inline]
233 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
239 write_string_rust(self.get_writer(), string)
240 }
241
242 #[cfg(target_arch = "aarch64")]
243 #[inline]
244 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
250 use std::arch::aarch64::{
251 uint8x16_t, vandq_u8, vceqq_u8, vdupq_n_u8, veorq_u8, vgetq_lane_u16, vld1q_u8,
252 vorrq_u8, vpaddq_u8, vreinterpretq_u16_u8,
253 };
254 use std::mem;
255
256 #[inline]
257 unsafe fn bit_mask() -> uint8x16_t {
258 unsafe {
259 mem::transmute([
260 0x01_u8, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10,
261 0x20, 0x40, 0x80,
262 ])
263 }
264 }
265
266 #[inline]
267 unsafe fn neon_movemask(input: uint8x16_t) -> u16 {
268 unsafe {
269 let simd_input: uint8x16_t = vandq_u8(input, bit_mask());
270 let tmp: uint8x16_t = vpaddq_u8(simd_input, simd_input);
271 let tmp = vpaddq_u8(tmp, tmp);
272 let tmp = vpaddq_u8(tmp, tmp);
273
274 vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0)
275 }
276 }
277
278 let writer = self.get_writer();
279 let mut idx = 0;
283 unsafe {
284 let zero = vdupq_n_u8(0);
285 let lower_quote_range = vdupq_n_u8(0x1F);
286 let quote = vdupq_n_u8(b'"');
287 let backslash = vdupq_n_u8(b'\\');
288 while string.len() - idx > 16 {
289 let data: uint8x16_t = vld1q_u8(string.as_ptr().add(idx));
291 let bs_or_quote = vorrq_u8(vceqq_u8(data, backslash), vceqq_u8(data, quote));
293 let in_quote_range = vandq_u8(data, lower_quote_range);
295 let is_unchanged = veorq_u8(data, in_quote_range);
299 let in_range = vceqq_u8(is_unchanged, zero);
300 let quote_bits = neon_movemask(vorrq_u8(bs_or_quote, in_range));
301 if quote_bits == 0 {
302 idx += 16;
303 } else {
304 let quote_dist = quote_bits.trailing_zeros() as usize;
305 stry!(writer.write_all(&string[0..idx + quote_dist]));
306 let ch = string[idx + quote_dist];
307 match ESCAPED[ch as usize] {
308 b'u' => stry!(u_encode(writer, ch)),
309 escape => stry!(writer.write_all(&[b'\\', escape])),
310 }
311
312 *string = &string[idx + quote_dist + 1..];
313 idx = 0;
314 }
315 }
316 }
317 stry!(writer.write_all(&string[0..idx]));
318 *string = &string[idx..];
319 Ok(())
320 }
321
322 #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
323 #[inline]
324 unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
330 let writer = self.get_writer();
331 use std::arch::wasm32::{
332 u8x16_bitmask, u8x16_eq, u8x16_splat, v128, v128_and, v128_load, v128_or, v128_xor,
333 };
334
335 let mut idx = 0;
339 let zero = u8x16_splat(0);
340 let lower_quote_range = u8x16_splat(0x1F);
341 let quote = u8x16_splat(b'"');
342 let backslash = u8x16_splat(b'\\');
343 while string.len() - idx > 16 {
344 let data = v128_load(string.as_ptr().add(idx).cast::<v128>());
346 let bs_or_quote = v128_or(u8x16_eq(data, backslash), u8x16_eq(data, quote));
348 let in_quote_range = v128_and(data, lower_quote_range);
350 let is_unchanged = v128_xor(data, in_quote_range);
354 let in_range = u8x16_eq(is_unchanged, zero);
355 let quote_bits = u8x16_bitmask(v128_or(bs_or_quote, in_range));
356 if quote_bits == 0 {
357 idx += 16;
358 } else {
359 let quote_dist = quote_bits.trailing_zeros() as usize;
360 stry!(writer.write_all(&string[0..idx + quote_dist]));
361 let ch = string[idx + quote_dist];
362 match ESCAPED[ch as usize] {
363 b'u' => stry!(u_encode(writer, ch)),
364 escape => stry!(writer.write_all(&[b'\\', escape])),
365 }
366
367 *string = &string[idx + quote_dist + 1..];
368 idx = 0;
369 }
370 }
371 stry!(writer.write_all(&string[0..idx]));
372 *string = &string[idx..];
373 Ok(())
374 }
375}
376
377#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
378unsafe fn write_str_simd_fastest<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
379where
380 W: Write,
381{
382 if std::is_x86_feature_detected!("avx2") {
416 unsafe { write_str_simd_avx2(writer, string) }
417 } else if std::is_x86_feature_detected!("sse2") {
418 unsafe { write_str_simd_sse2(writer, string) }
419 } else {
420 #[cfg(not(feature = "portable"))]
421 return write_string_rust(writer, string);
422 #[cfg(feature = "portable")]
423 return write_str_simd_portable(writer, string);
424 }
425}
426#[inline]
427fn write_string_container<W>(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()>
428where
429 W: Write,
430{
431 stry!(writer.write_all(&string[..start]));
432
433 for (index, ch) in string.iter().enumerate().skip(start) {
434 let escape = ESCAPED[*ch as usize];
435 if escape > 0 {
436 stry!(writer.write_all(&string[start..index]));
437 if escape == b'u' {
438 stry!(u_encode(writer, *ch));
439 } else {
440 stry!(writer.write_all(&[b'\\', escape]));
441 }
442 start = index + 1;
443 }
444 }
445 writer.write_all(&string[start..])
446}
447
448#[inline]
449fn write_string_rust<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
450where
451 W: Write,
452{
453 for (index, ch) in string.iter().enumerate() {
455 if ESCAPED[*ch as usize] > 0 {
456 write_string_container(writer, string, index)?;
457 *string = &string[string.len()..];
458 return Ok(());
459 }
460 }
461 writer.write_all(string)?;
462 *string = &string[string.len()..];
463 Ok(())
464}
465
466#[cfg(feature = "portable")]
467#[inline]
468unsafe fn write_str_simd_portable<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
474where
475 W: Write,
476{
477 use std::simd::{SimdPartialEq, ToBitMask, u8x32};
478
479 let mut idx = 0;
480 let zero = u8x32::splat(0);
481 let lower_quote_range = u8x32::splat(0x1F_u8);
482 let quote = u8x32::splat(b'"');
483 let backslash = u8x32::splat(b'\\');
484 while string.len() - idx >= 32 {
485 let data = u8x32::from_slice(&string[idx..]);
487 let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote);
489 let in_quote_range = data & lower_quote_range;
491 let is_unchanged = data ^ in_quote_range;
495 let in_range = is_unchanged.simd_eq(zero);
496 let quote_bits = (bs_or_quote | in_range).to_bitmask();
497 if quote_bits == 0 {
498 idx += 32;
499 } else {
500 let quote_dist = quote_bits.trailing_zeros() as usize;
501 stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
502
503 let ch = string[idx + quote_dist];
504 match ESCAPED[ch as usize] {
505 b'u' => stry!(u_encode(writer, ch)),
506 escape => stry!(writer.write_all(&[b'\\', escape])),
507 };
508
509 *string = string.get_unchecked(idx + quote_dist + 1..);
510 idx = 0;
511 }
512 }
513 stry!(writer.write_all(&string[0..idx]));
514 *string = string.get_unchecked(idx..);
515 Ok(())
516}
517
518#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
519#[target_feature(enable = "avx2")]
520#[inline]
521unsafe fn write_str_simd_avx2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
527where
528 W: Write,
529{
530 #[cfg(target_arch = "x86")]
531 use std::arch::x86::{
532 __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
533 _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
534 };
535 #[cfg(target_arch = "x86_64")]
536 use std::arch::x86_64::{
537 __m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
538 _mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
539 };
540
541 unsafe {
542 let mut idx = 0;
543 let zero = _mm256_set1_epi8(0);
544 let lower_quote_range = _mm256_set1_epi8(0x1F_i8);
545 #[allow(clippy::cast_possible_wrap)] let quote = _mm256_set1_epi8(b'"' as i8);
547 #[allow(clippy::cast_possible_wrap)] let backslash = _mm256_set1_epi8(b'\\' as i8);
549 while string.len() - idx >= 32 {
550 #[allow(clippy::cast_ptr_alignment)]
552 let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>());
553 let bs_or_quote = _mm256_or_si256(
555 _mm256_cmpeq_epi8(data, backslash),
556 _mm256_cmpeq_epi8(data, quote),
557 );
558 let in_quote_range = _mm256_and_si256(data, lower_quote_range);
560 let is_unchanged = _mm256_xor_si256(data, in_quote_range);
564 let in_range = _mm256_cmpeq_epi8(is_unchanged, zero);
565 let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range));
566 if quote_bits == 0 {
567 idx += 32;
568 } else {
569 let quote_dist = quote_bits.trailing_zeros() as usize;
570 stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
571
572 let ch = string[idx + quote_dist];
573 match ESCAPED[ch as usize] {
574 b'u' => stry!(u_encode(writer, ch)),
575 escape => stry!(writer.write_all(&[b'\\', escape])),
576 }
577
578 *string = string.get_unchecked(idx + quote_dist + 1..);
579 idx = 0;
580 }
581 }
582 stry!(writer.write_all(&string[0..idx]));
583 *string = string.get_unchecked(idx..);
584 }
585
586 Ok(())
587}
588
589#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
590#[target_feature(enable = "sse2")]
591#[inline]
592unsafe fn write_str_simd_sse2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
598where
599 W: Write,
600{
601 #[cfg(target_arch = "x86")]
602 use std::arch::x86::{
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 #[cfg(target_arch = "x86_64")]
607 use std::arch::x86_64::{
608 __m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
609 _mm_set1_epi8, _mm_xor_si128,
610 };
611
612 unsafe {
613 let mut idx = 0;
614 let zero = _mm_set1_epi8(0);
615 let lower_quote_range = _mm_set1_epi8(0x1F_i8);
616 #[allow(clippy::cast_possible_wrap)] let quote = _mm_set1_epi8(b'"' as i8);
618 #[allow(clippy::cast_possible_wrap)] let backslash = _mm_set1_epi8(b'\\' as i8);
620 while string.len() - idx > 16 {
621 #[allow(clippy::cast_ptr_alignment)]
623 let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>());
624 let bs_or_quote =
626 _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote));
627 let in_quote_range = _mm_and_si128(data, lower_quote_range);
629 let is_unchanged = _mm_xor_si128(data, in_quote_range);
633 let in_range = _mm_cmpeq_epi8(is_unchanged, zero);
634 let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range));
635 if quote_bits == 0 {
636 idx += 16;
637 } else {
638 let quote_dist = quote_bits.trailing_zeros() as usize;
639 stry!(writer.write_all(&string[0..idx + quote_dist]));
640
641 let ch = string[idx + quote_dist];
642 match ESCAPED[ch as usize] {
643 b'u' => stry!(u_encode(writer, ch)),
644 escape => stry!(writer.write_all(&[b'\\', escape])),
645 }
646
647 *string = &string[idx + quote_dist + 1..];
648 idx = 0;
649 }
650 }
651 stry!(writer.write_all(&string[0..idx]));
652 *string = &string[idx..];
653 }
654
655 Ok(())
656}
657
658pub struct DumpGenerator {
660 code: Vec<u8>,
661}
662
663impl Default for DumpGenerator {
664 fn default() -> Self {
665 Self {
666 code: Vec::with_capacity(1024),
667 }
668 }
669}
670
671impl DumpGenerator {
672 #[must_use]
674 pub fn new() -> Self {
675 Self::default()
676 }
677
678 #[must_use]
680 pub fn consume(self) -> String {
681 unsafe { String::from_utf8_unchecked(self.code) }
684 }
685}
686
687impl BaseGenerator for DumpGenerator {
688 type T = Vec<u8>;
689
690 #[inline]
691 fn write(&mut self, slice: &[u8]) -> io::Result<()> {
692 extend_from_slice(&mut self.code, slice);
693 Ok(())
694 }
695 #[inline]
696 fn write_char(&mut self, ch: u8) -> io::Result<()> {
697 self.code.push(ch);
698 Ok(())
699 }
700
701 #[inline]
702 fn get_writer(&mut self) -> &mut Vec<u8> {
703 &mut self.code
704 }
705
706 #[inline]
707 fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
708 self.code.push(min);
709 Ok(())
710 }
711}
712
713pub struct PrettyGenerator {
715 code: Vec<u8>,
716 dent: u16,
717 spaces_per_indent: u16,
718}
719
720impl PrettyGenerator {
721 #[must_use]
723 pub fn new(spaces: u16) -> Self {
724 Self {
725 code: Vec::with_capacity(1024),
726 dent: 0,
727 spaces_per_indent: spaces,
728 }
729 }
730
731 #[must_use]
733 pub fn consume(self) -> String {
734 unsafe { String::from_utf8_unchecked(self.code) }
735 }
736}
737
738impl BaseGenerator for PrettyGenerator {
739 type T = Vec<u8>;
740 #[inline]
741 fn write(&mut self, slice: &[u8]) -> io::Result<()> {
742 extend_from_slice(&mut self.code, slice);
743 Ok(())
744 }
745
746 #[inline]
747 fn write_char(&mut self, ch: u8) -> io::Result<()> {
748 self.code.push(ch);
749 Ok(())
750 }
751
752 #[inline]
753 fn get_writer(&mut self) -> &mut Vec<u8> {
754 &mut self.code
755 }
756
757 #[inline]
758 fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
759 extend_from_slice(&mut self.code, slice);
760 Ok(())
761 }
762
763 fn new_line(&mut self) -> io::Result<()> {
764 self.code.push(b'\n');
765 self.code.resize(
766 self.code.len() + (self.dent * self.spaces_per_indent) as usize,
767 b' ',
768 );
769 Ok(())
770 }
771
772 fn indent(&mut self) {
773 self.dent += 1;
774 }
775
776 fn dedent(&mut self) {
777 self.dent -= 1;
778 }
779}
780
781pub struct WriterGenerator<'w, W: 'w + Write> {
783 writer: &'w mut W,
784}
785
786impl<'w, W> WriterGenerator<'w, W>
787where
788 W: 'w + Write,
789{
790 pub fn new(writer: &'w mut W) -> Self {
792 WriterGenerator { writer }
793 }
794}
795
796impl<W> BaseGenerator for WriterGenerator<'_, W>
797where
798 W: Write,
799{
800 type T = W;
801
802 #[inline]
803 fn get_writer(&mut self) -> &mut W {
804 self.writer
805 }
806
807 #[inline]
808 fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
809 self.writer.write_all(&[min])
810 }
811}
812
813pub struct PrettyWriterGenerator<'w, W>
815where
816 W: 'w + Write,
817{
818 writer: &'w mut W,
819 dent: u16,
820 spaces_per_indent: u16,
821}
822
823impl<'w, W> PrettyWriterGenerator<'w, W>
824where
825 W: 'w + Write,
826{
827 pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self {
829 PrettyWriterGenerator {
830 writer,
831 dent: 0,
832 spaces_per_indent,
833 }
834 }
835}
836
837impl<W> BaseGenerator for PrettyWriterGenerator<'_, W>
838where
839 W: Write,
840{
841 type T = W;
842
843 #[inline]
844 fn get_writer(&mut self) -> &mut W {
845 self.writer
846 }
847
848 #[inline]
849 fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
850 self.writer.write_all(slice)
851 }
852
853 fn new_line(&mut self) -> io::Result<()> {
854 stry!(self.write_char(b'\n'));
855 for _ in 0..(self.dent * self.spaces_per_indent) {
856 stry!(self.write_char(b' '));
857 }
858 Ok(())
859 }
860
861 fn indent(&mut self) {
862 self.dent += 1;
863 }
864
865 fn dedent(&mut self) {
866 self.dent -= 1;
867 }
868}
869
870#[inline]
875pub(crate) fn extend_from_slice(dst: &mut Vec<u8>, src: &[u8]) {
876 let dst_len = dst.len();
877 let src_len = src.len();
878
879 dst.reserve(src_len);
880
881 unsafe {
882 ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len);
884 dst.set_len(dst_len + src_len);
885 }
886}
887
888#[cfg(test)]
889mod tests {
890
891 #[test]
892 fn test_write_string_rust() {
893 let mut writer = Vec::new();
894 let mut string = "Hello, World!".as_bytes();
895 super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
896 assert_eq!(writer, "Hello, World!".as_bytes());
897 assert!(string.is_empty(), "write_string_rust should advance string");
898 }
899 #[test]
900 fn test_write_string_rust2() {
901 let mut writer = Vec::new();
902 let mut string = "Hello, \"World!\"".as_bytes();
903 super::write_string_rust(&mut writer, &mut string).expect("failed to write string");
904 assert_eq!(writer, "Hello, \\\"World!\\\"".as_bytes());
905 assert!(string.is_empty(), "write_string_rust should advance string");
906 }
907
908 #[test]
909 fn test_write_string_long_with_escapes() {
910 use super::{BaseGenerator, DumpGenerator};
911 let input = "abcdefghijklmnopqrstuvwxyz012345\"escape here\"";
915 let mut g = DumpGenerator::new();
916 g.write_string(input).expect("write failed");
917 assert_eq!(
918 g.consume(),
919 "\"abcdefghijklmnopqrstuvwxyz012345\\\"escape here\\\"\""
920 );
921 }
922}