use crate::binxml::value_variant::{BinXmlValue, BinXmlValueType};
use crate::utils::Utf16LeSlice;
use bumpalo::Bump;
use bumpalo::collections::Vec as BumpVec;
use std::mem::ManuallyDrop;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Name<'a> {
value: &'a str,
}
impl<'a> Name<'a> {
pub fn new(value: &'a str) -> Self {
Name { value }
}
pub fn as_str(&self) -> &str {
self.value
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Text<'a> {
Utf16(Utf16LeSlice<'a>),
Utf8(&'a str),
}
impl<'a> Text<'a> {
pub fn utf16(value: Utf16LeSlice<'a>) -> Self {
Text::Utf16(value)
}
pub fn utf8(value: &'a str) -> Self {
Text::Utf8(value)
}
pub fn is_empty(&self) -> bool {
match self {
Text::Utf16(value) => value.is_empty(),
Text::Utf8(value) => value.is_empty(),
}
}
pub fn as_utf8(&self) -> Option<&str> {
match self {
Text::Utf16(_) => None,
Text::Utf8(value) => Some(value),
}
}
pub fn as_utf16(&self) -> Option<Utf16LeSlice<'_>> {
match self {
Text::Utf16(value) => Some(*value),
Text::Utf8(_) => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Node<'a> {
Element(ElementId),
Text(Text<'a>),
Value(BinXmlValue<'a>),
EntityRef(Name<'a>),
CharRef(u16),
CData(Text<'a>),
PITarget(Name<'a>),
PIData(Text<'a>),
Placeholder(Placeholder),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Placeholder {
pub id: u16,
pub value_type: BinXmlValueType,
pub optional: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Attr<'a> {
pub name: Name<'a>,
pub value: IrVec<'a, Node<'a>>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TemplateValue<'a> {
Value(BinXmlValue<'a>),
BinXmlElement(ElementId),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Element<'a> {
pub name: Name<'a>,
pub attrs: IrVec<'a, Attr<'a>>,
pub children: IrVec<'a, Node<'a>>,
pub has_element_child: bool,
}
impl<'a> Element<'a> {
pub fn new_in(name: Name<'a>, arena: &'a Bump) -> Self {
Element {
name,
attrs: IrVec::new_in(arena),
children: IrVec::new_in(arena),
has_element_child: false,
}
}
pub fn push_child(&mut self, node: Node<'a>) {
if matches!(node, Node::Element(_)) {
self.has_element_child = true;
}
self.children.push(node);
}
}
pub type IrVec<'a, T> = BumpVec<'a, T>;
pub type ElementId = usize;
#[derive(Debug, Clone)]
pub struct IrArena<'a> {
elements: IrVec<'a, Element<'a>>,
}
impl<'a> IrArena<'a> {
pub fn new_in(arena: &'a Bump) -> Self {
IrArena {
elements: IrVec::new_in(arena),
}
}
pub fn with_capacity_in(capacity: usize, arena: &'a Bump) -> Self {
IrArena {
elements: IrVec::with_capacity_in(capacity, arena),
}
}
pub fn new_node(&mut self, element: Element<'a>) -> ElementId {
let id = self.elements.len();
self.elements.push(element);
id
}
pub fn reserve(&mut self, additional: usize) {
self.elements.reserve(additional);
}
pub fn count(&self) -> usize {
self.elements.len()
}
pub fn get(&self, id: ElementId) -> Option<&Element<'a>> {
self.elements.get(id)
}
pub fn get_mut(&mut self, id: ElementId) -> Option<&mut Element<'a>> {
self.elements.get_mut(id)
}
}
#[derive(Debug, Clone)]
pub struct IrTree<'a> {
arena: ManuallyDrop<IrArena<'a>>,
root: ElementId,
}
impl<'a> IrTree<'a> {
pub fn new(arena: IrArena<'a>, root: ElementId) -> Self {
IrTree {
arena: ManuallyDrop::new(arena),
root,
}
}
pub fn root(&self) -> ElementId {
self.root
}
pub fn arena(&self) -> &IrArena<'a> {
&self.arena
}
pub fn arena_mut(&mut self) -> &mut IrArena<'a> {
&mut self.arena
}
pub fn root_element(&self) -> &Element<'a> {
self.element(self.root)
}
pub fn element(&self, id: ElementId) -> &Element<'a> {
self.arena().get(id).expect("invalid element id")
}
pub fn element_mut(&mut self, id: ElementId) -> &mut Element<'a> {
self.arena_mut().get_mut(id).expect("invalid element id")
}
}
pub(crate) fn is_optional_empty(value: &BinXmlValue<'_>) -> bool {
match value {
BinXmlValue::NullType => true,
BinXmlValue::StringType(s) => s.is_empty(),
BinXmlValue::AnsiStringType(s) => s.is_empty(),
BinXmlValue::BinaryType(bytes) => bytes.is_empty(),
BinXmlValue::BinXmlType(bytes) => bytes.is_empty(),
BinXmlValue::StringArrayType(v) => v.is_empty(),
BinXmlValue::Int8ArrayType(v) => v.is_empty(),
BinXmlValue::UInt8ArrayType(v) => v.is_empty(),
BinXmlValue::Int16ArrayType(v) => v.is_empty(),
BinXmlValue::UInt16ArrayType(v) => v.is_empty(),
BinXmlValue::Int32ArrayType(v) => v.is_empty(),
BinXmlValue::UInt32ArrayType(v) => v.is_empty(),
BinXmlValue::Int64ArrayType(v) => v.is_empty(),
BinXmlValue::UInt64ArrayType(v) => v.is_empty(),
BinXmlValue::Real32ArrayType(v) => v.is_empty(),
BinXmlValue::Real64ArrayType(v) => v.is_empty(),
BinXmlValue::BoolArrayType(v) => v.is_empty(),
BinXmlValue::GuidArrayType(v) => v.is_empty(),
BinXmlValue::FileTimeArrayType(v) => v.is_empty(),
BinXmlValue::SysTimeArrayType(v) => v.is_empty(),
BinXmlValue::SidArrayType(v) => v.is_empty(),
BinXmlValue::HexInt32ArrayType(v) => v.is_empty(),
BinXmlValue::HexInt64ArrayType(v) => v.is_empty(),
_ => false,
}
}
pub(crate) fn is_optional_empty_template_value(value: &TemplateValue<'_>) -> bool {
match value {
TemplateValue::BinXmlElement(_) => false,
TemplateValue::Value(value) => is_optional_empty(value),
}
}
#[cfg(test)]
mod drop_free_tests {
use super::*;
use crate::binxml::value_variant::BinXmlValue;
#[test]
fn ir_and_value_types_are_drop_free() {
assert!(!std::mem::needs_drop::<Name<'static>>());
assert!(!std::mem::needs_drop::<Text<'static>>());
assert!(!std::mem::needs_drop::<BinXmlValue<'static>>());
assert!(!std::mem::needs_drop::<Node<'static>>());
assert!(!std::mem::needs_drop::<IrTree<'static>>());
}
}