use crate::portability::trailingzeroes;
use crate::value::ValueTrait;
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use std::io;
use std::io::Write;
use std::marker::PhantomData;
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;
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, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ];
pub trait BaseGenerator {
type T: Write;
fn get_writer(&mut self) -> &mut Self::T;
#[inline(always)]
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
self.get_writer().write_all(slice)
}
#[inline(always)]
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(always)]
fn new_line(&mut self) -> io::Result<()> {
Ok(())
}
#[inline(always)]
fn indent(&mut self) {}
#[inline(always)]
fn dedent(&mut self) {}
#[inline(never)]
fn write_string_complex(&mut self, string: &[u8], mut start: usize) -> io::Result<()> {
stry!(self.write(&string[..start]));
for (index, ch) in string.iter().enumerate().skip(start) {
let escape = ESCAPED[*ch as usize];
if escape > 0 {
stry!(self.write(&string[start..index]));
stry!(self.write(&[b'\\', escape]));
start = index + 1;
}
if escape == b'u' {
stry!(write!(self.get_writer(), "{:04x}", ch));
}
}
self.write(&string[start..])
}
#[inline(always)]
fn write_string(&mut self, string: &str) -> io::Result<()> {
stry!(self.write_char(b'"'));
let mut string = string.as_bytes();
let mut len = string.len();
let mut idx = 0;
unsafe {
let zero = _mm256_set1_epi8(0);
let lower_quote_range = _mm256_set1_epi8(0x1F as i8);
let quote = _mm256_set1_epi8(b'"' as i8);
let backslash = _mm256_set1_epi8(b'\\' as i8);
while len - idx >= 32 {
#[allow(clippy::cast_ptr_alignment)]
let data: __m256i = _mm256_loadu_si256(string.as_ptr().add(idx) as *const __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 {
let quote_dist = trailingzeroes(quote_bits as u64) as usize;
stry!(self.get_writer().write_all(&string[0..idx + quote_dist]));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(write!(self.get_writer(), "\\u{:04x}", ch)),
escape => stry!(self.write(&[b'\\', escape])),
};
string = &string[idx + quote_dist + 1..];
idx = 0;
len = string.len();
} else {
idx += 32;
}
}
let zero = _mm_set1_epi8(0);
let lower_quote_range = _mm_set1_epi8(0x1F as i8);
let quote = _mm_set1_epi8(b'"' as i8);
let backslash = _mm_set1_epi8(b'\\' as i8);
while len - idx > 16 {
#[allow(clippy::cast_ptr_alignment)]
let data: __m128i = _mm_loadu_si128(string.as_ptr().add(idx) as *const __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 {
let quote_dist = trailingzeroes(quote_bits as u64) as usize;
stry!(self.get_writer().write_all(&string[0..idx + quote_dist]));
let ch = string[idx + quote_dist];
match ESCAPED[ch as usize] {
b'u' => stry!(write!(self.get_writer(), "\\u{:04x}", ch)),
escape => stry!(self.write(&[b'\\', escape])),
};
string = &string[idx + quote_dist + 1..];
idx = 0;
len = string.len();
} else {
idx += 16;
}
}
stry!(self.get_writer().write_all(&string[0..idx]));
string = &string[idx..];
}
for (index, ch) in string.iter().enumerate() {
if ESCAPED[*ch as usize] > 0 {
self.write_string_complex(string, index)?;
return self.write_char(b'"');
}
}
stry!(self.write(string));
self.write_char(b'"')
}
#[inline(always)]
fn write_float(&mut self, num: f64) -> io::Result<()> {
let mut buffer = ryu::Buffer::new();
let s = buffer.format(num);
self.get_writer().write_all(s.as_bytes())
}
#[inline(always)]
fn write_int(&mut self, num: i64) -> io::Result<()> {
itoa::write(self.get_writer(), num).map(|_| ())
}
}
pub struct DumpGenerator<VT: ValueTrait> {
_value: PhantomData<VT>,
code: Vec<u8>,
}
impl<VT: ValueTrait> DumpGenerator<VT> {
pub fn new() -> Self {
DumpGenerator {
_value: PhantomData,
code: Vec::with_capacity(1024),
}
}
pub fn consume(self) -> String {
unsafe { String::from_utf8_unchecked(self.code) }
}
}
impl<VT: ValueTrait> BaseGenerator for DumpGenerator<VT> {
type T = Vec<u8>;
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
extend_from_slice(&mut self.code, slice);
Ok(())
}
#[inline(always)]
fn write_char(&mut self, ch: u8) -> io::Result<()> {
self.code.push(ch);
Ok(())
}
#[inline(always)]
fn get_writer(&mut self) -> &mut Vec<u8> {
&mut self.code
}
#[inline(always)]
fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
self.code.push(min);
Ok(())
}
}
pub struct PrettyGenerator<V: ValueTrait> {
code: Vec<u8>,
dent: u16,
spaces_per_indent: u16,
_value: PhantomData<V>,
}
impl<V: ValueTrait> PrettyGenerator<V> {
pub fn new(spaces: u16) -> Self {
PrettyGenerator {
code: Vec::with_capacity(1024),
dent: 0,
spaces_per_indent: spaces,
_value: PhantomData,
}
}
pub fn consume(self) -> String {
unsafe { String::from_utf8_unchecked(self.code) }
}
}
impl<V: ValueTrait> BaseGenerator for PrettyGenerator<V> {
type T = Vec<u8>;
#[inline(always)]
fn write(&mut self, slice: &[u8]) -> io::Result<()> {
extend_from_slice(&mut self.code, slice);
Ok(())
}
#[inline(always)]
fn write_char(&mut self, ch: u8) -> io::Result<()> {
self.code.push(ch);
Ok(())
}
#[inline(always)]
fn get_writer(&mut self) -> &mut Vec<u8> {
&mut self.code
}
#[inline(always)]
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');
for _ in 0..(self.dent * self.spaces_per_indent) {
self.code.push(b' ');
}
Ok(())
}
fn indent(&mut self) {
self.dent += 1;
}
fn dedent(&mut self) {
self.dent -= 1;
}
}
pub struct WriterGenerator<'w, W: 'w + Write, V: ValueTrait> {
writer: &'w mut W,
_value: PhantomData<V>,
}
impl<'w, W, V> WriterGenerator<'w, W, V>
where
W: 'w + Write,
V: ValueTrait,
{
pub fn new(writer: &'w mut W) -> Self {
WriterGenerator {
writer,
_value: PhantomData,
}
}
}
impl<'w, W, V> BaseGenerator for WriterGenerator<'w, W, V>
where
W: Write,
V: ValueTrait,
{
type T = W;
#[inline(always)]
fn get_writer(&mut self) -> &mut W {
&mut self.writer
}
#[inline(always)]
fn write_min(&mut self, _: &[u8], min: u8) -> io::Result<()> {
self.writer.write_all(&[min])
}
}
pub struct PrettyWriterGenerator<'w, W, V>
where
W: 'w + Write,
V: ValueTrait,
{
writer: &'w mut W,
dent: u16,
spaces_per_indent: u16,
_value: PhantomData<V>,
}
impl<'w, W, V> PrettyWriterGenerator<'w, W, V>
where
W: 'w + Write,
V: ValueTrait,
{
pub fn new(writer: &'w mut W, spaces_per_indent: u16) -> Self {
PrettyWriterGenerator {
writer,
dent: 0,
spaces_per_indent,
_value: PhantomData,
}
}
}
impl<'w, W, V> BaseGenerator for PrettyWriterGenerator<'w, W, V>
where
W: Write,
V: ValueTrait,
{
type T = W;
#[inline(always)]
fn get_writer(&mut self) -> &mut W {
&mut self.writer
}
#[inline(always)]
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(always)]
pub 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 {
dst.set_len(dst_len + src_len);
ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr().add(dst_len), src_len);
}
}