diff options
author | Eric Wong <normalperson@yhbt.net> | 2010-09-01 18:51:41 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2010-09-01 18:53:45 -0700 |
commit | 916b7be928c1a7c6b4aa8ab2f58987487a78777e (patch) | |
tree | f0ec9a034870972323384de67e1019c5bc5de451 | |
parent | 28316a1d951d8dd5a1c8b8eb31149f82dd29ff3b (diff) | |
download | rpatricia-916b7be928c1a7c6b4aa8ab2f58987487a78777e.tar.gz |
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.
-rw-r--r-- | ext/rpatricia/rpatricia.c | 36 | ||||
-rw-r--r-- | test/test_duplicate.rb | 28 | ||||
-rw-r--r-- | test/test_gc.rb | 15 | ||||
-rw-r--r-- | test/test_subclass.rb | 11 |
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 |