use crate::beve::header::{self, byte_width, decode_size};
use crate::beve::reader::{MAX_DEPTH, Reader, Typed, complex_payload, key_width, payload_len};
use crate::error::{ErrorCode, PResult};
use crate::stream::{Framer, Split};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Mode {
Values,
Array,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Frame {
Object {
cat: u8,
width: usize,
left: usize,
key: bool,
},
Values { left: usize },
Strings { left: usize },
}
impl Frame {
fn done(&self) -> bool {
match *self {
Frame::Object { left, .. } | Frame::Values { left } | Frame::Strings { left } => {
left == 0
}
}
}
}
#[derive(Debug, Clone, Copy)]
enum Duty {
Value,
Key { cat: u8, width: usize },
Text,
}
struct Scanner {
pos: usize,
pending: usize,
stack: Vec<Frame>,
started: bool,
}
impl Scanner {
fn new() -> Self {
Scanner {
pos: 0,
pending: 0,
stack: Vec::new(),
started: false,
}
}
fn restart(&mut self, at: usize) {
self.pos = at;
self.pending = 0;
self.stack.clear();
self.started = false;
}
fn rebase(&mut self, shift: usize) {
self.pos = self.pos.saturating_sub(shift);
}
fn duty(&self) -> Duty {
match self.stack.last() {
Some(&Frame::Object {
cat, width, key, ..
}) if key => Duty::Key { cat, width },
Some(Frame::Strings { .. }) => Duty::Text,
_ => Duty::Value,
}
}
fn taken(&mut self) {
match self.stack.last_mut() {
None => self.started = true,
Some(Frame::Object { left, key, .. }) => {
if *key {
*key = false;
} else {
*left -= 1;
*key = true;
}
}
Some(Frame::Values { left } | Frame::Strings { left }) => *left -= 1,
}
}
fn advance(&mut self, buf: &[u8]) -> PResult<Option<usize>> {
loop {
if self.pending > 0 {
let take = self.pending.min(buf.len() - self.pos);
self.pos += take;
self.pending -= take;
if self.pending > 0 {
return Ok(None);
}
}
while self.stack.last().is_some_and(Frame::done) {
self.stack.pop();
}
if self.started && self.stack.is_empty() {
return Ok(Some(self.pos));
}
match self.duty() {
Duty::Key {
cat: header::CAT_FLOAT,
..
}
| Duty::Text => {
let mut p = self.pos;
let Some(n) = size_at(buf, &mut p) else {
return Ok(None);
};
self.pos = p;
self.pending = usize::try_from(n).map_err(|_| ErrorCode::UnexpectedEnd)?;
self.taken();
}
Duty::Key { width, .. } => {
if buf.len() - self.pos < width {
return Ok(None);
}
self.pos += width;
self.taken();
}
Duty::Value => {
let (used, skip, frame) = match head(buf, self.pos, self.stack.len()) {
Ok(step) => step,
Err(ErrorCode::UnexpectedEnd) => return Ok(None),
Err(e) => return Err(e),
};
self.pos += used;
self.pending = skip;
self.taken();
if let Some(frame) = frame {
self.stack.push(frame);
}
}
}
}
}
}
fn size_at(buf: &[u8], p: &mut usize) -> Option<u64> {
decode_size(buf, p).ok()
}
fn head(buf: &[u8], at: usize, depth: usize) -> PResult<(usize, usize, Option<Frame>)> {
let mut r = Reader::new(&buf[at..]);
let h = r.head()?;
let (skip, frame) = match header::ty(h) {
header::TY_NULL_BOOL => match h {
header::NULL | header::FALSE | header::TRUE => (0, None),
_ => return Err(ErrorCode::InvalidHeader),
},
header::TY_NUMBER => {
let w = byte_width(header::sub(h), header::count(h)).ok_or(ErrorCode::InvalidHeader)?;
(w, None)
}
header::TY_STRING => (r.count()?, None),
header::TY_OBJECT => {
let width = key_width(h)?;
let left = r.count()?;
enter(depth)?;
let cat = header::sub(h);
(
0,
Some(Frame::Object {
cat,
width,
left,
key: true,
}),
)
}
header::TY_GENERIC_ARRAY => {
let left = r.count()?;
enter(depth)?;
(0, Some(Frame::Values { left }))
}
header::TY_TYPED_ARRAY => {
let form = r.typed_head(h)?;
enter(depth)?;
match form {
Typed::Bools(n) => (n.div_ceil(8), None),
Typed::Strings(n) => (0, Some(Frame::Strings { left: n })),
Typed::Fixed(elem, n) => (payload_len(elem, n)?, None),
}
}
header::TY_EXTENSION => match header::ext_id(h) {
header::EXT_DELIMITER => (0, None),
header::EXT_TYPE_TAG => {
r.size()?;
enter(depth)?;
(0, Some(Frame::Values { left: 1 }))
}
header::EXT_MATRIX => {
enter(depth)?;
(1, Some(Frame::Values { left: 2 }))
}
header::EXT_COMPLEX => {
let (_, width, pairs) = r.complex_head()?;
(complex_payload(width, pairs)?, None)
}
_ => return Err(ErrorCode::UnsupportedFeature),
},
_ => return Err(ErrorCode::InvalidHeader),
};
Ok((r.position(), skip, frame))
}
fn enter(depth: usize) -> PResult<()> {
if depth >= MAX_DEPTH as usize {
return Err(ErrorCode::ExceededMaxDepth);
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Elem {
Generic,
Fixed { implied: u8, width: usize },
Strings,
Bools { bit: u32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Between,
InItem,
BeforeArray,
BeforeElement,
Done,
}
pub(crate) struct Splitter {
mode: Mode,
scan: Scanner,
state: State,
cursor: usize,
left: usize,
elem: Elem,
implied: Option<u8>,
}
impl Splitter {
pub(crate) fn new(mode: Mode) -> Self {
Splitter {
mode,
scan: Scanner::new(),
state: match mode {
Mode::Array => State::BeforeArray,
Mode::Values => State::Between,
},
cursor: 0,
left: 0,
elem: Elem::Generic,
implied: None,
}
}
pub(crate) fn implied(&self) -> Option<u8> {
self.implied
}
fn open_array(&mut self, buf: &[u8], eof: bool) -> PResult<Option<Split>> {
if self.cursor >= buf.len() {
return Ok(Some(if eof { Split::End } else { Split::Need }));
}
let mut r = Reader::new(&buf[self.cursor..]);
let outcome = (|| {
let h = r.head()?;
match header::ty(h) {
header::TY_GENERIC_ARRAY => Ok((r.count()?, Elem::Generic)),
header::TY_TYPED_ARRAY => Ok(match r.typed_head(h)? {
Typed::Bools(n) => (n, Elem::Bools { bit: 0 }),
Typed::Strings(n) => (n, Elem::Strings),
Typed::Fixed(elem, n) => {
let implied = header::element_of(elem);
(
n,
Elem::Fixed {
implied,
width: header::element_width(implied)
.ok_or(ErrorCode::InvalidHeader)?,
},
)
}
}),
header::TY_EXTENSION if h == header::COMPLEX => {
let (class, width, pairs) = r.complex_head()?;
let n = pairs.ok_or(ErrorCode::ExpectedArray)?;
Ok((
n,
Elem::Fixed {
implied: header::complex_element(class),
width: 2 * width,
},
))
}
_ => Err(ErrorCode::ExpectedArray),
}
})();
let (left, elem) = match outcome {
Ok(pair) => pair,
Err(ErrorCode::UnexpectedEnd) if !eof => return Ok(Some(Split::Need)),
Err(e) => return Err(e),
};
self.cursor += r.position();
self.left = left;
self.elem = elem;
self.state = State::BeforeElement;
Ok(None)
}
fn close_array(&mut self) {
if let Elem::Bools { bit } = self.elem
&& bit > 0
{
self.cursor += 1;
self.elem = Elem::Bools { bit: 0 };
}
self.state = State::Done;
}
}
impl Framer for Splitter {
fn next(&mut self, buf: &[u8], eof: bool) -> PResult<Split> {
self.implied = None;
loop {
match self.state {
State::Between => {
while buf.get(self.cursor) == Some(&header::DELIMITER) {
self.cursor += 1;
}
if self.cursor >= buf.len() {
return Ok(if eof { Split::End } else { Split::Need });
}
self.scan.restart(self.cursor);
self.state = State::InItem;
}
State::InItem => {
let Some(end) = self.scan.advance(buf)? else {
return if eof {
Err(ErrorCode::UnexpectedEnd)
} else {
Ok(Split::Need)
};
};
let start = self.cursor;
self.cursor = end;
match self.mode {
Mode::Values => self.state = State::Between,
Mode::Array => {
self.left -= 1;
self.state = State::BeforeElement;
}
}
return Ok(Split::Item { start, end });
}
State::BeforeArray => {
if let Some(split) = self.open_array(buf, eof)? {
return Ok(split);
}
}
State::BeforeElement => {
if self.left == 0 {
self.close_array();
continue;
}
let start = self.cursor;
let found = match &mut self.elem {
Elem::Generic => {
if start < buf.len() {
self.scan.restart(start);
self.state = State::InItem;
continue;
}
None
}
Elem::Fixed { implied, width } => {
let (implied, width) = (*implied, *width);
if buf.len() - start < width {
None
} else {
self.cursor = start + width;
self.implied = Some(implied);
Some(start + width)
}
}
Elem::Strings => {
let mut p = start;
match size_at(buf, &mut p)
.and_then(|n| usize::try_from(n).ok())
.and_then(|n| p.checked_add(n))
.filter(|&end| end <= buf.len())
{
Some(end) => {
self.cursor = end;
self.implied = Some(header::STRING);
Some(end)
}
None => None,
}
}
Elem::Bools { bit } => match buf.get(start) {
Some(&byte) => {
self.implied = Some(if (byte >> *bit) & 1 == 1 {
header::TRUE
} else {
header::FALSE
});
*bit += 1;
if *bit == 8 {
*bit = 0;
self.cursor = start + 1;
}
Some(start)
}
None => None,
},
};
return match found {
Some(end) => {
self.left -= 1;
Ok(Split::Item { start, end })
}
None if eof => Err(ErrorCode::UnexpectedEnd),
None => Ok(Split::Need),
};
}
State::Done => {
return if self.cursor < buf.len() {
Err(ErrorCode::TrailingContent)
} else if eof {
Ok(Split::End)
} else {
Ok(Split::Need)
};
}
}
}
}
#[inline]
fn consumed(&self) -> usize {
self.cursor
}
fn rebase(&mut self) {
let shift = self.cursor;
self.cursor = 0;
self.scan.rebase(shift);
}
#[inline]
fn position(&self) -> usize {
self.cursor.max(self.scan.pos)
}
}