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 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_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 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 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 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 let data: uint8x16_t = vld1q_u8(string.as_ptr().add(idx));
293 let bs_or_quote = vorrq_u8(vceqq_u8(data, backslash), vceqq_u8(data, quote));
295 let in_quote_range = vandq_u8(data, lower_quote_range);
297 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 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 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 let data = v128_load(string.as_ptr().add(idx).cast::<v128>());
348 let bs_or_quote = v128_or(u8x16_eq(data, backslash), u8x16_eq(data, quote));
350 let in_quote_range = v128_and(data, lower_quote_range);
352 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 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 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]
466unsafe 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 let data = u8x32::from_slice(&string[idx..]);
485 let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote);
487 let in_quote_range = data & lower_quote_range;
489 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]
519unsafe 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)] let quote = _mm256_set1_epi8(b'"' as i8);
544 #[allow(clippy::cast_possible_wrap)] let backslash = _mm256_set1_epi8(b'\\' as i8);
546 while string.len() - idx >= 32 {
547 #[allow(clippy::cast_ptr_alignment)]
549 let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>());
550 let bs_or_quote = _mm256_or_si256(
552 _mm256_cmpeq_epi8(data, backslash),
553 _mm256_cmpeq_epi8(data, quote),
554 );
555 let in_quote_range = _mm256_and_si256(data, lower_quote_range);
557 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]
587unsafe 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)] let quote = _mm_set1_epi8(b'"' as i8);
612 #[allow(clippy::cast_possible_wrap)] let backslash = _mm_set1_epi8(b'\\' as i8);
614 while string.len() - idx > 16 {
615 #[allow(clippy::cast_ptr_alignment)]
617 let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>());
618 let bs_or_quote =
620 _mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote));
621 let in_quote_range = _mm_and_si128(data, lower_quote_range);
623 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
650pub 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 #[must_use]
666 pub fn new() -> Self {
667 Self::default()
668 }
669
670 #[must_use]
672 pub fn consume(self) -> String {
673 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
705pub struct PrettyGenerator {
707 code: Vec<u8>,
708 dent: u16,
709 spaces_per_indent: u16,
710}
711
712impl PrettyGenerator {
713 #[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 #[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
773pub 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 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
805pub 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 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#[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 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}