From 916b7be928c1a7c6b4aa8ab2f58987487a78777e Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 1 Sep 2010 18:51:41 -0700 Subject: enable Patricia#dup to work Instead of defining the Patricia.new singleton method, we only need to define the allocation function. The allocation function allows "dup" and "clone" methods to be called on Patricia instances and we will walk the original tree to copy node prefixes and data into the new one. This also allows us to subclass Patricia and create objects from that subclass. --- ext/rpatricia/rpatricia.c | 36 ++++++++++++++++++++++++++++++++---- test/test_duplicate.rb | 28 ++++++++++++++++++++++++++++ test/test_gc.rb | 15 +++++++++++++-- test/test_subclass.rb | 11 +++++++++++ 4 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 test/test_duplicate.rb create mode 100644 test/test_subclass.rb 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 #include "patricia.h" +#include 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 -- cgit v1.2.3-24-ge0c7