use alloc::borrow::Cow;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::str;
use deser_core::de::DeserializeDriver;
use deser_core::ext::ExtValue;
use deser_core::{
Atom, Bytes, ContainerShape, Error, ErrorKind, Event, Implicit, ImplicitValue, Text,
};
use crate::float::{is_float, parse_float};
use crate::object::{ClassName, PropertyVisibility, Visibility};
use crate::reference::{Reference, ReferenceKind};
#[cold]
pub(crate) fn syntax_error(offset: usize, msg: &str) -> Error {
Error::with_offset(ErrorKind::Syntax, format!("syntax error: {}", msg), offset)
}
#[cold]
fn eof_error(offset: usize) -> Error {
Error::with_offset(ErrorKind::EndOfFile, "unexpected end of input", offset)
}
enum Head<'i> {
Null,
Bool(bool),
Int(i64, &'i [u8]),
Float(f64),
Str(&'i [u8]),
EscapedStr(Vec<u8>),
Array(usize),
Object(&'i str, usize),
Custom(&'i str, &'i [u8]),
Enum(&'i str, &'i [u8]),
Reference(ReferenceKind, u64),
}
struct Lexer<'i> {
input: &'i [u8],
pos: usize,
}
impl<'i> Lexer<'i> {
fn next_byte(&mut self) -> Result<u8, Error> {
match self.input.get(self.pos) {
Some(&byte) => {
self.pos += 1;
Ok(byte)
}
None => Err(eof_error(self.pos)),
}
}
fn expect(&mut self, expected: u8) -> Result<(), Error> {
let pos = self.pos;
if self.next_byte()? != expected {
return Err(syntax_error(
pos,
&format!("expected '{}'", char::from(expected)),
));
}
Ok(())
}
fn take(&mut self, len: usize) -> Result<&'i [u8], Error> {
match self.pos.checked_add(len) {
Some(end) if end <= self.input.len() => {
let bytes = &self.input[self.pos..end];
self.pos = end;
Ok(bytes)
}
_ => Err(eof_error(self.input.len())),
}
}
fn number_text(&mut self, signed: bool) -> &'i [u8] {
let start = self.pos;
if signed && matches!(self.input.get(self.pos), Some(b'+' | b'-')) {
self.pos += 1;
}
while self.input.get(self.pos).is_some_and(u8::is_ascii_digit) {
self.pos += 1;
}
&self.input[start..self.pos]
}
fn unsigned(&mut self) -> Result<u64, Error> {
let pos = self.pos;
let text = self.number_text(false);
if text.is_empty() {
return Err(match self.input.get(self.pos) {
None => eof_error(self.pos),
Some(_) => syntax_error(pos, "expected a number"),
});
}
parse_ascii::<u64>(text).ok_or_else(|| syntax_error(pos, "number out of range"))
}
fn length(&mut self) -> Result<usize, Error> {
let pos = self.pos;
usize::try_from(self.unsigned()?).map_err(|_| syntax_error(pos, "length out of range"))
}
fn quoted(&mut self, len: usize) -> Result<&'i [u8], Error> {
self.expect(b'"')?;
let bytes = self.take(len)?;
self.expect(b'"')?;
Ok(bytes)
}
fn close(&mut self) -> Result<usize, Error> {
let pos = self.pos;
self.expect(b'}')?;
Ok(pos)
}
fn head(&mut self) -> Result<Head<'i>, Error> {
let start = self.pos;
let tag = self.next_byte()?;
if tag == b'N' {
self.expect(b';')?;
return Ok(Head::Null);
}
if !b"bidsSaOCErR".contains(&tag) {
return Err(syntax_error(start, "unknown type"));
}
self.expect(b':')?;
let head = match tag {
b'b' => {
let pos = self.pos;
let value = match self.next_byte()? {
b'0' => false,
b'1' => true,
_ => return Err(syntax_error(pos, "invalid boolean")),
};
self.expect(b';')?;
Head::Bool(value)
}
b'i' => {
let pos = self.pos;
let text = self.number_text(true);
if !text.last().is_some_and(u8::is_ascii_digit) {
return Err(syntax_error(pos, "invalid integer"));
}
let value = parse_ascii::<i64>(text)
.ok_or_else(|| syntax_error(pos, "integer out of range"))?;
self.expect(b';')?;
Head::Int(value, text)
}
b'd' => {
let pos = self.pos;
let len = self.input[pos..]
.iter()
.position(|&c| c == b';')
.ok_or_else(|| eof_error(self.input.len()))?;
let text = &self.input[pos..pos + len];
if !is_float(text) {
return Err(syntax_error(pos, "invalid float"));
}
self.pos = pos + len + 1;
Head::Float(parse_float(text))
}
b's' => {
let len = self.length()?;
self.expect(b':')?;
let bytes = self.quoted(len)?;
self.expect(b';')?;
Head::Str(bytes)
}
b'S' => {
let len = self.length()?;
self.expect(b':')?;
self.expect(b'"')?;
let mut bytes = Vec::with_capacity(len.min(self.input.len() - self.pos));
for _ in 0..len {
let pos = self.pos;
match self.next_byte()? {
b'\\' => {
let hex = self.take(2)?;
let value = str::from_utf8(hex)
.ok()
.filter(|hex| hex.bytes().all(|c| c.is_ascii_hexdigit()))
.and_then(|hex| u8::from_str_radix(hex, 16).ok())
.ok_or_else(|| syntax_error(pos, "invalid escape sequence"))?;
bytes.push(value);
}
byte => bytes.push(byte),
}
}
self.expect(b'"')?;
self.expect(b';')?;
Head::EscapedStr(bytes)
}
b'a' => {
let len = self.length()?;
self.expect(b':')?;
self.expect(b'{')?;
Head::Array(len)
}
b'O' => {
let class = self.class()?;
self.expect(b':')?;
let pos = self.pos;
let text = self.number_text(true);
let (negative, digits) = match text {
[b'-', digits @ ..] => (true, digits),
[b'+', digits @ ..] => (false, digits),
digits => (false, digits),
};
let len = match digits.is_empty() {
true => Some(0),
false => parse_ascii::<usize>(digits),
};
let len = len
.filter(|&len| !negative || len == 0)
.ok_or_else(|| syntax_error(pos, "invalid number of properties"))?;
self.expect(b':')?;
self.expect(b'{')?;
Head::Object(class, len)
}
b'C' => {
let class = self.class()?;
self.expect(b':')?;
let len = self.length()?;
self.expect(b':')?;
self.expect(b'{')?;
let payload = self.take(len)?;
self.expect(b'}')?;
Head::Custom(class, payload)
}
b'E' => {
let len = self.length()?;
self.expect(b':')?;
let pos = self.pos + 1;
let text = self.quoted(len)?;
self.expect(b';')?;
let colon = text
.iter()
.position(|&c| c == b':')
.ok_or_else(|| syntax_error(pos, "enum case without class"))?;
let (class, case) = (&text[..colon], &text[colon + 1..]);
if !is_class_name(class) || !is_name(case) {
return Err(syntax_error(pos, "invalid enum case"));
}
Head::Enum(utf8_class(class, pos)?, case)
}
b'r' | b'R' => {
let number = self.unsigned()?;
self.expect(b';')?;
let kind = match tag {
b'r' => ReferenceKind::Object,
_ => ReferenceKind::Value,
};
Head::Reference(kind, number)
}
_ => unreachable!(),
};
Ok(head)
}
fn class(&mut self) -> Result<&'i str, Error> {
let len = self.length()?;
self.expect(b':')?;
let pos = self.pos + 1;
let class = self.quoted(len)?;
if !is_class_name(class) {
return Err(syntax_error(pos, "invalid class name"));
}
utf8_class(class, pos)
}
}
fn utf8_class(class: &[u8], pos: usize) -> Result<&str, Error> {
str::from_utf8(class).map_err(|_| {
Error::with_offset(
ErrorKind::UnsupportedType,
"class names that are not valid UTF-8 are not supported",
pos,
)
})
}
fn parse_ascii<T: str::FromStr>(text: &[u8]) -> Option<T> {
str::from_utf8(text).ok()?.parse().ok()
}
pub(crate) fn is_name(bytes: &[u8]) -> bool {
!bytes.is_empty()
&& bytes
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'_' || c >= 0x80)
}
pub(crate) fn is_class_name(bytes: &[u8]) -> bool {
bytes.split(|&c| c == b'\\').all(is_name)
}
pub(crate) fn int_key(text: &[u8]) -> Option<i64> {
let digits = text.strip_prefix(b"-").unwrap_or(text);
match digits {
[b'0'] if digits.len() == text.len() => Some(0),
[b'1'..=b'9', rest @ ..] if rest.iter().all(u8::is_ascii_digit) => parse_ascii(text),
_ => None,
}
}
struct ScanFrame {
remaining: usize,
list: Option<usize>,
next: i64,
}
pub(crate) struct Scan {
pub(crate) end: usize,
pub(crate) lists: Vec<bool>,
}
pub(crate) fn scan(input: &[u8], start: usize) -> Result<Scan, Error> {
let mut lexer = Lexer { input, pos: start };
let mut lists = Vec::new();
let mut objects = Vec::new();
let mut stack = Vec::new();
scan_value(&mut lexer, &mut lists, &mut objects, &mut stack)?;
while let Some(frame) = stack.last_mut() {
if frame.remaining == 0 {
lexer.close()?;
stack.pop();
continue;
}
frame.remaining -= 1;
let pos = lexer.pos;
let key = match lexer.head()? {
Head::Int(value, _) => Some(value),
Head::Str(text) => int_key(text),
Head::EscapedStr(ref text) => int_key(text),
_ => return Err(syntax_error(pos, "keys must be integers or strings")),
};
if let Some(list) = frame.list {
if key == Some(frame.next) {
frame.next += 1;
} else {
lists[list] = false;
frame.list = None;
}
}
scan_value(&mut lexer, &mut lists, &mut objects, &mut stack)?;
}
Ok(Scan {
end: lexer.pos,
lists,
})
}
fn scan_value(
lexer: &mut Lexer<'_>,
lists: &mut Vec<bool>,
objects: &mut Vec<bool>,
stack: &mut Vec<ScanFrame>,
) -> Result<(), Error> {
let pos = lexer.pos;
match lexer.head()? {
Head::Reference(kind, number) => {
let target = usize::try_from(number)
.ok()
.and_then(|number| number.checked_sub(1))
.and_then(|index| objects.get(index));
match (kind, target) {
(ReferenceKind::Object, Some(true)) => objects.push(true),
(ReferenceKind::Value, Some(_)) => {}
_ => return Err(syntax_error(pos, "invalid reference")),
}
}
Head::Array(len) => {
objects.push(false);
stack.push(ScanFrame {
remaining: len,
list: Some(lists.len()),
next: 0,
});
lists.push(true);
}
Head::Object(_, len) => {
objects.push(true);
stack.push(ScanFrame {
remaining: len,
list: None,
next: 0,
});
}
Head::Custom(..) | Head::Enum(..) => objects.push(true),
_ => objects.push(false),
}
Ok(())
}
#[derive(Clone, Copy, PartialEq)]
enum Container {
List,
Array,
Object,
}
struct EmitFrame {
remaining: usize,
container: Container,
}
pub(crate) fn emit<'i>(
input: &'i [u8],
start: usize,
lists: &[bool],
driver: &mut DeserializeDriver<'_, 'i>,
) -> Result<(), Error> {
let mut emitter = Emitter {
lexer: Lexer { input, pos: start },
lists: lists.iter(),
stack: Vec::new(),
driver,
};
emitter.value()?;
while let Some(frame) = emitter.stack.last_mut() {
if frame.remaining == 0 {
let container = frame.container;
emitter.stack.pop();
let pos = emitter.lexer.close()?;
emitter.driver.state_mut().set_input_range(pos, pos + 1);
emitter.driver.emit(match container {
Container::List => Event::SeqEnd,
_ => Event::MapEnd,
})?;
continue;
}
frame.remaining -= 1;
let container = frame.container;
let pos = emitter.lexer.pos;
let key = emitter.lexer.head()?;
if container != Container::List {
let end = emitter.lexer.pos;
emitter.driver.state_mut().set_input_range(pos, end);
emitter.key(key, container == Container::Object)?;
}
emitter.value()?;
}
Ok(())
}
struct Emitter<'a, 'd, 'i, 'l> {
lexer: Lexer<'i>,
lists: core::slice::Iter<'l, bool>,
stack: Vec<EmitFrame>,
driver: &'a mut DeserializeDriver<'d, 'i>,
}
impl<'i> Emitter<'_, '_, 'i, '_> {
fn value(&mut self) -> Result<(), Error> {
let start = self.lexer.pos;
let head = self.lexer.head()?;
let end = self.lexer.pos;
let driver = &mut *self.driver;
driver.state_mut().set_input_range(start, end);
match head {
Head::Null => driver.emit(Atom::Null),
Head::Bool(value) => driver.emit(Atom::Bool(value)),
Head::Int(value, _) => driver.emit(int_atom(value)),
Head::Float(value) => driver.emit(Atom::F64(value)),
Head::Str(bytes) => driver.emit_borrowed(Event::Atom(string_atom(bytes))),
Head::EscapedStr(bytes) => driver.emit(owned_string_atom(bytes)),
Head::Array(len) => {
let is_list = *self.lists.next().unwrap();
let mut shape = ContainerShape::with_len(len);
shape.set_ambiguous_empty(len == 0);
self.stack.push(EmitFrame {
remaining: len,
container: if is_list {
Container::List
} else {
Container::Array
},
});
driver.emit(if is_list {
Event::SeqStart(shape)
} else {
Event::MapStart(shape)
})
}
Head::Object(class, len) => {
set_class(driver, class);
self.stack.push(EmitFrame {
remaining: len,
container: Container::Object,
});
driver.emit(Event::MapStart(ContainerShape::with_len(len)))
}
Head::Custom(class, payload) => {
set_class(driver, class);
driver.emit_borrowed(Event::Atom(Atom::Bytes(Bytes::borrowed(payload))))
}
Head::Enum(class, case) => {
set_class(driver, class);
driver.emit_borrowed(Event::Atom(string_atom(case)))
}
Head::Reference(kind, number) => {
driver.emit(Atom::Ext(ExtValue::owned(Reference::new(kind, number))))
}
}
}
fn key(&mut self, key: Head<'i>, is_object: bool) -> Result<(), Error> {
let driver = &mut *self.driver;
match key {
Head::Int(value, text) => {
let text = match int_key(text) {
Some(_) => Text::borrowed(str::from_utf8(text).unwrap()),
None => Text::owned(value.to_string()),
};
driver.emit_borrowed(Event::Atom(int_key_atom(text, value)))
}
Head::Str(bytes) => match int_key(bytes) {
Some(value) => {
let text = Text::borrowed(str::from_utf8(bytes).unwrap());
driver.emit_borrowed(Event::Atom(int_key_atom(text, value)))
}
None => {
let name = match is_object {
true => demangle(bytes, driver),
false => bytes,
};
driver.emit_borrowed(Event::Atom(string_atom(name)))
}
},
Head::EscapedStr(bytes) => match int_key(&bytes) {
Some(value) => {
let text = Text::owned(String::from_utf8(bytes).unwrap());
driver.emit(int_key_atom(text, value))
}
None => {
let name = match is_object {
true => demangle(&bytes, driver),
false => &bytes,
};
driver.emit(owned_string_atom(name.to_vec()))
}
},
_ => unreachable!(),
}
}
}
fn set_class(driver: &mut DeserializeDriver<'_, '_>, class: &str) {
driver.state_mut().event_mut::<ClassName>().0 = Some(class.to_string());
}
fn demangle<'b>(name: &'b [u8], driver: &mut DeserializeDriver<'_, '_>) -> &'b [u8] {
let Some(rest) = name.strip_prefix(b"\0") else {
return name;
};
let Some(end) = rest.iter().position(|&c| c == 0) else {
return name;
};
let visibility = match &rest[..end] {
b"*" => Visibility::Protected,
class => match str::from_utf8(class) {
Ok(class) if !class.is_empty() => Visibility::Private(class.to_string()),
_ => return name,
},
};
driver.state_mut().event_mut::<PropertyVisibility>().0 = Some(visibility);
&rest[end + 1..]
}
fn int_atom(value: i64) -> Atom<'static> {
match u64::try_from(value) {
Ok(value) => Atom::U64(value),
Err(_) => Atom::I64(value),
}
}
fn int_key_atom(text: Text<'_>, value: i64) -> Atom<'_> {
let value = match u64::try_from(value) {
Ok(value) => ImplicitValue::U64(value),
Err(_) => ImplicitValue::I64(value),
};
Atom::Implicit(Implicit::new(text, value))
}
fn string_atom(bytes: &[u8]) -> Atom<'_> {
match str::from_utf8(bytes) {
Ok(text) => Atom::Str(Text::borrowed(text)),
Err(_) => Atom::Bytes(Bytes::borrowed(bytes)),
}
}
fn owned_string_atom(bytes: Vec<u8>) -> Atom<'static> {
match String::from_utf8(bytes) {
Ok(text) => Atom::Str(Text::owned(text)),
Err(err) => Atom::Bytes(Bytes::new(Cow::Owned(err.into_bytes()))),
}
}