[librsvg] node.rs: Implement rsvg_node_ref() and rsvg_node_unref()



commit 2477434b4c8ec179b372817d464104ba06f20b30
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Feb 15 19:44:40 2017 -0600

    node.rs: Implement rsvg_node_ref() and rsvg_node_unref()
    
    In Rust, we have Rc<Node> for the main refcounted node struct.
    
    We export that to C by boxing as Box<Rc<Node>>, since an Rc<Node>
    normally lives in the stack - that is, the Rc is in the stack, and the
    Node and its refcount are in the heap.
    
    When we ref() a node, we return a new Box:
    
      Box::into_raw (Box::new (node.clone ()))
    
    This means that in C, "x = rsvg_node_ref (y)" gives you back a pointer x
    that is different from y!  Both refer to the same Node in the Rust
    world, and after that the node has one more refcount, as expected.
    
    When we unref() a node, we expect you to call "x = rsvg_node_unref (x)".
    This returns NULL, so your x=NULL after that, i.e. the node that was in
    x is not accessible anymore.

 rsvg-base.c       |    2 +-
 rsvg-private.h    |   15 ++++++++---
 rust/src/cnode.rs |    8 +++---
 rust/src/lib.rs   |    1 +
 rust/src/node.rs  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 5 files changed, 84 insertions(+), 13 deletions(-)
---
diff --git a/rsvg-base.c b/rsvg-base.c
index 3397a42..449d8de 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -426,7 +426,7 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
             ctx->priv->treebase = newnode;
         }
 
-        ctx->priv->currentnode = newnode;
+        ctx->priv->currentnode = rsvg_node_ref (newnode);
 
         node_set_atts (newnode, ctx, creator, atts);
     }
