about summary refs log tree commit
diff options
context:
space:
mode:
-rw-r--r--ext/rpatricia/rpatricia.c36
-rw-r--r--test/test_duplicate.rb28
-rw-r--r--test/test_gc.rb15
-rw-r--r--test/test_subclass.rb11
4 files changed, 84 insertions, 6 deletions
diff --git a/ext/rpatricia/rpatricia.c b/ext/rpatricia/rpatricia.c
index f395eea..f9564d2 100644
--- a/ext/rpatricia/rpatricia.c
+++ b/ext/rpatricia/rpatricia.c
@@ -6,6 +6,7 @@
 #include "ruby.h"
 #include <stdlib.h>
 #include "patricia.h"
+#include <assert.h>
 
 static VALUE cPatricia, cNode;
 
@@ -245,11 +246,37 @@ p_tree_free (void *ptr)
 }
 
 static VALUE
-p_new (VALUE self)
+p_alloc(VALUE klass)
 {
   patricia_tree_t *tree;
   tree = New_Patricia(32); /* assuming only IPv4 */
-  return Data_Wrap_Struct(cPatricia, p_tree_mark, p_tree_free, tree);
+
+  return Data_Wrap_Struct(klass, p_tree_mark, p_tree_free, tree);
+}
+
+static VALUE
+p_init_copy(VALUE self, VALUE orig)
+{
+  patricia_tree_t *orig_tree;
+
+  Data_Get_Struct(orig, patricia_tree_t, orig_tree);
+  if (orig_tree->head) {
+    patricia_tree_t *tree;
+    patricia_node_t *orig_node, *node;
+    prefix_t prefix;
+    VALUE user_data;
+
+    Data_Get_Struct(self, patricia_tree_t, tree);
+    PATRICIA_WALK(orig_tree->head, orig_node) {
+      node = patricia_lookup(tree, orig_node->prefix);
+      assert(node->prefix == orig_node->prefix);
+
+      user_data = (VALUE)(orig_node->data);
+      if (T_STRING == TYPE(user_data))
+        user_data = rb_str_dup(user_data);
+      PATRICIA_DATA_SET(node, user_data);
+    } PATRICIA_WALK_END;
+  }
 }
 
 void
@@ -258,8 +285,9 @@ Init_rpatricia (void)
   cPatricia = rb_define_class("Patricia", rb_cObject);
   cNode = rb_define_class_under(cPatricia, "Node", rb_cObject);
 
-  /* create new Patricia object */
-  rb_define_singleton_method(cPatricia, "new", p_new, 0);
+  /* allocate new Patricia object, called before initialize  */
+  rb_define_alloc_func(cPatricia, p_alloc);
+  rb_define_method(cPatricia, "initialize_copy", p_init_copy, 1);
 
   /*---------- methods to tree ----------*/
   /* add string */
diff --git a/test/test_duplicate.rb b/test/test_duplicate.rb
new file mode 100644
index 0000000..c50a211
--- /dev/null
+++ b/test/test_duplicate.rb
@@ -0,0 +1,28 @@
+require 'test/unit'
+require 'rpatricia'
+require 'stringio'
+
+class TestDuplicate < Test::Unit::TestCase
+  def test_dup
+    tmp = {}
+    t = Patricia.new
+    t.add('127.0.0.0/8', tmp)
+    t2 = t.dup
+    assert_equal 1, t2.num_nodes
+    assert_equal tmp.object_id, t2.match_best('127.0.0.1').data.object_id
+    t2.add('10.0.0.0/8', zz = [])
+    assert_equal 2, t2.num_nodes
+    assert_equal 1, t.num_nodes
+
+    oldout = $stdout
+    begin
+      $stdout = stringio = StringIO.new
+      t2.show_nodes
+      puts "--"
+      t.show_nodes
+    ensure
+      $stdout = oldout
+    end
+    p stringio.string
+  end
+end
diff --git a/test/test_gc.rb b/test/test_gc.rb
index 5829deb..316d355 100644
--- a/test/test_gc.rb
+++ b/test/test_gc.rb
@@ -15,9 +15,20 @@ class TestGc < Test::Unit::TestCase
     @strings.add('127.0.0.0/24', "localhost")
   end
 
+  def test_gc_dup
+    100000.times do
+      tmp = @strings.dup
+      tmp.remove '127.0.0.0/24'
+      tmp = @arrays.dup
+      tmp.remove '127.0.0.0/24'
+    end
+    assert_equal [], @arrays.match_best('127.0.0.1').data
+    assert_equal "localhost", @strings.match_best('127.0.0.1').data
+  end
+
   def test_gc
     assert_nothing_raised do
-      5_000_000.times do
+      500_000.times do
         t = Patricia.new
         t.add('10.0.0.0/8', {})
         t.add('127.0.0.0/24', "home sweet home")
@@ -25,7 +36,7 @@ class TestGc < Test::Unit::TestCase
     end
 
     # ensure what we created originally didn't get GC-ed'
-    5_000_000.times do
+    500_000.times do
       assert_equal [], @arrays.match_best('127.0.0.1').data
       assert_equal "localhost", @strings.match_best('127.0.0.1').data
     end
diff --git a/test/test_subclass.rb b/test/test_subclass.rb
new file mode 100644
index 0000000..013bfdd
--- /dev/null
+++ b/test/test_subclass.rb
@@ -0,0 +1,11 @@
+require 'test/unit'
+require 'rpatricia'
+
+class SubPatricia < Patricia
+end
+
+class TestSubclass < Test::Unit::TestCase
+  def test_new
+    assert_equal SubPatricia, SubPatricia.new.class
+  end
+end