use alloc::borrow::Cow;
use alloc::vec::Vec;
use pith_digest::{Error, Result};
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum Tok {
Num(f64),
Name(Cow<'static, [u8]>),
Str(Cow<'static, [u8]>),
ArrOpen,
ArrClose,
DictOpen,
DictClose,
Kw(Cow<'static, [u8]>),
}
pub(crate) fn is_ws(b: u8) -> bool {
matches!(b, 0x00 | 0x09 | 0x0A | 0x0C | 0x0D | 0x20)
}
pub(crate) fn is_delim(b: u8) -> bool {
matches!(
b,
b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
)
}
pub(crate) struct Lexer<'a> {
pub(crate) data: &'a [u8],
pub(crate) pos: usize,
}
impl<'a> Lexer<'a> {
pub(crate) fn new(data: &'a [u8]) -> Self {
Lexer { data, pos: 0 }
}
pub(crate) fn skip_ws(&mut self) {
while self.pos < self.data.len() {
let b = self.data[self.pos];
if is_ws(b) {
self.pos += 1;
} else if b == b'%' {
while self.pos < self.data.len()
&& self.data[self.pos] != 0x0A
&& self.data[self.pos] != 0x0D
{
self.pos += 1;
}
} else {
break;
}
}
}
pub(crate) fn rest(&self) -> &'a [u8] {
&self.data[self.pos..]
}
pub(crate) fn next(&mut self) -> Result<Option<Tok>> {
self.skip_ws();
if self.pos >= self.data.len() {
return Ok(None);
}
let b = self.data[self.pos];
match b {
b'[' => {
self.pos += 1;
Ok(Some(Tok::ArrOpen))
}
b']' => {
self.pos += 1;
Ok(Some(Tok::ArrClose))
}
b'<' => {
if self.data.get(self.pos + 1) == Some(&b'<') {
self.pos += 2;
Ok(Some(Tok::DictOpen))
} else {
self.hex_string().map(Some)
}
}
b'>' => {
if self.data.get(self.pos + 1) == Some(&b'>') {
self.pos += 2;
Ok(Some(Tok::DictClose))
} else {
Err(Error::BadValue("lone '>' in object stream"))
}
}
b'(' => self.literal_string().map(Some),
b')' | b'{' | b'}' => {
let t = self.data[self.pos..self.pos + 1].to_vec();
self.pos += 1;
Ok(Some(Tok::Kw(Cow::Owned(t))))
}
b'/' => self.name().map(Some),
_ if b == b'+' || b == b'-' || b == b'.' || b.is_ascii_digit() => {
self.number_or_word().map(Some)
}
_ => {
let start = self.pos;
while self.pos < self.data.len() {
let c = self.data[self.pos];
if is_ws(c) || is_delim(c) {
break;
}
self.pos += 1;
}
Ok(Some(Tok::Kw(Cow::Owned(
self.data[start..self.pos].to_vec(),
))))
}
}
}
fn name(&mut self) -> Result<Tok> {
self.pos += 1; let start = self.pos;
let mut out: Option<Vec<u8>> = None;
while self.pos < self.data.len() {
let c = self.data[self.pos];
if is_ws(c) || is_delim(c) {
break;
}
if c == b'#' {
if out.is_none() {
out = Some(self.data[start..self.pos].to_vec());
}
let v = self
.data
.get(self.pos + 1..self.pos + 3)
.and_then(|h| hex_val(h[0]).zip(hex_val(h[1])))
.map(|(a, b)| a * 16 + b);
match v {
Some(byte) => {
out.as_mut().expect("initialized").push(byte);
self.pos += 3;
}
None => {
return Err(Error::BadValue("name #xx escape"));
}
}
} else {
if let Some(v) = out.as_mut() {
v.push(c);
}
self.pos += 1;
}
}
let name = match out {
Some(v) => Cow::Owned(v),
None => Cow::Owned(self.data[start..self.pos].to_vec()),
};
Ok(Tok::Name(name))
}
fn literal_string(&mut self) -> Result<Tok> {
self.pos += 1; let mut out = Vec::new();
let mut depth = 1usize;
while self.pos < self.data.len() {
let b = self.data[self.pos];
self.pos += 1;
match b {
b'(' => {
depth += 1;
if depth > 64 {
return Err(Error::BadValue("literal string nesting"));
}
out.push(b);
}
b')' => {
depth -= 1;
if depth == 0 {
return Ok(Tok::Str(Cow::Owned(out)));
}
out.push(b);
}
b'\\' => {
let n = match self.data.get(self.pos) {
Some(&n) => n,
None => {
return Err(Error::Truncated {
what: "string escape",
needed: 1,
found: 0,
});
}
};
self.pos += 1;
match n {
b'n' => out.push(0x0A),
b'r' => out.push(0x0D),
b't' => out.push(0x09),
b'b' => out.push(0x08),
b'f' => out.push(0x0C),
b'(' | b')' | b'\\' => out.push(n),
b'0'..=b'7' => {
let mut v = (n - b'0') as u32;
for _ in 0..2 {
match self.data.get(self.pos) {
Some(&d @ b'0'..=b'7') => {
v = v * 8 + (d - b'0') as u32;
self.pos += 1;
}
_ => break,
}
}
out.push((v & 0xFF) as u8);
}
0x0D => {
if self.data.get(self.pos) == Some(&0x0A) {
self.pos += 1;
}
}
0x0A => {}
other => out.push(other), }
}
_ => out.push(b),
}
}
Err(Error::Truncated {
what: "literal string",
needed: 1,
found: 0,
})
}
fn hex_string(&mut self) -> Result<Tok> {
self.pos += 1; let mut out = Vec::new();
let mut hi: Option<u8> = None;
while self.pos < self.data.len() {
let b = self.data[self.pos];
self.pos += 1;
if b == b'>' {
if let Some(h) = hi {
out.push(h << 4); }
return Ok(Tok::Str(Cow::Owned(out)));
}
if is_ws(b) {
continue;
}
match hex_val(b) {
Some(v) => match hi.take() {
Some(h) => out.push((h << 4) | v),
None => hi = Some(v),
},
None => return Err(Error::BadValue("hex string character")),
}
}
Err(Error::Truncated {
what: "hex string",
needed: 1,
found: 0,
})
}
fn number_or_word(&mut self) -> Result<Tok> {
let start = self.pos;
let mut is_num = true;
let mut seen_dot = false;
let mut seen_digit = false;
if matches!(self.data[self.pos], b'+' | b'-') {
self.pos += 1;
}
while self.pos < self.data.len() {
let c = self.data[self.pos];
if c.is_ascii_digit() {
seen_digit = true;
self.pos += 1;
} else if c == b'.' && !seen_dot {
seen_dot = true;
self.pos += 1;
} else {
break;
}
}
if !seen_digit {
is_num = false;
}
if self.pos < self.data.len()
&& !is_ws(self.data[self.pos])
&& !is_delim(self.data[self.pos])
{
is_num = false;
}
if !is_num || self.pos == start {
self.pos = start;
while self.pos < self.data.len() {
let c = self.data[self.pos];
if is_ws(c) || is_delim(c) {
break;
}
self.pos += 1;
}
return Ok(Tok::Kw(Cow::Owned(self.data[start..self.pos].to_vec())));
}
let s = &self.data[start..self.pos];
let v = parse_f64(s).ok_or(Error::BadValue("number"))?;
Ok(Tok::Num(v))
}
pub(crate) fn peek(&mut self) -> Result<Option<Tok>> {
let save = self.pos;
let t = self.next()?;
self.pos = save;
Ok(t)
}
pub(crate) fn eat_kw(&mut self, kw: &[u8]) -> Result<bool> {
let save = self.pos;
match self.next()? {
Some(Tok::Kw(w)) if &*w == kw => Ok(true),
_ => {
self.pos = save;
Ok(false)
}
}
}
pub(crate) fn expect_kw(&mut self, kw: &[u8], what: &'static str) -> Result<()> {
match self.next()? {
Some(Tok::Kw(w)) if &*w == kw => Ok(()),
_ => Err(Error::BadValue(what)),
}
}
pub(crate) fn expect_int(&mut self, what: &'static str) -> Result<i64> {
match self.next()? {
Some(Tok::Num(v)) => f64_as_i64(v).ok_or(Error::BadValue(what)),
_ => Err(Error::BadValue(what)),
}
}
}
pub(crate) fn hex_val(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}
pub(crate) fn is_int(v: f64) -> bool {
if !v.is_finite() || !(-4.6e18..=4.6e18).contains(&v) {
return false;
}
(v as i128) as f64 == v
}
pub(crate) fn f64_as_i64(v: f64) -> Option<i64> {
if is_int(v) && (-4.0e18..=4.0e18).contains(&v) {
let i = v as i64;
if i as f64 == v {
return Some(i);
}
}
None
}
pub(crate) fn parse_f64(s: &[u8]) -> Option<f64> {
let mut i = 0;
let mut neg = false;
if let Some(&c) = s.first() {
if c == b'+' {
i = 1;
} else if c == b'-' {
neg = true;
i = 1;
}
}
let mut int: f64 = 0.0;
let mut any = false;
while let Some(&c) = s.get(i) {
match c {
b'0'..=b'9' => {
int = int * 10.0 + f64::from(c - b'0');
any = true;
i += 1;
}
b'.' => {
i += 1;
let mut frac = 0.0f64;
let mut scale = 1.0f64;
while let Some(&d) = s.get(i) {
if d.is_ascii_digit() {
frac = frac * 10.0 + f64::from(d - b'0');
scale *= 10.0;
any = true;
i += 1;
} else {
return None;
}
}
let v = int + frac / scale;
return if any {
Some(if neg { -v } else { v })
} else {
None
};
}
_ => return None,
}
}
if any {
Some(if neg { -int } else { int })
} else {
None
}
}