diff --git a/rsvg-private.h b/rsvg-private.h
index be830db..1d24f2f 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -339,6 +339,7 @@ typedef void (* CNodeDraw) (RsvgNode *node, gpointer impl, RsvgDrawingCtx *ctx,
 typedef void (* CNodeFree) (gpointer impl);
 
 /* Implemented in rust/src/node.rs */
+/* Call node = rsvg_node_unref (node) when you are done with the node */
 G_GNUC_INTERNAL
 RsvgNode *rsvg_rust_cnode_new (RsvgNodeType  node_type,
                                RsvgNode     *parent,
@@ -346,7 +347,7 @@ RsvgNode *rsvg_rust_cnode_new (RsvgNodeType  node_type,
                                gpointer      impl,
                                CNodeSetAtts  set_atts_fn,
                                CNodeDraw     draw_fn,
-                               CNodeFree     free_fn);
+                               CNodeFree     free_fn) G_GNUC_WARN_UNUSED_RESULT;
 
 /* Implemented in rust/src/node.rs */
 G_GNUC_INTERNAL
@@ -357,8 +358,14 @@ G_GNUC_INTERNAL
 RsvgNodeType rsvg_node_get_type (RsvgNode *node);
 
 /* Implemented in rust/src/node.rs */
+/* Call this as newref = rsvg_node_ref (node);  You don't own the node anymore, just the newref! */
 G_GNUC_INTERNAL
-void rsvg_node_unref (RsvgNode *node);
+RsvgNode *rsvg_node_ref (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
+
+/* Implemented in rust/src/node.rs */
+/* Call this as node = rsvg_node_unref (node);  Then node will be NULL and you don't own it anymore! */
+G_GNUC_INTERNAL
+RsvgNode *rsvg_node_unref (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
 
 /* Implemented in rust/src/node.rs */
 G_GNUC_INTERNAL
@@ -366,11 +373,11 @@ RsvgState *rsvg_node_get_state (RsvgNode *node);
 
 /* Implemented in rust/src/node.rs
  *
- * Returns a strong reference to the parent (or NULL); use rsvg_node_unref()
+ * Returns a new strong reference to the parent (or NULL); use rsvg_node_unref()
  * when you are done.
  */
 G_GNUC_INTERNAL
-RsvgNode *rsvg_node_get_parent (RsvgNode *node);
+RsvgNode *rsvg_node_get_parent (RsvgNode *node) G_GNUC_WARN_UNUSED_RESULT;
 
 /* Implemented in rust/src/node.rs */
 G_GNUC_INTERNAL
diff --git a/rust/src/cnode.rs b/rust/src/cnode.rs
index 2ad4a69..d97367f 100644
--- a/rust/src/cnode.rs
+++ b/rust/src/cnode.rs
@@ -64,10 +64,10 @@ pub extern fn rsvg_rust_cnode_new (node_type:   NodeType,
         free_fn:     free_fn
     };
 
-    Box::into_raw (Box::new (Rc::new (Node::new (node_type,
-                                                 parent,
-                                                 state,
-                                                 Box::new (cnode)))))
+    box_node (Rc::new (Node::new (node_type,
+                                  parent,
+                                  state,
+                                  Box::new (cnode))))
 }
 
 #[no_mangle]
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 7ea6920..a3cea12 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -57,6 +57,7 @@ pub use length::{
 pub use node::{
     rsvg_node_get_type,
     rsvg_node_get_parent,
+    rsvg_node_ref,
     rsvg_node_unref,
     rsvg_node_get_state,
     rsvg_node_add_child,
diff --git a/rust/src/node.rs b/rust/src/node.rs
index 8946ef0..5ede878 100644
--- a/rust/src/node.rs
+++ b/rust/src/node.rs
@@ -4,6 +4,7 @@ use std::rc::Rc;
 use std::rc::Weak;
 use std::cell::RefCell;
 use std::ptr;
+use std::mem;
 
 use drawing_ctx::RsvgDrawingCtx;
 
@@ -140,7 +141,7 @@ extern "C" {
 
 impl Drop for Node {
     fn drop (&mut self) {
-        unsafe { rsvg_state_free (self.state); }
+//        unsafe { rsvg_state_free (self.state); }
     }
 }
 
@@ -152,6 +153,10 @@ pub extern fn rsvg_node_get_type (raw_node: *const RsvgNode) -> NodeType {
     node.get_type ()
 }
 
+pub fn box_node (node: RsvgNode) -> *mut RsvgNode {
+    Box::into_raw (Box::new (node))
+}
+
 #[no_mangle]
 pub extern fn rsvg_node_get_parent (raw_node: *const RsvgNode) -> *const RsvgNode {
     assert! (!raw_node.is_null ());
@@ -162,16 +167,26 @@ pub extern fn rsvg_node_get_parent (raw_node: *const RsvgNode) -> *const RsvgNod
 
         Some (ref weak_node) => {
             let strong_node = weak_node.upgrade ().unwrap ();
-            Box::into_raw (Box::new (strong_node))
+            box_node (strong_node)
         }
     }
 }
 
 #[no_mangle]
-pub unsafe extern fn rsvg_node_unref (raw_node: *mut RsvgNode) {
+pub unsafe extern fn rsvg_node_ref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
     assert! (!raw_node.is_null ());
+    let node: &RsvgNode = & *raw_node;
 
-    let _ = Box::from_raw (raw_node);
+    box_node (node.clone ())
+}
+
+#[no_mangle]
+pub extern fn rsvg_node_unref (raw_node: *mut RsvgNode) -> *mut RsvgNode {
+    if !raw_node.is_null () {
+        let _ = unsafe { Box::from_raw (raw_node) };
+    }
+
+    ptr::null_mut () // so the caller can do "node = rsvg_node_unref (node);" and lose access to the node
 }
 
 #[no_mangle]
@@ -223,3 +238,51 @@ pub extern fn rsvg_node_foreach_child (raw_node: *const RsvgNode, func: NodeFore
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use std::rc::Rc;
+    use drawing_ctx::RsvgDrawingCtx;
+    use handle::RsvgHandle;
+    use property_bag::RsvgPropertyBag;
+    use state::RsvgState;
+    use super::*;
+    use std::ptr;
+    use std::mem;
+
+    struct TestNodeImpl {}
+
+    impl NodeTrait for TestNodeImpl {
+        fn set_atts (&self, node: &RsvgNode, handle: *const RsvgHandle, pbag: *const RsvgPropertyBag) {
+        }
+
+        fn draw (&self, node: &RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: i32) {
+        }
+
+        fn get_c_impl (&self) -> *const RsvgCNodeImpl {
+            return ptr::null ();
+        }
+    }
+
+    #[test]
+    fn node_refs_and_unrefs () {
+        let node = Rc::new (Node::new (NodeType::Path,
+                                       None,
+                                       ptr::null_mut (),
+                                       Box::new (TestNodeImpl {})));
+
+        let mut ref1 = box_node (node);
+
+        let new_node: &mut RsvgNode = unsafe { &mut *ref1 };
+        let weak = Rc::downgrade (new_node);
+
+        let mut ref2 = unsafe { rsvg_node_ref (new_node) };
+        assert! (weak.upgrade ().is_some ());
+
+        ref2 = unsafe { rsvg_node_unref (ref2) };
+        assert! (weak.upgrade ().is_some ());
+
+        ref1 = unsafe { rsvg_node_unref (ref1) };
+        assert! (weak.upgrade ().is_none ());
+    }
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]