[librsvg: 37/48] Don't use dynamic dispatch for the XML handlers; we only have three cases anyway



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]