[librsvg: 37/48] Don't use dynamic dispatch for the XML handlers; we only have three cases anyway
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 37/48] Don't use dynamic dispatch for the XML handlers; we only have three cases anyway
- Date: Sat, 17 Nov 2018 10:23:08 +0000 (UTC)
commit 6d6d3b859dcbd2c1d6298eb2a630de4e0a92717f
Author: Federico Mena Quintero <federico gnome org>
Date: Wed Nov 7 12:46:17 2018 -0600
Don't use dynamic dispatch for the XML handlers; we only have three cases anyway
rsvg_internals/src/xml.rs | 571 +++++++++++++++++++++++-----------------------
1 file changed, 283 insertions(+), 288 deletions(-)
---
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index 362af7dc..32b2ebae 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -3,6 +3,7 @@ use encoding::DecoderTrap;
use libc;
use std;
use std::cell::RefCell;
+use std::mem;
use std::ptr;
use std::rc::Rc;
use std::str;
@@ -18,71 +19,275 @@ use text::NodeChars;
use tree::{RsvgTree, Tree};
use util::utf8_cstr;
-/// A trait for processing a certain kind of XML subtree
-///
-/// In the "normal" state of processing, an `XmlHandler` may create an RsvgNode
-/// for each SVG element it finds, and create NodeChars inside those nodes when it
-/// encounters character data.
-///
-/// There may be other, special contexts for different subtrees, for example,
-/// for the `<style>` element.
-trait XmlHandler {
- /// Called when the XML parser sees the beginning of an element
- fn start_element(
- &self,
- previous_handler: Option<&XmlHandler>,
- parent: Option<&Rc<Node>>,
- handle: *mut RsvgHandle,
- name: &str,
- pbag: &PropertyBag,
- ) -> Box<XmlHandler>;
+// struct XIncludeContext {
+// needs_fallback: bool,
+// }
+//
+// impl XmlHandler for XIncludeContext {
+// fn start_element(
+// &self,
+// _previous_handler: Option<&XmlHandler>,
+// _parent: Option<&Rc<Node>>,
+// handle: *mut RsvgHandle,
+// _name: &str,
+// pbag: &PropertyBag,
+// ) -> Box<XmlHandler> {
+// let mut href = None;
+// let mut parse = None;
+// let mut encoding = None;
+//
+// for (_key, attr, value) in pbag.iter() {
+// match attr {
+// Attribute::Href => href = Some(value),
+// Attribute::Parse => parse = Some(value),
+// Attribute::Encoding => encoding = Some(value),
+// _ => (),
+// }
+// }
+//
+// self.acquire(handle, href, parse, encoding);
+//
+// unimplemented!("finish start_xinclude() here");
+//
+// Box::new(XIncludeContext::empty())
+// }
+//
+// fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
+// unimplemented!();
+// }
+//
+// fn characters(&self, text: &str) {
+// unimplemented!();
+// }
+// }
+//
+// impl XIncludeContext {
+// fn empty() -> XIncludeContext {
+// XIncludeContext {
+// needs_fallback: true,
+// }
+// }
+//
+// fn acquire(
+// &self,
+// handle: *mut RsvgHandle,
+// href: Option<&str>,
+// parse: Option<&str>,
+// encoding: Option<&str>,
+// ) {
+// if let Some(href) = href {
+// if parse == Some("text") {
+// self.acquire_text(handle, href, encoding);
+// } else {
+// unimplemented!("finish the xml case here");
+// }
+// }
+// }
+//
+// fn acquire_text(&self, handle: *mut RsvgHandle, href: &str, encoding: Option<&str>) {
+// let binary = match handle::acquire_data(handle, href) {
+// Ok(b) => b,
+// Err(e) => {
+// rsvg_log!("could not acquire \"{}\": {}", href, e);
+// return;
+// }
+// };
+//
+// let encoding = encoding.unwrap_or("utf-8");
+//
+// let encoder = match encoding_from_whatwg_label(encoding) {
+// Some(enc) => enc,
+// None => {
+// rsvg_log!("unknown encoding \"{}\" for \"{}\"", encoding, href);
+// return;
+// }
+// };
+//
+// let utf8_data = match encoder.decode(&binary.data, DecoderTrap::Strict) {
+// Ok(data) => data,
+//
+// Err(e) => {
+// rsvg_log!(
+// "could not convert contents of \"{}\" from character encoding \"{}\": {}",
+// href,
+// encoding,
+// e
+// );
+// return;
+// }
+// };
+//
+// unimplemented!("rsvg_xml_state_characters(utf8_data)");
+// }
+// }
+enum ContextKind {
+ // Starting state
+ Start,
+
+ // Creating nodes for elements under the given parent
+ ElementCreation(Rc<Node>),
+
+ // Inside a <style> element
+ Style(StyleContext),
+
+ // An element inside a <style> context, to be ignored
+ UnsupportedStyleChild,
+
+ // Inside <xi:include>
+ XInclude,
+}
- /// Called when the XML parser sees the end of an element.
- fn end_element(&self, handle: *mut RsvgHandle, name: &str) -> Option<Rc<Node>>;
+/// Handles the `<style>` element by parsing its character contents as CSS
+struct StyleContext {
+ is_text_css: bool,
+ text: String,
+}
- /// Called when the XML parser sees character data or CDATA
- fn characters(&self, text: &str);
+/// A concrete parsing context for a surrounding `element_name` and its XML event handlers
+struct Context {
+ element_name: String,
+ kind: ContextKind,
+}
- fn get_node(&self) -> Option<Rc<Node>> {
- None
+impl Context {
+ fn empty() -> Context {
+ Context {
+ element_name: String::new(),
+ kind: ContextKind::Start,
+ }
}
}
-struct NodeCreationContext {
- node: Option<Rc<Node>>,
+// A *const RsvgXmlState is just the type that we export to C
+pub enum RsvgXmlState {}
+
+/// Holds the state used for XML processing
+///
+/// These methods are called when an XML event is parsed out of the XML stream: `start_element`,
+/// `end_element`, `characters`.
+///
+/// When an element starts, we push a corresponding `Context` into the `context_stack`. Within
+/// that context, all XML events will be forwarded to it, and processed in one of the `XmlHandler`
+/// trait objects. Normally the context refers to a `NodeCreationContext` implementation which is
+/// what creates normal graphical elements.
+///
+/// When we get to a `<style>` element, we push a `StyleContext`, which processes its contents
+/// specially.
+struct XmlState {
+ tree: Option<Box<Tree>>,
+ context: Context,
+ context_stack: Vec<Context>,
+}
+
+fn style_characters(style_ctx: &mut StyleContext, text: &str) {
+ style_ctx.text.push_str(text);
}
-impl XmlHandler for NodeCreationContext {
- fn start_element(
+impl XmlState {
+ fn new() -> XmlState {
+ XmlState {
+ tree: None,
+ context: Context::empty(),
+ context_stack: Vec::new(),
+ }
+ }
+
+ pub fn set_root(&mut self, root: &Rc<Node>) {
+ if self.tree.is_some() {
+ panic!("The tree root has already been set");
+ }
+
+ self.tree = Some(Box::new(Tree::new(root)));
+ }
+
+ pub fn steal_tree(&mut self) -> Option<Box<Tree>> {
+ self.tree.take()
+ }
+
+ fn push_context(&mut self, ctx: Context) {
+ let top = mem::replace(&mut self.context, ctx);
+ self.context_stack.push(top);
+ }
+
+ pub fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag) {
+ let new_ctx = match self.context.kind {
+ ContextKind::Start => self.element_creation_start_element(None, handle, name, pbag),
+ ContextKind::ElementCreation(ref parent) => {
+ let parent = parent.clone();
+ self.element_creation_start_element(Some(&parent), handle, name, pbag)
+ }
+ ContextKind::Style(_) => self.style_start_element(true, name, pbag),
+ ContextKind::UnsupportedStyleChild => self.style_start_element(true, name, pbag),
+ ContextKind::XInclude => self.xinclude_start_element(handle, name, pbag),
+ };
+
+ self.push_context(new_ctx);
+ }
+
+ pub fn end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
+ // We can unwrap since start_element() always adds a context to the stack
+ let top = self.context_stack.pop().unwrap();
+
+ let is_root = if let ContextKind::Start = top.kind {
+ true
+ } else {
+ false
+ };
+
+ let context = mem::replace(&mut self.context, top);
+
+ assert!(context.element_name == name);
+
+ match context.kind {
+ ContextKind::Start => panic!("end_element: XML handler stack is empty!?"),
+ ContextKind::ElementCreation(node) => {
+ self.element_creation_end_element(is_root, node, handle)
+ }
+ ContextKind::Style(style_ctx) => self.style_end_element(style_ctx, handle),
+ ContextKind::UnsupportedStyleChild => (),
+ ContextKind::XInclude => self.xinclude_end_element(handle, name),
+ }
+ }
+
+ pub fn characters(&mut self, text: &str) {
+ match self.context.kind {
+ ContextKind::Start => panic!("characters: XML handler stack is empty!?"),
+ ContextKind::ElementCreation(ref parent) => {
+ self.element_creation_characters(parent, text)
+ }
+ ContextKind::Style(ref mut style_ctx) => style_characters(style_ctx, text),
+ ContextKind::UnsupportedStyleChild => (),
+ ContextKind::XInclude => self.xinclude_characters(text),
+ }
+ }
+
+ fn element_creation_start_element(
&self,
- _previous_handler: Option<&XmlHandler>,
parent: Option<&Rc<Node>>,
handle: *mut RsvgHandle,
name: &str,
pbag: &PropertyBag,
- ) -> Box<XmlHandler> {
+ ) -> Context {
match name {
- "include" => {
- let ctx = XIncludeContext::empty();
- ctx.start_element(Some(self), parent, handle, name, pbag)
- }
-
- "style" => {
- let ctx = StyleContext::empty();
- ctx.start_element(Some(self), parent, handle, name, pbag)
- }
-
+ "include" => unimplemented!(),
+ "style" => self.style_start_element(false, name, pbag),
_ => {
let node = self.create_node(parent, handle, name, pbag);
- Box::new(NodeCreationContext { node: Some(node) })
+ Context {
+ element_name: name.to_string(),
+ kind: ContextKind::ElementCreation(node),
+ }
}
}
}
- fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
- let node = self.node.as_ref().unwrap().clone();
-
+ fn element_creation_end_element(
+ &mut self,
+ is_root: bool,
+ node: Rc<Node>,
+ handle: *mut RsvgHandle,
+ ) {
// The "svg" node is special; it parses its style attributes
// here, not during element creation.
if node.get_type() == NodeType::Svg {
@@ -91,23 +296,19 @@ impl XmlHandler for NodeCreationContext {
});
}
- Some(node)
- }
-
- fn characters(&self, text: &str) {
- let node = self.node.as_ref().unwrap();
-
- if text.len() == 0 {
- return;
+ if is_root {
+ self.set_root(&node);
}
+ }
- if node.accept_chars() {
+ fn element_creation_characters(&self, node: &Rc<Node>, text: &str) {
+ if text.len() != 0 && node.accept_chars() {
let chars_node = if let Some(child) = node.find_last_chars_child() {
child
} else {
let child = node_new(
NodeType::Chars,
- Some(&node),
+ Some(node),
"rsvg-chars",
None,
None,
@@ -123,16 +324,6 @@ impl XmlHandler for NodeCreationContext {
}
}
- fn get_node(&self) -> Option<Rc<Node>> {
- Some(self.node.as_ref().unwrap().clone())
- }
-}
-
-impl NodeCreationContext {
- fn empty() -> NodeCreationContext {
- NodeCreationContext { node: None }
- }
-
fn create_node(
&self,
parent: Option<&Rc<Node>>,
@@ -160,27 +351,22 @@ impl NodeCreationContext {
new_node
}
-}
-/// Handles the `<style>` element by parsing its character contents as CSS
-struct StyleContext {
- is_text_css: bool,
- text: RefCell<String>,
-}
+ fn style_start_element(&self, inside_style: bool, name: &str, pbag: &PropertyBag) -> Context {
+ if inside_style {
+ // We are already inside a <style> element, and we don't support
+ // elements in there. Just push a state that we will ignore.
+
+ return Context {
+ element_name: name.to_string(),
+ kind: ContextKind::UnsupportedStyleChild,
+ };
+ }
-impl XmlHandler for StyleContext {
- fn start_element(
- &self,
- _previous_handler: Option<&XmlHandler>,
- _parent: Option<&Rc<Node>>,
- _handle: *mut RsvgHandle,
- _name: &str,
- pbag: &PropertyBag,
- ) -> Box<XmlHandler> {
// FIXME: See these:
//
- // https://www.w3.org/TR/SVG/styling.html#StyleElementTypeAttribute
- // https://www.w3.org/TR/SVG/styling.html#ContentStyleTypeAttribute
+ // https://www.w3.org/TR/SVG11/styling.html#StyleElementTypeAttribute
+ // https://www.w3.org/TR/SVG11/styling.html#ContentStyleTypeAttribute
//
// If the "type" attribute is not present, we should fallback to the
// "contentStyleType" attribute of the svg element, which in turn
@@ -197,227 +383,36 @@ impl XmlHandler for StyleContext {
}
}
- Box::new(StyleContext {
- is_text_css,
- text: RefCell::new(String::new()),
- })
- }
-
- fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
- if self.is_text_css {
- let text = self.text.borrow();
- css::parse_into_handle(handle, &text);
+ Context {
+ element_name: name.to_string(),
+ kind: ContextKind::Style(StyleContext {
+ is_text_css,
+ text: String::new(),
+ }),
}
-
- None
}
- fn characters(&self, text: &str) {
- self.text.borrow_mut().push_str(text);
- }
-}
-
-impl StyleContext {
- fn empty() -> StyleContext {
- StyleContext {
- is_text_css: false,
- text: RefCell::new(String::new()),
+ fn style_end_element(&mut self, style_ctx: StyleContext, handle: *mut RsvgHandle) {
+ if style_ctx.is_text_css {
+ css::parse_into_handle(handle, &style_ctx.text);
}
}
-}
-struct XIncludeContext {
- needs_fallback: bool,
-}
-
-impl XmlHandler for XIncludeContext {
- fn start_element(
- &self,
- _previous_handler: Option<&XmlHandler>,
- _parent: Option<&Rc<Node>>,
+ fn xinclude_start_element(
+ &mut self,
handle: *mut RsvgHandle,
- _name: &str,
+ name: &str,
pbag: &PropertyBag,
- ) -> Box<XmlHandler> {
- let mut href = None;
- let mut parse = None;
- let mut encoding = None;
-
- for (_key, attr, value) in pbag.iter() {
- match attr {
- Attribute::Href => href = Some(value),
- Attribute::Parse => parse = Some(value),
- Attribute::Encoding => encoding = Some(value),
- _ => (),
- }
- }
-
- self.acquire(handle, href, parse, encoding);
-
- unimplemented!("finish start_xinclude() here");
-
- Box::new(XIncludeContext::empty())
- }
-
- fn end_element(&self, handle: *mut RsvgHandle, _name: &str) -> Option<Rc<Node>> {
+ ) -> Context {
unimplemented!();
}
- fn characters(&self, text: &str) {
+ fn xinclude_end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
unimplemented!();
}
-}
-
-impl XIncludeContext {
- fn empty() -> XIncludeContext {
- XIncludeContext {
- needs_fallback: true,
- }
- }
-
- fn acquire(
- &self,
- handle: *mut RsvgHandle,
- href: Option<&str>,
- parse: Option<&str>,
- encoding: Option<&str>,
- ) {
- if let Some(href) = href {
- if parse == Some("text") {
- self.acquire_text(handle, href, encoding);
- } else {
- unimplemented!("finish the xml case here");
- }
- }
- }
-
- fn acquire_text(&self, handle: *mut RsvgHandle, href: &str, encoding: Option<&str>) {
- let binary = match handle::acquire_data(handle, href) {
- Ok(b) => b,
- Err(e) => {
- rsvg_log!("could not acquire \"{}\": {}", href, e);
- return;
- }
- };
-
- let encoding = encoding.unwrap_or("utf-8");
-
- let encoder = match encoding_from_whatwg_label(encoding) {
- Some(enc) => enc,
- None => {
- rsvg_log!("unknown encoding \"{}\" for \"{}\"", encoding, href);
- return;
- }
- };
-
- let utf8_data = match encoder.decode(&binary.data, DecoderTrap::Strict) {
- Ok(data) => data,
-
- Err(e) => {
- rsvg_log!(
- "could not convert contents of \"{}\" from character encoding \"{}\": {}",
- href,
- encoding,
- e
- );
- return;
- }
- };
-
- unimplemented!("rsvg_xml_state_characters(utf8_data)");
- }
-}
-
-/// A concrete parsing context for a surrounding `element_name` and its XML event handlers
-struct Context {
- element_name: String,
- handler: Box<XmlHandler>,
-}
-
-// A *const RsvgXmlState is just the type that we export to C
-pub enum RsvgXmlState {}
-
-/// Holds the state used for XML processing
-///
-/// These methods are called when an XML event is parsed out of the XML stream: `start_element`,
-/// `end_element`, `characters`.
-///
-/// When an element starts, we push a corresponding `Context` into the `context_stack`. Within
-/// that context, all XML events will be forwarded to it, and processed in one of the `XmlHandler`
-/// trait objects. Normally the context refers to a `NodeCreationContext` implementation which is
-/// what creates normal graphical elements.
-///
-/// When we get to a `<style>` element, we push a `StyleContext`, which processes its contents
-/// specially.
-struct XmlState {
- tree: Option<Box<Tree>>,
-
- context_stack: Vec<Context>,
-}
-
-impl XmlState {
- fn new() -> XmlState {
- XmlState {
- tree: None,
- context_stack: Vec::new(),
- }
- }
-
- pub fn set_root(&mut self, root: &Rc<Node>) {
- if self.tree.is_some() {
- panic!("The tree root has already been set");
- }
-
- self.tree = Some(Box::new(Tree::new(root)));
- }
-
- pub fn steal_tree(&mut self) -> Option<Box<Tree>> {
- self.tree.take()
- }
-
- pub fn start_element(&mut self, handle: *mut RsvgHandle, name: &str, pbag: &PropertyBag) {
- let next_context = if let Some(top) = self.context_stack.last() {
- top.handler.start_element(
- Some(&*top.handler),
- top.handler.get_node().as_ref(),
- handle,
- name,
- pbag,
- )
- } else {
- let default_context = NodeCreationContext::empty();
-
- default_context.start_element(None, None, handle, name, pbag)
- };
-
- let context = Context {
- element_name: name.to_string(),
- handler: next_context,
- };
-
- self.context_stack.push(context);
- }
-
- pub fn end_element(&mut self, handle: *mut RsvgHandle, name: &str) {
- if let Some(top) = self.context_stack.pop() {
- assert!(name == top.element_name);
-
- if let Some(node) = top.handler.end_element(handle, name) {
- if self.context_stack.is_empty() {
- self.set_root(&node);
- }
- }
- } else {
- panic!("end_element: XML handler stack is empty!?");
- }
- }
- pub fn characters(&mut self, text: &str) {
- if let Some(top) = self.context_stack.last() {
- top.handler.characters(text);
- } else {
- panic!("characters: XML handler stack is empty!?");
- }
+ fn xinclude_characters(&mut self, text: &str) {
+ unimplemented!();
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]