macro_rules! stry {
($e:expr) => {
match $e {
::std::result::Result::Ok(val) => val,
::std::result::Result::Err(err) => return ::std::result::Result::Err(err),
}
};
}
use std::io;
use std::io::Write;
use std::ptr;
const QU: u8 = b'"';
const BS: u8 = b'\\';
const BB: u8 = b'b';
const TT: u8 = b't';
const NN: u8 = b'n';
const FF: u8 = b'f';
const RR: u8 = b'r';
const UU: u8 = b'u';
const __: u8 = 0;
pub(crate) static ESCAPED: [u8; 256] = [
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, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ];
#[inline(never)]
fn u_encode<W>(w: &mut W, byte: u8) -> io::Result<()>
where
W: Write,
{
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let bytes = [
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
];
w.write_all(&bytes)
}
pub trait BaseGenerator {
type T: Write;
fn get_writer(&mut self) -> &mut Self::T;
#[inline]
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
self.get_writer().write_all(slice)
}
#[inline]
fn write_char(&mut self, ch: u8) -> io::Result<()> {
self.get_writer().write_all(&[ch])
}
fn write_min(&mut self, slice: &[u8], min: u8) -> io::Result<()>;
#[inline]
fn new_line(&mut self) -> io::Result<()> {
Ok(())
}
#[inline]
fn indent(&mut self) {}
#[inline]
fn dedent(&mut self) {}
#[inline]
fn write_string(&mut self, string: &str) -> io::Result<()> {
stry!(self.write_char(b'"'));
stry!(self.write_string_content(string));
self.write_char(b'"')
}
#[inline]
fn write_string_content(&mut self, string: &str) -> io::Result<()> {
let mut string = string.as_bytes();
unsafe {
stry!(self.write_str_simd(&mut string));
}
write_string_rust(self.get_writer(), &mut string)
}
#[inline]
fn write_simple_string(&mut self, string: &str) -> io::Result<()> {
self.write(br#"""#)?;
write_string_rust(self.get_writer(), &mut string.as_bytes())?;
self.write(br#"""#)
}
#[inline]
fn write_simple_str_content(&mut self, string: &str) -> io::Result<()> {
let mut string = string.as_bytes();
write_string_rust(self.get_writer(), &mut string)
}
#[inline]
fn write_float(&mut self, num: f64) -> io::Result<()> {
let mut buffer = ryu::Buffer::new();
let s = buffer.format_finite(num);
self.get_writer().write_all(s.as_bytes())
}
#[inline]
fn write_int<I: itoa::Integer>(&mut self, num: I) -> io::Result<()> {
let mut buffer = itoa::Buffer::new();
let s = buffer.format(num);
self.get_writer().write_all(s.as_bytes())
}
#[cfg(all(
feature = "runtime-detection",
any(target_arch = "x86_64", target_arch = "x86"),
))]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
write_str_simd_fastest(self.get_writer(), string)
}
#[cfg(all(target_feature = "avx2", not(feature = "runtime-detection")))]
#[inline]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
write_str_simd_avx2(self.get_writer(), string)
}
#[cfg(all(
target_feature = "sse2",
not(target_feature = "avx2"),
not(feature = "runtime-detection")
))]
#[inline]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
write_str_simd_sse42(self.get_writer(), string)
}
#[cfg(not(any(
all(
feature = "runtime-detection",
any(target_arch = "x86_64", target_arch = "x86")
),
feature = "portable",
target_feature = "avx2",
target_feature = "sse4.2",
target_feature = "simd128",
target_arch = "aarch64",
)))]
#[inline]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
self.write_simple_string(std::str::from_utf8_unchecked(string))
}
#[cfg(target_arch = "aarch64")]
#[inline]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
use std::arch::aarch64::{
uint8x16_t, vandq_u8, vceqq_u8, vdupq_n_u8, veorq_u8, vgetq_lane_u16, vld1q_u8,
vorrq_u8, vpaddq_u8, vreinterpretq_u16_u8,
};
use std::mem;
#[inline]
unsafe fn bit_mask() -> uint8x16_t {
mem::transmute([
0x01_u8, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20,
0x40, 0x80,
])
}
#[inline]
unsafe fn neon_movemask(input: uint8x16_t) -> u16 {
let simd_input: uint8x16_t = vandq_u8(input, bit_mask());
let tmp: uint8x16_t = vpaddq_u8(simd_input, simd_input);
let tmp = vpaddq_u8(tmp, tmp);
let tmp = vpaddq_u8(tmp, tmp);
vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0)
}
let writer = self.get_writer();
let mut idx = 0;
let zero = vdupq_n_u8(0);
let lower_quote_range = vdupq_n_u8(0x1F);
let quote = vdupq_n_u8(b'"');
let backslash = vdupq_n_u8(b'\\');
while string.len() - idx > 16 {
let data: uint8x16_t = vld1q_u8(string.as_ptr().add(idx));
let bs_or_quote = vorrq_u8(vceqq_u8(data, backslash), vceqq_u8(data, quote));
let in_quote_range = vandq_u8(data, lower_quote_range);
let is_unchanged = veorq_u8(data, in_quote_range);
let in_range = vceqq_u8(is_unchanged, zero);
let quote_bits = neon_movemask(vorrq_u8(bs_or_quote, in_range));
if quote_bits == 0 {
idx += 16;
} else {
let quote_dist = quote_bits.trailing_zeros() as usize;
stry!(writer.write_all(&string[0..idx + quote_dist]));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(u_encode(writer, ch)),
escape => stry!(writer.write_all(&[b'\\', escape])),
}
*string = &string[idx + quote_dist + 1..];
idx = 0;
}
}
stry!(writer.write_all(&string[0..idx]));
*string = &string[idx..];
Ok(())
}
#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
#[inline]
unsafe fn write_str_simd(&mut self, string: &mut &[u8]) -> io::Result<()> {
let writer = self.get_writer();
use std::arch::wasm32::{
u8x16_bitmask, u8x16_eq, u8x16_splat, v128, v128_and, v128_load, v128_or, v128_xor,
};
let mut idx = 0;
let zero = u8x16_splat(0);
let lower_quote_range = u8x16_splat(0x1F);
let quote = u8x16_splat(b'"');
let backslash = u8x16_splat(b'\\');
while string.len() - idx > 16 {
let data = v128_load(string.as_ptr().add(idx).cast::<v128>());
let bs_or_quote = v128_or(u8x16_eq(data, backslash), u8x16_eq(data, quote));
let in_quote_range = v128_and(data, lower_quote_range);
let is_unchanged = v128_xor(data, in_quote_range);
let in_range = u8x16_eq(is_unchanged, zero);
let quote_bits = u8x16_bitmask(v128_or(bs_or_quote, in_range));
if quote_bits == 0 {
idx += 16;
} else {
let quote_dist = quote_bits.trailing_zeros() as usize;
stry!(writer.write_all(&string[0..idx + quote_dist]));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(u_encode(writer, ch)),
escape => stry!(writer.write_all(&[b'\\', escape])),
}
*string = &string[idx + quote_dist + 1..];
idx = 0;
}
}
stry!(writer.write_all(&string[0..idx]));
*string = &string[idx..];
Ok(())
}
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
unsafe fn write_str_simd_fastest<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
where
W: Write,
{
if std::is_x86_feature_detected!("avx2") {
write_str_simd_avx2(writer, string)
} else if std::is_x86_feature_detected!("sse4.2") {
write_str_simd_sse42(writer, string)
} else {
#[cfg(not(feature = "portable"))]
let r = write_string_rust(writer, string);
#[cfg(feature = "portable")]
let r = write_str_simd_portable(writer, string);
r
}
}
#[inline]
fn write_string_container<W>(writer: &mut W, string: &[u8], mut start: usize) -> io::Result<()>
where
W: Write,
{
stry!(writer.write_all(&string[..start]));
for (index, ch) in string.iter().enumerate().skip(start) {
let escape = ESCAPED[*ch as usize];
if escape > 0 {
stry!(writer.write_all(&string[start..index]));
if escape == b'u' {
stry!(u_encode(writer, *ch));
} else {
stry!(writer.write_all(&[b'\\', escape]));
};
start = index + 1;
}
}
writer.write_all(&string[start..])
}
#[inline]
fn write_string_rust<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
where
W: Write,
{
for (index, ch) in string.iter().enumerate() {
if ESCAPED[*ch as usize] > 0 {
return write_string_container(writer, string, index);
}
}
writer.write_all(string)
}
#[cfg(feature = "portable")]
#[inline]
unsafe fn write_str_simd_portable<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
where
W: Write,
{
use std::simd::{u8x32, SimdPartialEq, ToBitMask};
let mut idx = 0;
let zero = u8x32::splat(0);
let lower_quote_range = u8x32::splat(0x1F_u8);
let quote = u8x32::splat(b'"');
let backslash = u8x32::splat(b'\\');
while string.len() - idx >= 32 {
let data = u8x32::from_slice(&string[idx..]);
let bs_or_quote = data.simd_eq(backslash) | data.simd_eq(quote);
let in_quote_range = data & lower_quote_range;
let is_unchanged = data ^ in_quote_range;
let in_range = is_unchanged.simd_eq(zero);
let quote_bits = (bs_or_quote | in_range).to_bitmask();
if quote_bits == 0 {
idx += 32;
} else {
let quote_dist = quote_bits.trailing_zeros() as usize;
stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(u_encode(writer, ch)),
escape => stry!(writer.write_all(&[b'\\', escape])),
};
*string = string.get_unchecked(idx + quote_dist + 1..);
idx = 0;
}
}
stry!(writer.write_all(&string[0..idx]));
*string = string.get_unchecked(idx..);
Ok(())
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn write_str_simd_avx2<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
where
W: Write,
{
#[cfg(target_arch = "x86")]
use std::arch::x86::{
__m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
_mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
};
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::{
__m256i, _mm256_and_si256, _mm256_cmpeq_epi8, _mm256_loadu_si256, _mm256_movemask_epi8,
_mm256_or_si256, _mm256_set1_epi8, _mm256_xor_si256,
};
let mut idx = 0;
let zero = _mm256_set1_epi8(0);
let lower_quote_range = _mm256_set1_epi8(0x1F_i8);
#[allow(clippy::cast_possible_wrap)] let quote = _mm256_set1_epi8(b'"' as i8);
#[allow(clippy::cast_possible_wrap)] let backslash = _mm256_set1_epi8(b'\\' as i8);
while string.len() - idx >= 32 {
#[allow(clippy::cast_ptr_alignment)]
let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx).cast::<__m256i>());
let bs_or_quote = _mm256_or_si256(
_mm256_cmpeq_epi8(data, backslash),
_mm256_cmpeq_epi8(data, quote),
);
let in_quote_range = _mm256_and_si256(data, lower_quote_range);
let is_unchanged = _mm256_xor_si256(data, in_quote_range);
let in_range = _mm256_cmpeq_epi8(is_unchanged, zero);
let quote_bits = _mm256_movemask_epi8(_mm256_or_si256(bs_or_quote, in_range));
if quote_bits == 0 {
idx += 32;
} else {
let quote_dist = quote_bits.trailing_zeros() as usize;
stry!(writer.write_all(string.get_unchecked(0..idx + quote_dist)));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(u_encode(writer, ch)),
escape => stry!(writer.write_all(&[b'\\', escape])),
};
*string = string.get_unchecked(idx + quote_dist + 1..);
idx = 0;
}
}
stry!(writer.write_all(&string[0..idx]));
*string = string.get_unchecked(idx..);
Ok(())
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
#[target_feature(enable = "sse4.2")]
#[inline]
unsafe fn write_str_simd_sse42<W>(writer: &mut W, string: &mut &[u8]) -> io::Result<()>
where
W: Write,
{
#[cfg(target_arch = "x86")]
use std::arch::x86::{
__m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
_mm_set1_epi8, _mm_xor_si128,
};
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::{
__m128i, _mm_and_si128, _mm_cmpeq_epi8, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128,
_mm_set1_epi8, _mm_xor_si128,
};
let mut idx = 0;
let zero = _mm_set1_epi8(0);
let lower_quote_range = _mm_set1_epi8(0x1F_i8);
#[allow(clippy::cast_possible_wrap)] let quote = _mm_set1_epi8(b'"' as i8);
#[allow(clippy::cast_possible_wrap)] let backslash = _mm_set1_epi8(b'\\' as i8);
while string.len() - idx > 16 {
#[allow(clippy::cast_ptr_alignment)]
let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx).cast::<__m128i>());
let bs_or_quote =
_mm_or_si128(_mm_cmpeq_epi8(data, backslash), _mm_cmpeq_epi8(data, quote));
let in_quote_range = _mm_and_si128(data, lower_quote_range);
let is_unchanged = _mm_xor_si128(data, in_quote_range);
let in_range = _mm_cmpeq_epi8(is_unchanged, zero);
let quote_bits = _mm_movemask_epi8(_mm_or_si128(bs_or_quote, in_range));
if quote_bits == 0 {
idx += 16;
} else {
let quote_dist = quote_bits.trailing_zeros() as usize;
stry!(writer.write_all(&string[0..idx + quote_dist]));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(u_encode(writer, ch)),
escape => stry!(writer.write_all(&[b'\\', escape])),
}
*string = &string[idx + quote_dist + 1..];
idx = 0;
}
}
stry!(writer.write_all(&string[0..idx]));
*string = &string[idx..];
Ok(())
}
pub struct DumpGenerator {
code: Vec<u8>,
}
impl Default for DumpGenerator {
fn default() -> Self {
Self {
code: Vec::with_capacity(1024),
}
}
}
impl DumpGenerator {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn consume(self) -> String {
unsafe { String::from_utf8_unchecked(self.code) }
}
}
impl BaseGenerator for DumpGenerator {
type T = Vec<u8>;
#[inline]
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
extend_from_slice(&mut self.code, slice);
Ok(())
}
#[inline]
fn write_char(&mut self, ch: u8) -> io::Result<()> {
self.code.push(ch);
Ok(())
}
#[inline]
fn get_writer(&mut self) -> &mut Vec<u8> {
&mut self.code
}
#[inline]
fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
self.code.push(min);
Ok(())
}
}
pub struct PrettyGenerator {
code: Vec<u8>,
dent: u16,
spaces_per_indent: u16,
}
impl PrettyGenerator {
#[must_use]
pub fn new(spaces: u16) -> Self {
Self {
code: Vec::with_capacity(1024),
dent: 0,
spaces_per_indent: spaces,
}
}
#[must_use]
pub fn consume(self) -> String {
unsafe { String::from_utf8_unchecked(self.code) }
}
}
impl BaseGenerator for PrettyGenerator {
type T = Vec<u8>;
#[inline]
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
extend_from_slice(&mut self.code, slice);
Ok(())
}
#[inline]
fn write_char(&mut self, ch: u8) -> io::Result<()> {
self.code.push(ch);
Ok(())
}
#[inline]
fn get_writer(&mut self) -> &mut Vec<u8> {
&mut self.code
}
#[inline]
fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
extend_from_slice(&mut self.code, slice);
Ok(())
}
fn new_line(&mut self) -> io::Result<()> {
self.code.push(b'\n');
self.code.resize(
self.code.len() + (self.dent * self.spaces_per_indent) as usize,
b' ',
);
Ok(())
}
fn indent(&mut self) {
self.dent += 1;
}
fn dedent(&mut self) {
self.dent -= 1;
}
}
pub struct WriterGenerator<'w, W: 'w + Write> {
writer: &'w mut W,
}
impl<'w, W> WriterGenerator<'w, W>
where
W: 'w + Write,
{
pub fn new(writer: &'w mut W) -> Self {
WriterGenerator { writer }
}
}
impl<'w, W> BaseGenerator for WriterGenerator<'w, W>
where
W: Write,
{
type T = W;
#[inline]
fn get_writer(&mut self) -> &mut W {
self.writer
}
#[inline]
fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
self.writer.write_all(&[min])
}
}
pub struct PrettyWriterGenerator<'w, W>
where
W: 'w + Write,
{
writer: &'w mut W,
dent: u16,
spaces_per_indent: u16,
}
impl<'w, W> PrettyWriterGenerator<'w, W>
where
W: 'w + Write,
{
pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self {
PrettyWriterGenerator {
writer,
dent: 0,
spaces_per_indent,
}
}
}
impl<'w, W> BaseGenerator for PrettyWriterGenerator<'w, W>
where
W: Write,
{
type T = W;
#[inline]
fn get_writer(&mut self) -> &mut W {
self.writer
}
#[inline]
fn write_min(&mut self, slice: &[u8], _: u8) -> io::Result<()> {
self.writer.write_all(slice)
}
fn new_line(&mut self) -> io::Result<()> {
stry!(self.write_char(b'\n'));
for _ in 0..(self.dent * self.spaces_per_indent) {
stry!(self.write_char(b' '));
}
Ok(())
}
fn indent(&mut self) {
self.dent += 1;
}
fn dedent(&mut self) {
self.dent -= 1;
}
}
#[inline]
pub(crate) fn extend_from_slice(dst: &mut Vec<u8>, src: &[u8]) {
let dst_len = dst.len();
let src_len = src.len();
dst.reserve(src_len);
unsafe {
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len);
dst.set_len(dst_len + src_len);
}
}