use crate::Error;
use crate::io::{ReadBuffer, WriteBuffer};
use crate::simd;
pub trait Format {
fn encode_struct<T: crate::Encode, W: WriteBuffer>(val: &T, w: &mut W) -> Result<(), Error> {
val.encode(w)
}
fn write_null(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_bytes(b"null")
}
fn write_bool(v: bool, w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_bytes(if v { b"true" } else { b"false" })
}
fn write_u64(v: u64, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
fn write_i64(v: i64, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
fn write_f64(v: f64, w: &mut impl WriteBuffer) -> Result<(), Error> {
if v.is_nan() || v.is_infinite() {
return Err(Error::InvalidFloat);
}
let mut buf = ryu::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
fn write_str(v: &str, w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'"')?;
let bytes = v.as_bytes();
let mut start = 0usize;
loop {
let remaining = &bytes[start..];
if remaining.is_empty() {
return w.write_byte(b'"');
}
let escape_pos = simd::scan_escape_chars(remaining);
if escape_pos == remaining.len() {
w.write_bytes(remaining)?;
return w.write_byte(b'"');
}
if escape_pos > 0 {
w.write_bytes(&remaining[..escape_pos])?;
}
let b = remaining[escape_pos];
match b {
b'"' => w.write_bytes(b"\\\"")?,
b'\\' => w.write_bytes(b"\\\\")?,
b'\n' => w.write_bytes(b"\\n")?,
b'\r' => w.write_bytes(b"\\r")?,
b'\t' => w.write_bytes(b"\\t")?,
_ => {
w.write_bytes(b"\\u00")?;
w.write_bytes(&[fast_hex_digit(b >> 4), fast_hex_digit(b & 0x0f)])?;
}
}
start += escape_pos + 1;
}
}
fn write_bytes(v: &[u8], w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'"')?;
let mut start = 0usize;
for i in 0..v.len() {
let b = v[i];
if b == b'"' || b == b'\\' || b < 0x20 {
if i > start {
w.write_bytes(&v[start..i])?;
}
match b {
b'"' => w.write_bytes(b"\\\"")?,
b'\\' => w.write_bytes(b"\\\\")?,
_ => {
w.write_bytes(b"\\u00")?;
w.write_bytes(&hex_digit(b >> 4))?;
w.write_bytes(&hex_digit(b & 0x0f))?;
}
}
start = i + 1;
}
}
if start < v.len() {
w.write_bytes(&v[start..])?;
}
w.write_byte(b'"')
}
fn begin_object(n_fields: usize, w: &mut impl WriteBuffer) -> Result<(), Error> {
let _ = n_fields;
w.write_byte(b'{')
}
fn write_field_key(key: &[u8], w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'"')?;
w.write_bytes(key)?;
w.write_bytes(b"\":")
}
fn field_separator(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b':')
}
fn object_separator(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b',')
}
fn end_object(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'}')
}
fn begin_array(len: usize, w: &mut impl WriteBuffer) -> Result<(), Error> {
let _ = len;
w.write_byte(b'[')
}
fn array_separator(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b',')
}
fn end_array(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b']')
}
fn read_bool(r: &mut ReadBuffer<'_>) -> Result<bool, Error> {
skip_whitespace(r);
match r.peek() {
b't' => {
r.expect_bytes(b"true")?;
Ok(true)
}
b'f' => {
r.expect_bytes(b"false")?;
Ok(false)
}
b => Err(Error::UnexpectedByte {
expected: "boolean",
got: b,
offset: r.pos,
}),
}
}
fn read_u64(r: &mut ReadBuffer<'_>) -> Result<u64, Error> {
read_unsigned(r)
}
fn read_i64(r: &mut ReadBuffer<'_>) -> Result<i64, Error> {
read_signed(r)
}
fn read_f64(r: &mut ReadBuffer<'_>) -> Result<f64, Error> {
read_float(r)
}
fn read_str<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de str, Error> {
read_string(r)
}
fn read_bytes<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de [u8], Error> {
read_bytes_impl(r)
}
fn read_null(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
r.expect_bytes(b"null")
}
fn begin_object_decode(r: &mut ReadBuffer<'_>) -> Result<usize, Error> {
r.expect_byte(b'{')?;
Ok(0)
}
fn read_field_key<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de str, Error> {
read_string(r)
}
fn end_object_decode(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
r.expect_byte(b'}')
}
fn begin_array_decode(r: &mut ReadBuffer<'_>) -> Result<usize, Error> {
r.expect_byte(b'[')?;
Ok(0)
}
fn end_array_decode(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
r.expect_byte(b']')
}
fn skip_value(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
skip_value(r)
}
}
fn hex_digit(b: u8) -> [u8; 1] {
[if b < 10 { b + b'0' } else { b - 10 + b'a' }]
}
static HEX_DIGIT_TABLE: [u8; 16] = [
b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f',
];
#[inline]
fn fast_hex_digit(b: u8) -> u8 {
HEX_DIGIT_TABLE[(b & 0x0f) as usize]
}
#[inline(always)]
const fn is_json_ws(b: u8) -> bool {
matches!(b, b' ' | b'\t' | b'\n' | b'\r')
}
#[inline(always)]
pub fn skip_whitespace(r: &mut ReadBuffer<'_>) {
if r.pos < r.data.len() {
let b = unsafe { *r.data.get_unchecked(r.pos) };
if !is_json_ws(b) {
return;
}
} else {
return;
}
let n = simd::skip_whitespace(&r.data[r.pos..]);
r.pos += n;
}
#[inline(always)]
fn parse_8_digits_swar(chunk: u64) -> (u64, u32) {
let zeros = 0x3030_3030_3030_3030u64;
let nines = 0x3939_3939_3939_3939u64;
let lt0 = chunk.wrapping_sub(zeros);
let gt9 = nines.wrapping_sub(chunk);
let invalid = (lt0 | gt9) & 0x8080_8080_8080_8080u64;
if invalid != 0 {
let pos = invalid.trailing_zeros() / 8;
let bytes = chunk.to_le_bytes();
let mut val: u64 = 0;
for i in 0..pos {
val = val * 10 + (bytes[i as usize] - b'0') as u64;
}
return (val, pos);
}
let chunk = chunk - 0x3030_3030_3030_3030u64;
let chunk = (chunk * 10 + (chunk >> 8)) & 0x00FF_00FF_00FF_00FFu64;
let chunk = (chunk * 100 + (chunk >> 16)) & 0x0000_FFFF_0000_FFFFu64;
let chunk = chunk * 10000 + (chunk >> 32);
(chunk & 0xFFFF_FFFF, 8)
}
#[inline(always)]
pub fn read_unsigned(r: &mut ReadBuffer<'_>) -> Result<u64, Error> {
skip_whitespace(r);
let start = r.pos;
let data = r.data;
let mut n: u64 = 0;
let mut pos = start;
while pos + 8 <= data.len() {
let chunk = u64::from_le_bytes(unsafe { *(data.as_ptr().add(pos) as *const [u8; 8]) });
let (val, count) = parse_8_digits_swar(chunk);
if count == 8 {
n = n
.checked_mul(100_000_000)
.and_then(|v| v.checked_add(val))
.ok_or(Error::NumberOverflow { type_name: "u64" })?;
pos += 8;
} else {
for _ in 0..count {
let digit = (data[pos] - b'0') as u64;
n = n
.checked_mul(10)
.and_then(|v| v.checked_add(digit))
.ok_or(Error::NumberOverflow { type_name: "u64" })?;
pos += 1;
}
r.pos = pos;
if pos == start {
return Err(Error::UnexpectedByte {
expected: "digit",
got: r.peek(),
offset: r.pos,
});
}
return Ok(n);
}
}
while pos < data.len() {
let b = data[pos];
if !b.is_ascii_digit() {
break;
}
let digit = (b - b'0') as u64;
n = n
.checked_mul(10)
.and_then(|v| v.checked_add(digit))
.ok_or(Error::NumberOverflow { type_name: "u64" })?;
pos += 1;
}
r.pos = pos;
if pos == start {
return Err(Error::UnexpectedByte {
expected: "digit",
got: r.peek(),
offset: r.pos,
});
}
Ok(n)
}
#[inline(always)]
pub fn read_signed(r: &mut ReadBuffer<'_>) -> Result<i64, Error> {
skip_whitespace(r);
let data = r.data;
let neg = r.pos < data.len() && data[r.pos] == b'-';
if neg {
r.pos += 1;
let n = read_unsigned(r)?;
if n > (i64::MAX as u64) + 1 {
return Err(Error::NumberOverflow { type_name: "i64" });
}
if n == (i64::MAX as u64) + 1 {
Ok(i64::MIN)
} else {
Ok(-(n as i64))
}
} else {
let n = read_unsigned(r)?;
if n > i64::MAX as u64 {
return Err(Error::NumberOverflow { type_name: "i64" });
}
Ok(n as i64)
}
}
#[inline(always)]
pub fn read_float(r: &mut ReadBuffer<'_>) -> Result<f64, Error> {
skip_whitespace(r);
let start = r.pos;
let data = r.data;
if r.pos < data.len() && (data[r.pos] == b'+' || data[r.pos] == b'-') {
r.pos += 1;
}
let mut has_dot = false;
let mut has_exp = false;
let mut has_digits = false;
while r.pos < data.len() {
let b = data[r.pos];
if b.is_ascii_digit() {
has_digits = true;
r.pos += 1;
} else if b == b'.' && !has_dot && !has_exp && has_digits {
has_dot = true;
r.pos += 1;
if r.pos >= data.len() || !data[r.pos].is_ascii_digit() {
return Err(Error::InvalidFloat);
}
} else if (b == b'e' || b == b'E') && !has_exp && has_digits {
has_exp = true;
r.pos += 1;
if r.pos < data.len() && (data[r.pos] == b'+' || data[r.pos] == b'-') {
r.pos += 1;
}
if r.pos >= data.len() || !data[r.pos].is_ascii_digit() {
return Err(Error::InvalidFloat);
}
} else {
break;
}
}
if r.pos == start {
return Err(Error::InvalidFloat);
}
let slice = core::str::from_utf8(&data[start..r.pos])
.map_err(|_| Error::InvalidUtf8 { byte_offset: start })?;
slice.parse::<f64>().map_err(|_| Error::InvalidFloat)
}
#[inline(always)]
pub fn read_string<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de str, Error> {
r.expect_byte(b'"')?;
let start = r.pos;
let data = r.data;
let end = simd::scan_quote_or_backslash(&data[r.pos..]);
let abs = r.pos + end;
if abs >= data.len() {
r.pos = data.len();
return Err(Error::UnexpectedEof);
}
match data[abs] {
b'"' => {
let slice = core::str::from_utf8(&data[start..abs])
.map_err(|_| Error::InvalidUtf8 { byte_offset: start })?;
r.pos = abs + 1;
Ok(slice)
}
b'\\' => Err(Error::EscapeInBorrowedString { offset: abs }),
other => Err(Error::UnexpectedByte {
expected: "string body or terminator",
got: other,
offset: abs,
}),
}
}
#[inline(always)]
pub fn read_key_fast<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de [u8], Error> {
r.expect_byte(b'"')?;
let start = r.pos;
let data = r.data;
while r.pos < data.len() {
let remaining = &data[r.pos..];
if remaining.is_empty() {
return Err(Error::UnexpectedEof);
}
let end_pos = simd::scan_quote_or_backslash(remaining);
if end_pos == 0 {
return Err(Error::UnexpectedEof);
}
let ch = remaining[end_pos];
if ch == b'"' {
let end = r.pos + end_pos;
r.pos = end + 1;
return Ok(&data[start..end]);
}
if ch == b'\\' {
return Err(Error::UnexpectedByte {
expected: "key",
got: b'\\',
offset: r.pos + end_pos,
});
}
r.pos += end_pos + 1;
}
Err(Error::UnexpectedEof)
}
#[inline(always)]
pub fn read_string_owned<'de>(r: &mut ReadBuffer<'de>) -> Result<alloc::string::String, Error> {
let open_pos = r.pos;
r.expect_byte(b'"')?;
let start = r.pos;
let end = simd::scan_quote_or_backslash(&r.data[r.pos..]);
if r.pos + end >= r.data.len() {
return Err(Error::UnexpectedEof);
}
if r.data[r.pos + end] == b'"' {
let slice = core::str::from_utf8(&r.data[start..start + end])
.map_err(|_| Error::InvalidUtf8 { byte_offset: start })?;
r.pos = start + end + 1;
return Ok(alloc::string::String::from(slice));
}
r.pos = open_pos;
Ok(read_string_cow(r)?.into_owned())
}
#[inline(always)]
pub fn read_string_cow<'de>(
r: &mut ReadBuffer<'de>,
) -> Result<alloc::borrow::Cow<'de, str>, Error> {
r.expect_byte(b'"')?;
let start = r.pos;
let end = simd::scan_quote_or_backslash(&r.data[r.pos..]);
if r.pos + end >= r.data.len() {
return Err(Error::UnexpectedEof);
}
if r.data[r.pos + end] == b'"' {
let slice = core::str::from_utf8(&r.data[start..start + end])
.map_err(|_| Error::InvalidUtf8 { byte_offset: start })?;
r.pos = start + end + 1;
return Ok(alloc::borrow::Cow::Borrowed(slice));
}
let mut s = alloc::string::String::with_capacity(end + 16);
s.push_str(
core::str::from_utf8(&r.data[start..start + end])
.map_err(|_| Error::InvalidUtf8 { byte_offset: start })?,
);
r.pos += end;
while r.pos < r.data.len() {
let b = r.data[r.pos];
if b == b'"' {
r.pos += 1;
return Ok(alloc::borrow::Cow::Owned(s));
}
if b == b'\\' {
r.pos += 1;
if r.pos >= r.data.len() {
return Err(Error::UnexpectedEof);
}
let esc = r.data[r.pos];
r.pos += 1;
match esc {
b'"' => s.push('"'),
b'\\' => s.push('\\'),
b'/' => s.push('/'),
b'b' => s.push('\x08'),
b'f' => s.push('\x0c'),
b'n' => s.push('\n'),
b'r' => s.push('\r'),
b't' => s.push('\t'),
b'u' => {
if r.pos + 4 > r.data.len() {
return Err(Error::UnexpectedEof);
}
let hex = &r.data[r.pos..r.pos + 4];
let code = unescape_hex(hex)?;
if let Some(c) = core::char::from_u32(code) {
s.push(c);
} else {
return Err(Error::InvalidUtf8 {
byte_offset: r.pos - 2,
});
}
r.pos += 4;
}
_ => {
return Err(Error::UnexpectedByte {
expected: "escape sequence",
got: esc,
offset: r.pos - 1,
});
}
}
} else {
let chunk_start = r.pos;
let next = simd::scan_quote_or_backslash(&r.data[r.pos..]);
if next > 0 {
s.push_str(
core::str::from_utf8(&r.data[chunk_start..chunk_start + next]).map_err(
|_| Error::InvalidUtf8 {
byte_offset: chunk_start,
},
)?,
);
}
r.pos += next;
}
}
Err(Error::UnexpectedEof)
}
#[inline(always)]
fn unescape_hex(hex: &[u8]) -> Result<u32, Error> {
let mut code = 0u32;
for &b in hex {
let digit = match b {
b'0'..=b'9' => (b - b'0') as u32,
b'a'..=b'f' => (b - b'a' + 10) as u32,
b'A'..=b'F' => (b - b'A' + 10) as u32,
_ => {
return Err(Error::UnexpectedByte {
expected: "hex digit",
got: b,
offset: 0,
});
}
};
code = (code << 4) | digit;
}
Ok(code)
}
pub fn read_bytes<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de [u8], Error> {
read_bytes_impl(r)
}
pub fn read_bytes_impl<'de>(r: &mut ReadBuffer<'de>) -> Result<&'de [u8], Error> {
r.expect_byte(b'"')?;
let start = r.pos;
let end = simd::scan_quote_or_backslash(&r.data[r.pos..]);
if r.pos + end >= r.data.len() {
return Err(Error::UnexpectedEof);
}
r.expect_at(r.pos + end, b'"')?;
r.pos = start;
let result = &r.data[start..start + end];
r.pos = start + end + 1;
Ok(result)
}
#[inline(always)]
pub fn skip_value(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
skip_whitespace(r);
match r.peek() {
b'n' => r.expect_bytes(b"null"),
b't' => r.expect_bytes(b"true"),
b'f' => r.expect_bytes(b"false"),
b'0'..=b'9' | b'-' => {
r.pos += 1;
while r.pos < r.data.len() {
let c = r.data[r.pos];
if !matches!(c, b'0'..=b'9' | b'.' | b'e' | b'E' | b'+' | b'-') {
break;
}
r.pos += 1;
}
Ok(())
}
b'"' => skip_string(r),
b'[' => skip_container(r, b'[', b']'),
b'{' => skip_container(r, b'{', b'}'),
b => Err(Error::UnexpectedByte {
expected: "value",
got: b,
offset: r.pos,
}),
}
}
#[inline(always)]
fn skip_string(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
debug_assert_eq!(r.peek(), b'"');
r.pos += 1; loop {
let rem = &r.data[r.pos..];
let n = simd::scan_quote_or_backslash(rem);
if n >= rem.len() {
return Err(Error::UnexpectedEof);
}
r.pos += n;
match r.data[r.pos] {
b'"' => {
r.pos += 1;
return Ok(());
}
b'\\' => {
if r.pos + 1 >= r.data.len() {
return Err(Error::UnexpectedEof);
}
if r.data[r.pos + 1] == b'u' {
if r.pos + 6 > r.data.len() {
return Err(Error::UnexpectedEof);
}
r.pos += 6;
} else {
r.pos += 2;
}
}
_ => unreachable!(),
}
}
}
#[inline(always)]
fn skip_container(r: &mut ReadBuffer<'_>, open: u8, close: u8) -> Result<(), Error> {
debug_assert_eq!(r.peek(), open);
r.pos += 1;
let mut depth: u32 = 1;
while depth > 0 {
if r.pos >= r.data.len() {
return Err(Error::UnexpectedEof);
}
let b = r.data[r.pos];
if b == open {
depth += 1;
r.pos += 1;
} else if b == close {
depth -= 1;
r.pos += 1;
} else if b == b'"' {
skip_string(r)?;
} else {
r.pos += 1;
}
}
Ok(())
}
#[inline]
pub fn skip_comma_or_close(r: &mut ReadBuffer<'_>, _close: u8) -> Result<(), Error> {
skip_whitespace(r);
if r.peek() == b',' {
r.advance(1);
skip_whitespace(r);
}
Ok(())
}
#[inline]
pub fn estimate_array_len(data: &[u8], pos: usize, max_scan: usize) -> usize {
let end = (pos + max_scan).min(data.len());
let mut p = pos;
let mut depth: i32 = 0;
let mut count: usize = 1;
let mut saw_value = false;
while p < end {
let b = data[p];
match b {
b'[' | b'{' => {
depth += 1;
saw_value = true;
p += 1;
}
b']' | b'}' => {
if depth == 0 {
return if saw_value { count } else { 0 };
}
depth -= 1;
p += 1;
}
b',' if depth == 0 => {
count += 1;
p += 1;
}
b'"' => {
p += 1;
let rem = &data[p..end];
let q = simd::scan_quote_or_backslash(rem);
p += q;
while p < end && data[p] == b'\\' {
p += 2; let rem = &data[p.min(end)..end];
p += simd::scan_quote_or_backslash(rem);
}
if p < end {
p += 1; }
saw_value = true;
}
b' ' | b'\t' | b'\n' | b'\r' => {
p += 1;
}
_ => {
saw_value = true;
p += 1;
}
}
}
let scanned = p - pos;
if scanned == 0 || scanned >= data.len() - pos {
return count;
}
let total = data.len() - pos;
let approx = (count as u64).saturating_mul(total as u64) / scanned as u64;
(approx as usize).min(1 << 20) }
pub fn write_u64(v: u64, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_u32(v: u32, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_u16(v: u16, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_u8(v: u8, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_i64(v: i64, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_i32(v: i32, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_i16(v: i16, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_i8(v: i8, w: &mut impl WriteBuffer) -> Result<(), Error> {
let mut buf = itoa::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_f64(v: f64, w: &mut impl WriteBuffer) -> Result<(), Error> {
if v.is_nan() || v.is_infinite() {
return Err(Error::InvalidFloat);
}
let mut buf = ryu::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_f32(v: f32, w: &mut impl WriteBuffer) -> Result<(), Error> {
if v.is_nan() || v.is_infinite() {
return Err(Error::InvalidFloat);
}
let mut buf = ryu::Buffer::new();
w.write_bytes(buf.format(v).as_bytes())
}
pub fn write_bool(v: bool, w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_bytes(if v { b"true" } else { b"false" })
}
pub fn write_null(w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_bytes(b"null")
}
pub fn write_str(s: &str, w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'"')?;
let bytes = s.as_bytes();
let mut start = 0usize;
loop {
let remaining = &bytes[start..];
if remaining.is_empty() {
return w.write_byte(b'"');
}
let escape_pos = simd::scan_escape_chars(remaining);
if escape_pos == remaining.len() {
w.write_bytes(remaining)?;
return w.write_byte(b'"');
}
if escape_pos > 0 {
w.write_bytes(&remaining[..escape_pos])?;
}
let b = remaining[escape_pos];
match b {
b'"' => w.write_bytes(b"\\\"")?,
b'\\' => w.write_bytes(b"\\\\")?,
b'\n' => w.write_bytes(b"\\n")?,
b'\r' => w.write_bytes(b"\\r")?,
b'\t' => w.write_bytes(b"\\t")?,
_ => {
w.write_bytes(b"\\u00")?;
w.write_bytes(&[fast_hex_digit(b >> 4), fast_hex_digit(b & 0x0f)])?;
}
}
start += escape_pos + 1;
}
}
pub fn write_bytes(v: &[u8], w: &mut impl WriteBuffer) -> Result<(), Error> {
w.write_byte(b'"')?;
let mut start = 0usize;
loop {
let remaining = &v[start..];
if remaining.is_empty() {
return w.write_byte(b'"');
}
let escape_pos = simd::scan_escape_chars(remaining);
if escape_pos == remaining.len() {
w.write_bytes(remaining)?;
return w.write_byte(b'"');
}
if escape_pos > 0 {
w.write_bytes(&remaining[..escape_pos])?;
}
let b = remaining[escape_pos];
match b {
b'"' => w.write_bytes(b"\\\"")?,
b'\\' => w.write_bytes(b"\\\\")?,
_ => {
w.write_bytes(b"\\u00")?;
w.write_bytes(&[fast_hex_digit(b >> 4), fast_hex_digit(b & 0x0f)])?;
}
}
start += escape_pos + 1;
}
}
pub fn read_u64(r: &mut ReadBuffer<'_>) -> Result<u64, Error> {
skip_whitespace(r);
read_unsigned(r)
}
pub fn read_u32(r: &mut ReadBuffer<'_>) -> Result<u32, Error> {
read_unsigned(r).map(|v| v as u32)
}
pub fn read_u16(r: &mut ReadBuffer<'_>) -> Result<u16, Error> {
read_unsigned(r).map(|v| v as u16)
}
pub fn read_u8(r: &mut ReadBuffer<'_>) -> Result<u8, Error> {
read_unsigned(r).map(|v| v as u8)
}
pub fn read_i64(r: &mut ReadBuffer<'_>) -> Result<i64, Error> {
read_signed(r)
}
pub fn read_i32(r: &mut ReadBuffer<'_>) -> Result<i32, Error> {
read_signed(r).map(|v| v as i32)
}
pub fn read_i16(r: &mut ReadBuffer<'_>) -> Result<i16, Error> {
read_signed(r).map(|v| v as i16)
}
pub fn read_i8(r: &mut ReadBuffer<'_>) -> Result<i8, Error> {
read_signed(r).map(|v| v as i8)
}
pub fn read_f32(r: &mut ReadBuffer<'_>) -> Result<f32, Error> {
read_float(r).map(|v| v as f32)
}
pub fn read_f64(r: &mut ReadBuffer<'_>) -> Result<f64, Error> {
read_float(r)
}
pub fn read_bool(r: &mut ReadBuffer<'_>) -> Result<bool, Error> {
skip_whitespace(r);
match r.peek() {
b't' => {
r.expect_bytes(b"true")?;
Ok(true)
}
b'f' => {
r.expect_bytes(b"false")?;
Ok(false)
}
b => Err(Error::UnexpectedByte {
expected: "boolean",
got: b,
offset: r.pos,
}),
}
}
pub fn read_null(r: &mut ReadBuffer<'_>) -> Result<(), Error> {
skip_whitespace(r);
r.expect_bytes(b"null")
}
#[inline(always)]
pub fn binary_search_key(key: &[u8], sorted_keys: &[&str]) -> Option<usize> {
if sorted_keys.is_empty() {
return None;
}
let mut left = 0usize;
let mut right = sorted_keys.len();
while left < right {
let mid = left + (right - left) / 2;
let mid_key = sorted_keys[mid].as_bytes();
match key.cmp(mid_key) {
core::cmp::Ordering::Equal => return Some(mid),
core::cmp::Ordering::Less => {
if mid == 0 {
break;
}
right = mid;
}
core::cmp::Ordering::Greater => left = mid + 1,
}
}
None
}
#[inline(always)]
fn quick_hash(key: &[u8]) -> u32 {
let mut hash: u32 = 5381;
for &b in key {
hash = hash.wrapping_mul(33).wrapping_add(b as u32);
}
hash
}
#[inline(always)]
pub fn perfect_hash_lookup(key: &[u8], keys: &[&str]) -> Option<usize> {
let h = quick_hash(key) as usize;
let idx = h % keys.len();
if keys.get(idx).map(|k| k.as_bytes()) == Some(key) {
Some(idx)
} else {
None
}
}