use crate::error::ErrorKind;
use core::fmt;
const ESCAPED_BIT: u32 = 1 << 31;
const OFFSET_MASK: u32 = !ESCAPED_BIT;
pub const MAX_INPUT_LEN: usize = OFFSET_MASK as usize;
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct JsonStr {
start_packed: u32,
end: u32,
}
impl JsonStr {
pub(crate) const fn new(start: u32, end: u32, has_escapes: bool) -> Self {
let start_packed = if has_escapes {
start | ESCAPED_BIT
} else {
start
};
Self { start_packed, end }
}
#[must_use]
pub const fn has_escapes(&self) -> bool {
self.start_packed & ESCAPED_BIT != 0
}
#[must_use]
pub const fn start(&self) -> u32 {
self.start_packed & OFFSET_MASK
}
#[must_use]
pub const fn end(&self) -> u32 {
self.end
}
#[must_use]
pub fn raw_bytes<'input>(&self, input: &'input [u8]) -> Option<&'input [u8]> {
let start = self.start() as usize;
let end = self.end as usize;
input.get(start..end)
}
#[must_use]
pub fn as_str<'input>(&self, input: &'input [u8]) -> Option<&'input str> {
if self.has_escapes() {
return None;
}
let raw = self.raw_bytes(input)?;
Some(unsafe { core::str::from_utf8_unchecked(raw) })
}
}
impl fmt::Debug for JsonStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JsonStr")
.field("start", &self.start())
.field("end", &self.end)
.field("has_escapes", &self.has_escapes())
.finish_non_exhaustive()
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct JsonNum {
start: u32,
end: u32,
}
impl JsonNum {
pub(crate) const fn new(start: u32, end: u32) -> Self {
Self { start, end }
}
#[must_use]
pub const fn start(&self) -> u32 {
self.start
}
#[must_use]
pub const fn end(&self) -> u32 {
self.end
}
#[must_use]
pub fn raw_bytes<'input>(&self, input: &'input [u8]) -> Option<&'input [u8]> {
input.get(self.start as usize..self.end as usize)
}
#[must_use]
pub fn as_str<'input>(&self, input: &'input [u8]) -> &'input str {
self.raw_bytes(input)
.map_or("", |bytes| unsafe { core::str::from_utf8_unchecked(bytes) })
}
#[must_use]
pub fn is_float(&self, input: &[u8]) -> bool {
self.raw_bytes(input)
.is_some_and(|bytes| bytes.iter().any(|b| matches!(*b, b'.' | b'e' | b'E')))
}
#[inline]
pub fn as_i64(&self, input: &[u8]) -> Result<i64, ErrorKind> {
let bytes = self.raw_bytes(input).ok_or(ErrorKind::InvalidNumber)?;
parse_i64(bytes).ok_or(ErrorKind::NumberOutOfRange)
}
#[inline]
pub fn as_u64(&self, input: &[u8]) -> Result<u64, ErrorKind> {
let bytes = self.raw_bytes(input).ok_or(ErrorKind::InvalidNumber)?;
parse_u64(bytes).ok_or(ErrorKind::NumberOutOfRange)
}
#[inline]
pub fn as_i128(&self, input: &[u8]) -> Result<i128, ErrorKind> {
self.as_str(input)
.parse::<i128>()
.map_err(|_| ErrorKind::NumberOutOfRange)
}
#[inline]
pub fn as_u128(&self, input: &[u8]) -> Result<u128, ErrorKind> {
self.as_str(input)
.parse::<u128>()
.map_err(|_| ErrorKind::NumberOutOfRange)
}
pub fn as_f64(&self, input: &[u8]) -> Result<f64, ErrorKind> {
let v: f64 = self
.as_str(input)
.parse::<f64>()
.map_err(|_| ErrorKind::InvalidNumber)?;
if v.is_finite() {
Ok(v)
} else {
Err(ErrorKind::NumberOutOfRange)
}
}
}
impl fmt::Debug for JsonNum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JsonNum")
.field("start", &self.start)
.field("end", &self.end)
.finish()
}
}
const U64_FAST_DIGITS: usize = 19;
const I64_FAST_DIGITS: usize = 18;
#[inline]
fn parse_u64(raw: &[u8]) -> Option<u64> {
if raw.is_empty() {
return None;
}
if raw.len() <= U64_FAST_DIGITS {
let mut acc: u64 = 0;
for &b in raw {
let d = b.wrapping_sub(b'0');
if d >= 10 {
return None;
}
acc = acc * 10 + u64::from(d);
}
return Some(acc);
}
let mut acc: u64 = 0;
for &b in raw {
let d = b.wrapping_sub(b'0');
if d >= 10 {
return None;
}
acc = acc.checked_mul(10)?.checked_add(u64::from(d))?;
}
Some(acc)
}
#[inline]
fn parse_i64(raw: &[u8]) -> Option<i64> {
let (negative, digits) = match raw.split_first() {
Some((&b'-', rest)) => (true, rest),
_ => (false, raw),
};
if digits.is_empty() {
return None;
}
if digits.len() <= I64_FAST_DIGITS {
let mut acc: i64 = 0;
for &b in digits {
let d = b.wrapping_sub(b'0');
if d >= 10 {
return None;
}
acc = acc * 10 + i64::from(d);
}
return Some(if negative { -acc } else { acc });
}
let mut acc: i64 = 0;
if negative {
for &b in digits {
let d = b.wrapping_sub(b'0');
if d >= 10 {
return None;
}
acc = acc.checked_mul(10)?.checked_sub(i64::from(d))?;
}
} else {
for &b in digits {
let d = b.wrapping_sub(b'0');
if d >= 10 {
return None;
}
acc = acc.checked_mul(10)?.checked_add(i64::from(d))?;
}
}
Some(acc)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Event {
StartObject,
EndObject,
StartArray,
EndArray,
Key(JsonStr),
String(JsonStr),
Number(JsonNum),
Bool(bool),
Null,
}