use alloc::string::String;
use alloc::vec::Vec;
use super::decode::Decoder;
use super::event::BorrowedEvent;
use super::prelude::{Decision, Prelude};
use super::probe::Probe;
use super::scan::{self, Carried, Cursor, Pending, Scanned};
use crate::Limits;
use crate::encoding::Encoding;
use crate::error::{Error, ErrorKind, Result};
use crate::external::ExternalSource;
use crate::parser::Version;
const COMPACT_THRESHOLD: usize = 8192;
const NO_EXTERNAL: &(dyn ExternalSource + Sync) = &crate::external::NoExternal;
#[derive(Debug)]
enum Stage {
Prelude(Prelude),
Body(Decoder),
}
#[derive(Clone, Copy, Debug, Default)]
struct StreamEnd {
eof: bool,
done: bool,
}
pub struct Core<'e> {
text: String,
carried: Carried,
cursor: Cursor,
external: &'e (dyn ExternalSource + Sync),
stage: Stage,
probe: Option<Probe>,
whole: bool,
end: StreamEnd,
compacts: bool,
pending_end: Option<Pending>,
consumed: usize,
source_bytes: usize,
}
impl core::fmt::Debug for Core<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Core")
.field("buffered", &self.text.len())
.field("consumed", &self.consumed)
.field("stage", &self.stage)
.field("whole", &self.whole)
.field("eof", &self.end.eof)
.field("done", &self.end.done)
.finish_non_exhaustive()
}
}
impl Default for Core<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'e> Core<'e> {
#[must_use]
pub fn new() -> Self {
Self::with_limits(Limits::default())
}
#[must_use]
pub fn with_limits(limits: Limits) -> Self {
Self {
text: String::new(),
carried: Carried::new(limits),
cursor: Cursor::new(),
external: NO_EXTERNAL,
stage: Stage::Prelude(Prelude::default()),
probe: None,
whole: false,
end: StreamEnd::default(),
compacts: true,
pending_end: None,
consumed: 0,
source_bytes: 0,
}
}
#[must_use]
pub fn with_external(
mut self,
external: &'e (dyn ExternalSource + Sync),
) -> Self {
self.external = external;
self
}
#[must_use]
pub fn needs_input(&self) -> bool {
!self.end.done
&& !self.end.eof
&& self.pending_end.is_none()
&& !self.whole
}
pub fn feed(&mut self, bytes: &[u8]) -> Result<()> {
self.guarded(|core| {
let total = core.source_bytes.checked_add(bytes.len()).ok_or_else(
|| Error::new(ErrorKind::InputTooLarge, core.consumed),
)?;
core.carried.limits.check_input(total)?;
core.source_bytes = total;
for chunk in bytes.chunks(8192) {
core.ingest(chunk)?;
}
Ok(())
})
}
pub fn feed_eof(&mut self) -> Result<()> {
if self.end.done || self.end.eof {
return Ok(());
}
self.end.eof = true;
self.guarded(|core| {
core.read_declaration()?;
if let Stage::Body(decoder) = &mut core.stage {
decoder.finish(&mut core.text)?;
}
core.after_input()
})
}
pub fn next_event(&mut self) -> Result<Option<BorrowedEvent<'_>>> {
if let Some(pending) = self.pending_end.take() {
return Ok(Some(pending.into_event()));
}
self.prepare()?;
if !self.ready() {
return Ok(None);
}
self.compact();
self.end.done = true;
let consumed = self.consumed;
let external: &dyn ExternalSource = self.external;
let scanned = scan::scan(
&self.text,
&mut self.carried,
&mut self.cursor,
external,
)
.map_err(|e| offset_by(e, consumed))?;
let Some((event, pending)) = yielded(scanned) else {
return Ok(None);
};
self.pending_end = pending;
self.whole =
whole(&mut self.probe, &self.text, &mut self.cursor, self.end.eof);
self.end.done = false;
Ok(Some(event))
}
pub fn prepare(&mut self) -> Result<()> {
self.guarded(|core| {
while core.pending_end.is_none() && core.ready() {
core.compact();
core.check_construct()?;
let external: &dyn ExternalSource = core.external;
let skipped = scan::skip_eventless(
&core.text,
&mut core.carried,
&mut core.cursor,
external,
)
.map_err(|e| offset_by(e, core.consumed))?;
if !skipped {
break;
}
core.whole = whole(
&mut core.probe,
&core.text,
&mut core.cursor,
core.end.eof,
);
}
Ok(())
})
}
fn ready(&self) -> bool {
!self.end.done && self.whole
}
#[must_use]
pub fn offset(&self) -> usize {
self.consumed + self.cursor.pos
}
pub(crate) fn adopt(
&mut self,
text: String,
version: Version,
standalone: bool,
) -> Result<()> {
self.carried.declare(version, standalone);
self.stage = Stage::Body(Decoder::new(
Encoding::Utf8,
version,
self.carried.limits.max_document_bytes,
));
self.text = text;
self.end.eof = true;
self.compacts = false;
self.guarded(Self::after_input)
}
pub(crate) fn settled(&self) -> bool {
matches!(self.stage, Stage::Body(_))
}
#[cfg(feature = "std")]
pub(crate) fn fail(&mut self) {
self.end.done = true;
}
#[cfg(feature = "std")]
pub(crate) fn end_offset(&self) -> usize {
self.consumed + self.text.len()
}
fn guarded(
&mut self,
f: impl FnOnce(&mut Self) -> Result<()>,
) -> Result<()> {
if self.end.done {
return Ok(());
}
let outcome = f(self);
if outcome.is_err() {
self.end.done = true;
}
outcome
}
fn ingest(&mut self, bytes: &[u8]) -> Result<()> {
match &mut self.stage {
Stage::Body(decoder) => {
decoder.push(bytes, &mut self.text, self.consumed)?;
}
Stage::Prelude(prelude) => {
prelude.push(bytes);
self.read_declaration()?;
if let Stage::Prelude(prelude) = &self.stage {
self.check_cap(prelude.len(), 0)?;
}
}
}
self.after_input()
}
fn read_declaration(&mut self) -> Result<()> {
let Stage::Prelude(prelude) = &self.stage else {
return Ok(());
};
let Some(decision) = prelude.decide(self.end.eof)? else {
return Ok(());
};
let held = self.begin(decision);
let Stage::Body(decoder) = &mut self.stage else {
return Ok(());
};
decoder.push(&held[decision.bom..], &mut self.text, 0)
}
fn begin(&mut self, decision: Decision) -> Vec<u8> {
self.carried.declare(decision.version, decision.standalone);
let decoder = Decoder::new(
decision.encoding,
decision.version,
self.carried.limits.max_document_bytes,
);
match core::mem::replace(&mut self.stage, Stage::Body(decoder)) {
Stage::Prelude(prelude) => prelude.into_bytes(),
Stage::Body(_) => Vec::new(),
}
}
fn after_input(&mut self) -> Result<()> {
if !self.settled() {
return Ok(());
}
self.whole =
whole(&mut self.probe, &self.text, &mut self.cursor, self.end.eof);
self.check_construct()
}
fn check_construct(&self) -> Result<()> {
if self.compacts {
let (start, bytes) = self.construct_bytes();
self.check_cap(bytes, start)?;
}
Ok(())
}
fn construct_bytes(&self) -> (usize, usize) {
let start = self.probe.map_or(self.cursor.pos, |p| p.start);
let end = self.probe.and_then(|p| p.end()).unwrap_or(self.text.len());
(start, end.saturating_sub(start))
}
fn check_cap(&self, buffered: usize, start: usize) -> Result<()> {
let cap = self.carried.limits.max_construct_bytes;
if cap.is_some_and(|max| buffered > max) {
return Err(Error::new(
ErrorKind::ConstructTooLarge,
self.consumed + start,
));
}
Ok(())
}
fn compact(&mut self) {
if !self.compacts || self.cursor.pos < COMPACT_THRESHOLD {
return;
}
let drained = self.cursor.pos;
let _ = self.text.drain(..drained);
self.consumed += drained;
self.cursor.pos = 0;
match &mut self.probe {
Some(probe) if probe.start >= drained => probe.shift(drained),
_ => self.probe = None,
}
}
}
fn yielded(
scanned: Scanned<'_>,
) -> Option<(BorrowedEvent<'_>, Option<Pending>)> {
match scanned {
Scanned::Event(event) => Some((event, None)),
Scanned::SelfClosed(start, pending) => Some((start, Some(pending))),
Scanned::Eof => None,
}
}
fn whole(
probe: &mut Option<Probe>,
text: &str,
cursor: &mut Cursor,
eof: bool,
) -> bool {
let in_content = !cursor.open.is_empty();
if !in_content {
cursor.pos = skip_whitespace(text, cursor.pos);
}
let start = cursor.pos;
let probe = probe.get_or_insert_with(|| Probe::at(start));
if probe.start != start {
*probe = Probe::at(start);
}
probe.is_whole(text, in_content) || eof
}
fn skip_whitespace(text: &str, mut pos: usize) -> usize {
let bytes = text.as_bytes();
while pos < bytes.len()
&& matches!(bytes[pos], b' ' | b'\t' | b'\r' | b'\n')
{
pos += 1;
}
pos
}
fn offset_by(mut e: Error, consumed: usize) -> Error {
e.offset += consumed;
e
}
#[cfg(test)]
impl Core<'_> {
fn encoding(&self) -> Option<crate::encoding::Encoding> {
match &self.stage {
Stage::Body(decoder) => Some(decoder.encoding()),
Stage::Prelude(_) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stream::Event;
fn events(bytes: &[u8], n: usize) -> Result<Vec<Event>> {
let mut core = Core::new();
let mut chunks = bytes.chunks(n);
let mut out = Vec::new();
loop {
while core.needs_input() {
match chunks.next() {
Some(chunk) => core.feed(chunk)?,
None => core.feed_eof()?,
}
}
if let Some(event) = core.next_event()? {
out.push(event.into_owned());
} else if !core.needs_input() {
return Ok(out);
}
}
}
#[test]
fn next_event_is_none_while_input_is_needed() {
let mut core = Core::new();
core.feed(b"<a><b>te").expect("fine");
let mut seen = 0;
while let Some(_event) = core.next_event().expect("fine") {
seen += 1;
}
assert_eq!(seen, 2, "the two whole start tags");
assert!(core.needs_input(), "the text run has not ended");
core.feed(b"xt</b></a>").expect("fine");
core.feed_eof().expect("fine");
assert!(!core.needs_input());
assert!(matches!(
core.next_event().expect("fine"),
Some(BorrowedEvent::Text(t)) if t == "text"
));
}
#[test]
fn a_document_reads_the_same_whatever_the_chunking() {
let doc =
b"<?xml version=\"1.1\"?>\r\n<!DOCTYPE a [<!ENTITY e \"x\">]>\
<a x='1'>one <![CDATA[two]]> &e;\r\xC2\x85three</a>";
let expected = events(doc, doc.len()).expect("valid");
for n in 1..=doc.len() {
assert_eq!(events(doc, n).expect("valid"), expected, "chunk {n}");
}
}
#[test]
fn the_declaration_picks_the_decoder() {
let mut core = Core::new();
core.feed(b"<?xml version='1.0' encoding='iso-8859-1'?><a/>")
.expect("fine");
assert_eq!(core.encoding(), Some(Encoding::Latin1));
let mut core = Core::new();
core.feed(b"<?x").expect("fine");
assert_eq!(core.encoding(), None, "still deciding");
}
#[test]
fn a_failed_reader_accepts_nothing_more() {
let mut core = Core::new();
assert!(core.feed(b"<a>\x01</a>").is_err());
assert!(core.feed(b"<a/>").is_ok(), "ignored, not retried");
assert!(!core.needs_input());
assert!(core.next_event().expect("stays failed").is_none());
}
}