#![allow(clippy::cast_possible_truncation)]
use log::{debug, warn};
use memchr::memchr_iter;
use std::fmt::{self};
use crate::attribute::AttributeInfo;
use crate::defs::{AttrIdx, NodeIdx, ParseXmlError, XmlIdx, XmlLocation};
use crate::node::Node;
use crate::node_info::NodeInfo;
use crate::node_type::NodeType;
#[cfg(feature = "use_cstr")]
use std::ffi::CStr;
#[derive(PartialEq, Eq)]
#[must_use]
pub struct Document {
pub nodes: Vec<NodeInfo>,
pub attributes: Vec<AttributeInfo>,
pub xml: Vec<u8>,
}
impl Document {
pub fn new(xml: Vec<u8>) -> Result<Self, ParseXmlError> {
let mut node_count = memchr_iter(b'<', xml.as_slice()).count();
let attr_count = memchr_iter(b'=', xml.as_slice()).count();
node_count += (node_count / 10) + 1;
debug!("Estimated node count: {node_count}");
debug!("Estimated attribute count: {attr_count}");
if node_count > NodeIdx::MAX as usize {
return Err(ParseXmlError::InvalidXml(
"XML document has too many estimated nodes!".to_string(),
));
}
if attr_count > AttrIdx::MAX as usize {
return Err(ParseXmlError::InvalidXml(
"XML document has too many estimated attributes!".to_string(),
));
}
if xml.len() > XmlIdx::MAX as usize {
return Err(ParseXmlError::InvalidXml(
"XML document is too large!".to_string(),
));
}
let mut doc = Document {
nodes: Vec::with_capacity(node_count + 1), attributes: Vec::with_capacity(attr_count),
xml,
};
if doc.nodes.capacity() <= node_count || doc.attributes.capacity() < attr_count {
return Err(ParseXmlError::NotEnoughMemory);
}
#[cfg(not(feature = "forward_only"))]
doc.nodes.push(NodeInfo::new(0, 0, NodeType::Head));
#[cfg(feature = "forward_only")]
doc.nodes.push(NodeInfo::new(NodeType::Head));
doc.parse()?;
doc.nodes.shrink_to_fit();
doc.attributes.shrink_to_fit();
warn!(
"Document created with {} nodes and {} attributes",
doc.nodes.len(),
doc.attributes.len()
);
warn!(
"Warning: Expected {} nodes, but found {}",
node_count,
doc.nodes.len()
);
if attr_count < doc.attributes.len() {
warn!(
"Expected {} attributes, but found {}",
attr_count,
doc.attributes.len()
);
}
Ok(doc)
}
#[inline]
#[must_use]
pub fn root(&self) -> Option<Node<'_>> {
#[cfg(not(feature = "forward_only"))]
if self.nodes.len() > 1 {
Some(Node::new(1, &self.nodes[1], self))
} else {
None }
#[cfg(feature = "forward_only")]
if self.nodes.len() > 1 {
Some(Node::new(1, 0, &self.nodes[1], self))
} else {
None }
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.nodes.len() <= 1 }
#[inline]
#[must_use]
pub fn last_node_idx(&self) -> NodeIdx {
if self.is_empty() {
0 } else {
(self.nodes.len() - 1) as NodeIdx }
}
#[cfg(not(feature = "forward_only"))]
#[inline]
pub fn get_node(&self, node_idx: NodeIdx) -> Result<Node<'_>, ParseXmlError> {
if node_idx as usize >= self.nodes.len() {
return Err(ParseXmlError::InvalidXml(format!(
"Invalid node index: {node_idx}"
)));
}
Ok(Node::new(node_idx, &self.nodes[node_idx as usize], self))
}
#[cfg(feature = "forward_only")]
#[inline]
pub fn get_node(&self, node_idx: NodeIdx) -> Result<Node<'_>, ParseXmlError> {
if node_idx as usize >= self.nodes.len() {
return Err(ParseXmlError::InvalidXml(format!(
"Invalid node index: {node_idx}"
)));
}
Ok(Node::new(node_idx, 0, &self.nodes[node_idx as usize], self))
}
#[inline]
#[must_use]
pub fn get_xml_content(&mut self) -> &Vec<u8> {
&self.xml
}
pub(crate) fn add_node(
&mut self,
parent_idx: NodeIdx,
last_child_idx: NodeIdx,
mut node_type: NodeType,
) -> Result<NodeIdx, ParseXmlError> {
let node_idx = self.nodes.len() as NodeIdx;
if node_idx == NodeIdx::MAX {
return Err(ParseXmlError::NoMoreSpace);
}
if let NodeType::Element { attributes, .. } = &mut node_type {
*attributes = self.attributes.len() as AttrIdx..self.attributes.len() as AttrIdx;
}
#[cfg(not(feature = "forward_only"))]
let mut node_info = NodeInfo::new(node_idx, parent_idx, node_type);
#[cfg(feature = "forward_only")]
let node_info = NodeInfo::new(node_type);
#[cfg(not(feature = "forward_only"))]
{
let parent_info = &mut self.nodes[parent_idx as usize];
if parent_info.first_child_idx() == 0 {
parent_info.set_first_child_idx(node_idx); node_info.set_prev_sibling_idx(node_idx); } else {
let first_child_idx = parent_info.first_child_idx();
self.nodes[last_child_idx as usize].set_next_sibling_idx(node_idx); self.nodes[first_child_idx as usize].set_prev_sibling_idx(node_idx); node_info.set_prev_sibling_idx(last_child_idx); }
}
#[cfg(feature = "forward_only")]
{
let parent_info = &mut self.nodes[parent_idx as usize];
if parent_info.first_child_idx() == 0 {
parent_info.set_first_child_idx(node_idx); } else if last_child_idx != 0 {
self.nodes[last_child_idx as usize].set_next_sibling_idx(node_idx);
}
}
self.nodes.push(node_info);
Ok(node_idx)
}
pub(crate) fn add_attribute(
&mut self,
node_idx: NodeIdx,
name: XmlLocation,
value: XmlLocation,
) -> Result<AttrIdx, ParseXmlError> {
let attribute_idx = self.attributes.len() as AttrIdx;
self.attributes.push(AttributeInfo::new(name, value));
let node_info = &mut self.nodes[node_idx as usize];
if !node_info.is_element() {
return Err(ParseXmlError::InternalError);
}
let mut attributes_range = match &node_info.node_type() {
NodeType::Element { attributes, .. } => attributes.clone(),
_ => return Err(ParseXmlError::InternalError),
};
attributes_range.end += 1; node_info.set_node_type(NodeType::Element {
name: match &node_info.node_type() {
#[cfg(not(feature = "use_cstr"))]
NodeType::Element { name, .. } => name.clone(),
#[cfg(feature = "use_cstr")]
NodeType::Element { name, .. } => *name,
_ => return Err(ParseXmlError::InternalError),
},
attributes: attributes_range,
});
Ok(attribute_idx)
}
#[inline]
#[must_use]
pub fn get_str_from_location(&self, location: XmlLocation) -> &str {
#[cfg(not(feature = "use_cstr"))]
{
let xml_content = &self.xml[location.start as usize..location.end as usize];
std::str::from_utf8(xml_content).unwrap_or("non valid utf-8")
}
#[cfg(feature = "use_cstr")]
{
let content = std::ffi::CStr::from_bytes_until_nul(&self.xml[location as usize..])
.unwrap_or(c"cstr not valid");
content.to_str().unwrap_or("non valid utf-8")
}
}
#[cfg(feature = "use_cstr")]
#[inline]
#[must_use]
pub fn get_cstr_from_location(&self, location: XmlLocation) -> &CStr {
CStr::from_bytes_until_nul(&self.xml[location as usize..]).unwrap_or(c"cstr not valid")
}
#[inline]
#[must_use]
pub fn all_nodes(&self) -> Nodes<'_> {
Nodes::new(self)
}
#[inline]
#[must_use]
pub fn descendants(&self, node_idx: NodeIdx) -> Nodes<'_> {
Nodes::descendants(self, node_idx)
}
#[must_use]
pub fn last_descendant(&self, node_idx: NodeIdx) -> Option<NodeIdx> {
if node_idx == 0
|| self.nodes[node_idx as usize].first_child_idx() == 0
|| node_idx as usize >= (self.nodes.len() - 1)
{
None } else if node_idx == 1 {
Some(self.last_node_idx())
} else {
#[cfg(not(feature = "forward_only"))]
{
let mut up_idx = self.nodes[node_idx as usize].parent_idx;
let mut last_descendant = self.nodes[up_idx as usize].next_sibling_idx();
while last_descendant == 0 {
up_idx = self.nodes[up_idx as usize].parent_idx;
if up_idx <= 1 {
last_descendant = self.nodes.len() as NodeIdx; break;
}
last_descendant = self.nodes[up_idx as usize].next_sibling_idx();
}
Some(last_descendant - 1)
}
#[cfg(feature = "forward_only")]
{
let mut curr_node_idx = self.nodes[node_idx as usize].first_child_idx();
loop {
while self.nodes[curr_node_idx as usize].next_sibling_idx() != 0 {
curr_node_idx = self.nodes[curr_node_idx as usize].next_sibling_idx();
}
if self.nodes[curr_node_idx as usize].first_child_idx() != 0 {
curr_node_idx = self.nodes[curr_node_idx as usize].first_child_idx();
} else {
break; }
}
Some(curr_node_idx)
}
}
}
#[inline]
#[must_use]
pub fn next_seq_node(&self, current: NodeIdx) -> Option<Node<'_>> {
let next = current + 1;
if next < self.nodes.len() as NodeIdx {
self.get_node(next).ok()
} else {
None
}
}
#[inline]
#[must_use]
pub fn previous_seq_node(&self, current: NodeIdx) -> Option<Node<'_>> {
let previous = current - 1;
if previous > 0 {
self.get_node(previous).ok()
} else {
None
}
}
}
impl fmt::Debug for Document {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let Some(root) = self.root() {
macro_rules! writeln_indented {
($indent:expr, $f:expr, $fmt:expr) => {
for _ in 0..$indent { write!($f, " ")?; }
writeln!($f, $fmt)?;
};
($indent:expr, $f:expr, $fmt:expr, $($arg:tt)*) => {
for _ in 0..$indent { write!($f, " ")?; }
writeln!($f, $fmt, $($arg)*)?;
};
}
fn print_into_iter<
T: fmt::Debug,
E: ExactSizeIterator<Item = T>,
I: IntoIterator<Item = T, IntoIter = E>,
>(
prefix: &str,
data: I,
indent: usize,
f: &mut fmt::Formatter,
) -> Result<(), fmt::Error> {
let data = data.into_iter();
if data.len() == 0 {
return Ok(());
}
writeln_indented!(indent, f, "{}: [", prefix);
for v in data {
writeln_indented!(indent + 1, f, "{:?}", v);
}
writeln_indented!(indent, f, "]");
Ok(())
}
fn print_node(
node: &Node,
indent: usize,
f: &mut fmt::Formatter,
) -> Result<(), fmt::Error> {
if node.is_element() {
writeln_indented!(indent, f, "Element {{");
writeln_indented!(indent, f, " tag_name: {:?}", node.tag_name());
print_into_iter("attributes", node.attributes(), indent + 1, f)?;
if node.has_children() {
writeln_indented!(indent, f, " children: [");
print_children(node, indent + 2, f)?;
writeln_indented!(indent, f, " ]");
}
writeln_indented!(indent, f, "}}");
} else if node.is_text() {
writeln_indented!(indent, f, "Text {{");
writeln_indented!(indent, f, " \"{}\"", node.text().unwrap_or("No text"));
writeln_indented!(indent, f, "}}");
} else {
writeln_indented!(indent, f, "Unknown Node!");
}
Ok(())
}
fn print_children(
parent: &Node,
indent: usize,
f: &mut fmt::Formatter,
) -> Result<(), fmt::Error> {
for child in parent.children() {
print_node(&child, indent, f)?;
}
Ok(())
}
writeln!(f, "Document [")?;
print_node(&root, 1, f)?;
writeln!(f, "]")?;
Ok(())
} else {
write!(f, "Document [No root node]")?;
Ok(())
}
}
}
pub struct Nodes<'a> {
front: Option<Node<'a>>,
back: Option<Node<'a>>,
}
impl<'a> Nodes<'a> {
#[inline]
#[must_use]
pub fn new(document: &'a Document) -> Self {
let last_node_idx = document.last_node_idx();
if last_node_idx == 0 {
return Nodes {
front: None,
back: None,
};
}
let front = document.get_node(1);
let back = document.get_node(last_node_idx);
Nodes {
front: front.ok(),
back: back.ok(),
}
}
#[inline]
#[must_use]
pub fn descendants(document: &'a Document, node_idx: NodeIdx) -> Self {
match document.last_descendant(node_idx) {
None => Nodes {
front: None,
back: None,
},
Some(last_node_idx) => Nodes {
front: document.get_node(node_idx + 1).ok(),
back: document.get_node(last_node_idx).ok(),
},
}
}
}
impl<'a> Iterator for Nodes<'a> {
type Item = Node<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.front == self.back {
let node = self.front.take();
self.back = None;
node
} else {
let node = self.front.take();
self.front = node.as_ref().and_then(|n| n.doc.next_seq_node(n.idx()));
node
}
}
}
#[cfg(not(feature = "forward_only"))]
impl DoubleEndedIterator for Nodes<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.back == self.front {
let node = self.back.take();
self.front = None;
node
} else {
let node = self.back.take();
self.back = node.as_ref().and_then(|n| n.doc.previous_seq_node(n.idx()));
node
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_document_creation() {
let xml_data = b"<root><child>Text</child><totototo/></root>".to_vec();
let document = Document::new(xml_data).unwrap();
println!("Document created: {:#?}", document);
}
}