use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
use crate::encoding;
use crate::errors::{Error, Result};
use crate::events::{BytesText, Event};
use crate::name::QName;
use crate::parser::Parser;
use crate::reader::{BangType, ReadRefResult, ReadTextResult, Reader, Span, XmlSource};
use crate::utils::is_whitespace;
macro_rules! impl_buffered_source {
($($lf:lifetime, $reader:tt, $async:ident, $await:ident)?) => {
#[cfg(not(feature = "encoding"))]
#[inline]
$($async)? fn remove_utf8_bom(&mut self) -> io::Result<()> {
loop {
break match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) => {
if n.starts_with(encoding::UTF8_BOM) {
self $(.$reader)? .consume(encoding::UTF8_BOM.len());
}
Ok(())
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => Err(e),
};
}
}
#[cfg(feature = "encoding")]
#[inline]
$($async)? fn detect_encoding(&mut self) -> io::Result<Option<encoding::DetectedEncoding>> {
loop {
break match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) => {
if let Some(detected) = encoding::detect_encoding(n) {
self $(.$reader)? .consume(detected.bom_len());
Ok(Some(detected))
} else {
Ok(None)
}
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => Err(e),
};
}
}
#[inline]
$($async)? fn read_text $(<$lf>)? (
&mut self,
buf: &'b mut Vec<u8>,
position: &mut u64,
trim_whitespace_only: bool,
) -> ReadTextResult<'b, &'b mut Vec<u8>> {
let mut read = 0;
let start = buf.len();
loop {
let available = match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) if n.is_empty() => break,
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => {
*position += read;
return ReadTextResult::Err(e);
}
};
match oxixml_support::bytes::memchr2(b'<', b'&', available) {
Some(0) if read == 0 && available[0] == b'<' => return ReadTextResult::Markup(buf),
Some(0) if read == 0 => return ReadTextResult::Ref(buf),
Some(i) if available[i] == b'<' => {
buf.extend_from_slice(&available[..i]);
self $(.$reader)? .consume(i);
read += i as u64;
*position += read;
if trim_whitespace_only
&& buf[start..].iter().all(|b| crate::utils::is_whitespace(*b))
{
buf.truncate(start);
return ReadTextResult::Markup(buf);
}
return ReadTextResult::UpToMarkup(&buf[start..]);
}
Some(i) => {
buf.extend_from_slice(&available[..i]);
self $(.$reader)? .consume(i);
read += i as u64;
*position += read;
if trim_whitespace_only
&& buf[start..].iter().all(|b| crate::utils::is_whitespace(*b))
{
buf.truncate(start);
return ReadTextResult::Ref(buf);
}
return ReadTextResult::UpToRef(&buf[start..]);
}
None => {
buf.extend_from_slice(available);
let used = available.len();
self $(.$reader)? .consume(used);
read += used as u64;
}
}
}
*position += read;
ReadTextResult::UpToEof(&buf[start..])
}
#[inline]
$($async)? fn read_ref $(<$lf>)? (
&mut self,
buf: &'b mut Vec<u8>,
position: &mut u64,
) -> ReadRefResult<'b> {
let mut read = 0;
let start = buf.len();
loop {
let available = match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) if n.is_empty() => break,
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => {
*position += read;
return ReadRefResult::Err(e);
}
};
if read == 0 {
debug_assert!(
available.starts_with(b"&"),
"`read_ref` must be called at `&`:\n{:?}",
crate::utils::Bytes(available)
);
buf.push(b'&');
self $(.$reader)? .consume(1);
read += 1;
continue;
}
match oxixml_support::bytes::memchr3(b';', b'&', b'<', available) {
Some(i) if available[i] == b';' => {
let used = i + 1;
buf.extend_from_slice(&available[..used]);
self $(.$reader)? .consume(used);
read += used as u64;
*position += read;
return ReadRefResult::Ref(&buf[start..]);
}
Some(i) => {
let is_amp = available[i] == b'&';
buf.extend_from_slice(&available[..i]);
self $(.$reader)? .consume(i);
read += i as u64;
*position += read;
return if is_amp {
ReadRefResult::UpToRef(&buf[start..])
} else {
ReadRefResult::UpToMarkup(&buf[start..])
};
}
None => {
buf.extend_from_slice(available);
let used = available.len();
self $(.$reader)? .consume(used);
read += used as u64;
}
}
}
*position += read;
ReadRefResult::UpToEof(&buf[start..])
}
#[inline]
$($async)? fn read_with<$($lf,)? P: Parser>(
&mut self,
mut parser: P,
buf: &'b mut Vec<u8>,
position: &mut u64,
) -> Result<&'b [u8]> {
let mut read = 1;
let start = buf.len();
buf.push(b'<');
loop {
let available = match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) if n.is_empty() => break,
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => {
*position += read;
return Err(Error::from(e));
}
};
if let Some(i) = parser.feed(available) {
let used = i + 1; buf.extend_from_slice(&available[..used]);
self $(.$reader)? .consume(used);
read += used as u64;
*position += read;
return Ok(&buf[start..]);
}
buf.extend_from_slice(available);
let used = available.len();
self $(.$reader)? .consume(used);
read += used as u64;
}
*position += read;
Err(Error::Syntax(parser.eof_error(&buf[start..])))
}
#[inline]
$($async)? fn read_bang_element $(<$lf>)? (
&mut self,
buf: &'b mut Vec<u8>,
position: &mut u64,
) -> Result<(BangType, &'b [u8])> {
let start = buf.len();
let mut read = 2;
buf.push(b'<');
buf.push(b'!');
self $(.$reader)? .consume(1);
let mut bang_type = loop {
break match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) => BangType::new(n.first().copied())?,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(Error::from(e)),
};
};
loop {
let available = match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) if n.is_empty() => break,
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => {
*position += read;
return Err(Error::from(e));
}
};
if let Some(i) = bang_type.feed(&buf[start..], available) {
let consumed = i + 1; buf.extend_from_slice(&available[..consumed]);
self $(.$reader)? .consume(consumed);
read += consumed as u64;
*position += read;
return Ok((bang_type, &buf[start..]));
}
buf.extend_from_slice(available);
let used = available.len();
self $(.$reader)? .consume(used);
read += used as u64;
}
*position += read;
Err(Error::Syntax(bang_type.to_err()))
}
#[inline]
$($async)? fn skip_whitespace(&mut self, position: &mut u64) -> io::Result<()> {
loop {
break match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) => {
let count = n.iter().position(|b| !is_whitespace(*b)).unwrap_or(n.len());
if count > 0 {
self $(.$reader)? .consume(count);
*position += count as u64;
continue;
} else {
Ok(())
}
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => Err(e),
};
}
}
#[inline]
$($async)? fn peek_one(&mut self) -> io::Result<Option<u8>> {
self $(.$reader)? .consume(1);
let available = loop {
break match self $(.$reader)? .fill_buf() $(.$await)? {
Ok(n) => n,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
};
Ok(available.first().copied())
}
};
}
#[cfg(feature = "async-tokio")]
pub(super) use impl_buffered_source;
impl<'b, R: BufRead> XmlSource<'b, &'b mut Vec<u8>> for R {
impl_buffered_source!();
}
impl<R: BufRead> Reader<R> {
#[inline]
pub fn read_event_into<'b>(&mut self, buf: &'b mut Vec<u8>) -> Result<Event<'b>> {
self.read_event_impl(buf)
}
pub fn read_to_end_into(&mut self, end: QName, buf: &mut Vec<u8>) -> Result<Span> {
Ok(read_to_end!(self, end, buf, read_event_impl, {
buf.clear();
}))
}
pub fn read_text_into<'b>(
&mut self,
end: QName,
buf: &'b mut Vec<u8>,
) -> Result<BytesText<'b>> {
let start = buf.len();
let span = read_to_end!(self, end, buf, read_event_impl, {});
let len = span.end - span.start;
let end = start + len as usize;
Ok(BytesText::wrap(&buf[start..end], self.decoder()))
}
}
impl Reader<BufReader<File>> {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let file = File::open(path)?;
let reader = BufReader::new(file);
Ok(Self::from_reader(reader))
}
}
#[cfg(test)]
mod test {
use crate::reader::XmlSource;
use crate::reader::check_tests::check;
fn identity<T>(input: T) -> T {
input
}
check!(
#[test]
read_event_impl,
identity,
1,
&mut Vec::new()
);
}