rpatricia 0.05 → 0.06

Sign up to get free protection for your applications and to get access to all the features.
data/Changes CHANGED
@@ -1,3 +1,19 @@
1
+ 0.06 2010/09/14
2
+ - rely on Ruby GC for allocations/free
3
+ no need to explicitly destroy objects anymore
4
+ - Ruby 1.9.2 C API compatibility
5
+ - allow creation of subclasses of Patricia class
6
+ - non-String objects may be stored as node-data
7
+ - Patricia::Node objects are returned when match succeeds
8
+ It's no longer possible to call tree methods on node
9
+ objects and cause segfaults.
10
+ - Patricia#show_nodes may be given a custom IO-like object
11
+ - sync docs with remove, match_best and match_exact behavior
12
+ - ArgumentError is raised for invalid addresses
13
+ no more assertion failures for bad addresses
14
+ - Patricia#show_nodes and Patricia#num_nodes no longer
15
+ segfaults on empty trees
16
+
1
17
  0.05 2010/03/12
2
18
  - reorganized directory layout
3
19
  no chance of accidentally having 'extconf' in the require path
data/README CHANGED
@@ -60,10 +60,9 @@ new:
60
60
 
61
61
  pt = Patricia.new
62
62
 
63
- This is the class' constructor - it returns a Patricia object upon
64
- success or nil on failure. For now, the constructor takes no
65
- arguments, and defaults to creating a tree which uses AF_INET IPv4
66
- address and mask values as keys.
63
+ This is the class' constructor - it returns a Patricia object. For
64
+ now, the constructor takes no arguments, and defaults to creating a
65
+ tree which uses AF_INET IPv4 address and mask values as keys.
67
66
 
68
67
  The Patricia object will be destroyed automatically when there are
69
68
  no longer any references to it.
@@ -124,6 +123,14 @@ search_exact:
124
123
 
125
124
  match_exact: An alias of search_exact.
126
125
 
126
+ include?:
127
+
128
+ pt.include?(key_string);
129
+
130
+ This method behaves like match_best, but returns true on success
131
+ and false on failure. This method is more efficient than match_best
132
+ as it does not allocate a new object.
133
+
127
134
  remove:
128
135
 
129
136
  pt.remove(key_string);
@@ -132,7 +139,7 @@ remove:
132
139
  and mask specified from the Patricia Trie.
133
140
 
134
141
  If the matching node is found in the Patricia Trie, it is removed,
135
- and this method returns the true. This method returns nil on
142
+ and this method returns the true. This method returns false on
136
143
  failure.
137
144
 
138
145
  remove_node: An alias of remove
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require "mkmf"
3
3
 
4
+ have_func("ruby_xcalloc")
5
+ have_func("rb_str_set_len", "ruby.h")
4
6
  create_makefile("rpatricia")
5
-
@@ -31,7 +31,13 @@ static char copyright[] =
31
31
 
32
32
  #include "patricia.h"
33
33
 
34
- #define Delete free
34
+ #if defined(HAVE_RUBY_XCALLOC)
35
+ # include <ruby.h>
36
+ # define calloc xcalloc
37
+ # define Delete xfree
38
+ #else
39
+ # define Delete free
40
+ #endif
35
41
 
36
42
  /* { from prefix.c */
37
43
 
@@ -4,28 +4,57 @@
4
4
  */
5
5
 
6
6
  #include "ruby.h"
7
- #include <stdio.h> /* printf */
8
7
  #include <stdlib.h>
9
8
  #include "patricia.h"
9
+ #include <assert.h>
10
10
 
11
- VALUE cPatricia;
11
+ static VALUE cPatricia, cNode;
12
12
 
13
- void dummy(void) {}
13
+ static void dummy(void) {}
14
14
 
15
15
  static VALUE
16
16
  p_destroy (VALUE self)
17
17
  {
18
18
  patricia_tree_t *tree;
19
+
19
20
  Data_Get_Struct(self, patricia_tree_t, tree);
20
- Destroy_Patricia(tree, free);
21
+ Clear_Patricia(tree, dummy);
22
+ tree->head = NULL; /* Clear_Patricia() should do this, actually */
23
+
21
24
  return Qtrue;
22
25
  }
23
26
 
24
- VALUE
27
+ static void
28
+ p_node_mark (void *ptr)
29
+ {
30
+ patricia_node_t *node = ptr;
31
+
32
+ rb_gc_mark((VALUE)node->data);
33
+ }
34
+
35
+ static VALUE
36
+ wrap_node(patricia_node_t *node)
37
+ {
38
+ /* node will be freed when parent is freed */
39
+ return Data_Wrap_Struct(cNode, p_node_mark, 0, node);
40
+ }
41
+
42
+ static prefix_t *
43
+ my_ascii2prefix(int family, VALUE str)
44
+ {
45
+ char *cstr = StringValuePtr(str);
46
+ prefix_t *prefix = ascii2prefix(family, cstr);
47
+
48
+ if (!prefix)
49
+ rb_raise(rb_eArgError, "invalid prefix: %s", cstr);
50
+
51
+ return prefix;
52
+ }
53
+
54
+ static VALUE
25
55
  p_add (int argc, VALUE *argv, VALUE self)
26
56
  {
27
- int str_len;
28
- char *user_data;
57
+ VALUE user_data;
29
58
  patricia_tree_t *tree;
30
59
  patricia_node_t *node;
31
60
  prefix_t *prefix;
@@ -34,33 +63,34 @@ p_add (int argc, VALUE *argv, VALUE self)
34
63
  return Qnil;
35
64
 
36
65
  Data_Get_Struct(self, patricia_tree_t, tree);
37
- prefix = ascii2prefix(AF_INET, STR2CSTR(argv[0]));
66
+ prefix = my_ascii2prefix(AF_INET, argv[0]);
38
67
  node = patricia_lookup(tree, prefix);
39
68
  Deref_Prefix(prefix);
40
69
 
41
70
  if (argc == 2) {
42
- user_data = STR2CSTR(argv[1]);
43
- str_len = strlen(user_data);
44
- node->data = (char *) malloc((str_len + 1) * sizeof(char));
45
- sprintf((char *)node->data, user_data);
71
+ user_data = argv[1];
72
+
73
+ /* for backwards compatibility, we always dup and return new strings */
74
+ if (TYPE(user_data) == T_STRING)
75
+ user_data = rb_str_dup(user_data);
46
76
  } else {
47
- node->data = (char *) malloc(sizeof(char));
48
- sprintf((char *)node->data, "");
77
+ user_data = rb_str_new(NULL, 0);
49
78
  }
50
- return Data_Wrap_Struct(cPatricia, 0, 0, node);
79
+ PATRICIA_DATA_SET(node, user_data);
80
+
81
+ /* node will be freed when parent is freed */
82
+ return wrap_node(node);
51
83
  }
52
84
 
53
85
  static VALUE
54
86
  p_remove (VALUE self, VALUE r_key)
55
87
  {
56
- char *c_key;
57
88
  patricia_tree_t *tree;
58
89
  patricia_node_t *node;
59
90
  prefix_t *prefix;
60
91
 
61
92
  Data_Get_Struct(self, patricia_tree_t, tree);
62
- c_key = STR2CSTR(r_key);
63
- prefix = ascii2prefix (AF_INET, c_key);
93
+ prefix = my_ascii2prefix (AF_INET, r_key);
64
94
  node = patricia_search_exact(tree, prefix);
65
95
  Deref_Prefix (prefix);
66
96
 
@@ -80,15 +110,26 @@ p_match (VALUE self, VALUE r_key)
80
110
  prefix_t *prefix;
81
111
 
82
112
  Data_Get_Struct(self, patricia_tree_t, tree);
83
- prefix = ascii2prefix (AF_INET, STR2CSTR(r_key));
113
+ prefix = my_ascii2prefix (AF_INET, r_key);
84
114
  node = patricia_search_best(tree, prefix);
85
115
  Deref_Prefix (prefix);
86
116
 
87
- if (node)
88
- return Data_Wrap_Struct(cPatricia, 0, 0, node);
89
- else
90
- return Qfalse;
117
+ return node ? wrap_node(node) : Qnil;
118
+ }
119
+
120
+ static VALUE
121
+ p_include (VALUE self, VALUE r_key)
122
+ {
123
+ patricia_tree_t *tree;
124
+ patricia_node_t *node;
125
+ prefix_t *prefix;
126
+
127
+ Data_Get_Struct(self, patricia_tree_t, tree);
128
+ prefix = my_ascii2prefix (AF_INET, r_key);
129
+ node = patricia_search_best(tree, prefix);
130
+ Deref_Prefix (prefix);
91
131
 
132
+ return node ? Qtrue : Qfalse;
92
133
  }
93
134
 
94
135
  static VALUE
@@ -99,14 +140,11 @@ p_match_exact (VALUE self, VALUE r_key)
99
140
  prefix_t *prefix;
100
141
 
101
142
  Data_Get_Struct(self, patricia_tree_t, tree);
102
- prefix = ascii2prefix (AF_INET, STR2CSTR(r_key));
143
+ prefix = my_ascii2prefix (AF_INET, r_key);
103
144
  node = patricia_search_exact(tree, prefix);
104
145
  Deref_Prefix (prefix);
105
146
 
106
- if (node)
107
- return Data_Wrap_Struct(cPatricia, 0, 0, node);
108
- else
109
- return Qfalse;
147
+ return node ? wrap_node(node) : Qnil;
110
148
  }
111
149
 
112
150
  static VALUE
@@ -116,33 +154,63 @@ p_num_nodes (VALUE self)
116
154
  patricia_tree_t *tree;
117
155
 
118
156
  Data_Get_Struct(self, patricia_tree_t, tree);
119
- n = patricia_walk_inorder(tree->head, dummy);
157
+ n = tree->head ? patricia_walk_inorder(tree->head, dummy) : 0;
120
158
 
121
159
  return INT2NUM(n);
122
160
  }
123
161
 
162
+ /* needed for Ruby 1.8.6, in 1.8.7 and later */
163
+ #ifndef HAVE_RB_STR_SET_LEN
164
+ static void
165
+ rb_str_set_len(VALUE str, long len)
166
+ {
167
+ RSTRING(str)->len = len;
168
+ RSTRING(str)->ptr[len] = '\0';
169
+ }
170
+ #endif
171
+
124
172
  static VALUE
125
- p_print_nodes (VALUE self)
173
+ p_print_nodes (int argc, VALUE *argv, VALUE self)
126
174
  {
127
- int n;
128
- char buff[32];
175
+ ID id_printf = rb_intern("printf");
176
+ VALUE fmt = rb_str_new2("node: %s\n");
177
+ VALUE buf = rb_str_buf_new(128);
178
+ char *cbuf;
129
179
  patricia_tree_t *tree;
130
180
  patricia_node_t *node;
181
+ VALUE out;
131
182
  Data_Get_Struct(self, patricia_tree_t, tree);
132
183
 
133
- PATRICIA_WALK(tree->head, node) {
134
- prefix_toa2x(node->prefix, buff, 1);
135
- printf("node: %s\n", buff);
136
- } PATRICIA_WALK_END;
184
+ rb_scan_args(argc, argv, "01", &out);
185
+ if (NIL_P(out))
186
+ out = rb_stdout;
187
+
188
+ if (tree->head) {
189
+ PATRICIA_WALK(tree->head, node) {
190
+ rb_str_resize(buf, 128);
191
+ cbuf = RSTRING_PTR(buf);
192
+ prefix_toa2x(node->prefix, cbuf, 1);
193
+ rb_str_set_len(buf, strlen(cbuf));
194
+ rb_funcall(out, id_printf, 2, fmt, buf);
195
+ } PATRICIA_WALK_END;
196
+ }
137
197
  return Qtrue;
138
198
  }
139
199
 
140
200
  static VALUE
141
201
  p_data (VALUE self)
142
202
  {
203
+ VALUE user_data;
143
204
  patricia_node_t *node;
144
205
  Data_Get_Struct(self, patricia_node_t, node);
145
- return rb_str_new2((char *)node->data);
206
+
207
+ user_data = (VALUE)node->data;
208
+
209
+ /* for backwards compatibility, we always dup and return new strings */
210
+ if (TYPE(user_data) == T_STRING)
211
+ user_data = rb_str_dup(user_data);
212
+
213
+ return user_data;
146
214
  }
147
215
 
148
216
  static VALUE
@@ -173,21 +241,75 @@ p_prefixlen (VALUE self)
173
241
  return INT2NUM(node->prefix->bitlen);
174
242
  }
175
243
 
244
+ /* called during GC for each node->data attached to a Patricia tree */
245
+ static void
246
+ p_tree_mark_each(prefix_t *prefix, void *data)
247
+ {
248
+ VALUE user_data = (VALUE)data;
249
+
250
+ rb_gc_mark(user_data);
251
+ }
252
+
253
+ static void
254
+ p_tree_mark (void *ptr)
255
+ {
256
+ patricia_tree_t *tree = ptr;
257
+
258
+ patricia_process(tree, p_tree_mark_each);
259
+ }
260
+
261
+ static void
262
+ p_tree_free (void *ptr)
263
+ {
264
+ patricia_tree_t *tree = ptr;
265
+
266
+ /* no need to explicitly free each node->data, GC will do it for us */
267
+ Destroy_Patricia(tree, NULL);
268
+ }
269
+
176
270
  static VALUE
177
- p_new (VALUE self)
271
+ p_alloc(VALUE klass)
178
272
  {
179
273
  patricia_tree_t *tree;
180
274
  tree = New_Patricia(32); /* assuming only IPv4 */
181
- return Data_Wrap_Struct(cPatricia, 0, 0, tree);
275
+
276
+ return Data_Wrap_Struct(klass, p_tree_mark, p_tree_free, tree);
277
+ }
278
+
279
+ static VALUE
280
+ p_init_copy(VALUE self, VALUE orig)
281
+ {
282
+ patricia_tree_t *orig_tree;
283
+
284
+ Data_Get_Struct(orig, patricia_tree_t, orig_tree);
285
+ if (orig_tree->head) {
286
+ patricia_tree_t *tree;
287
+ patricia_node_t *orig_node, *node;
288
+ prefix_t prefix;
289
+ VALUE user_data;
290
+
291
+ Data_Get_Struct(self, patricia_tree_t, tree);
292
+ PATRICIA_WALK(orig_tree->head, orig_node) {
293
+ node = patricia_lookup(tree, orig_node->prefix);
294
+ assert(node->prefix == orig_node->prefix);
295
+
296
+ user_data = (VALUE)(orig_node->data);
297
+ if (T_STRING == TYPE(user_data))
298
+ user_data = rb_str_dup(user_data);
299
+ PATRICIA_DATA_SET(node, user_data);
300
+ } PATRICIA_WALK_END;
301
+ }
182
302
  }
183
303
 
184
304
  void
185
305
  Init_rpatricia (void)
186
306
  {
187
307
  cPatricia = rb_define_class("Patricia", rb_cObject);
308
+ cNode = rb_define_class_under(cPatricia, "Node", rb_cObject);
188
309
 
189
- /* create new Patricia object */
190
- rb_define_singleton_method(cPatricia, "new", p_new, 0);
310
+ /* allocate new Patricia object, called before initialize */
311
+ rb_define_alloc_func(cPatricia, p_alloc);
312
+ rb_define_method(cPatricia, "initialize_copy", p_init_copy, 1);
191
313
 
192
314
  /*---------- methods to tree ----------*/
193
315
  /* add string */
@@ -202,24 +324,27 @@ Init_rpatricia (void)
202
324
  rb_define_method(cPatricia, "match_exact", p_match_exact, 1);
203
325
  rb_define_method(cPatricia, "search_exact", p_match_exact, 1);
204
326
 
327
+ /* check existence */
328
+ rb_define_method(cPatricia, "include?", p_include, 1);
329
+
205
330
  /* removal */
206
331
  rb_define_method(cPatricia, "remove", p_remove, 1);
207
332
  rb_define_method(cPatricia, "remove_node", p_remove, 1);
208
333
 
209
334
  /* derivatives of climb */
210
335
  rb_define_method(cPatricia, "num_nodes", p_num_nodes, 0);
211
- rb_define_method(cPatricia, "show_nodes", p_print_nodes, 0);
336
+ rb_define_method(cPatricia, "show_nodes", p_print_nodes, -1);
212
337
 
213
338
  /* destroy tree */
214
339
  rb_define_method(cPatricia, "destroy", p_destroy, 0);
215
340
  rb_define_method(cPatricia, "clear", p_destroy, 0);
216
341
 
217
342
  /*---------- methods to node ----------*/
218
- rb_define_method(cPatricia, "data", p_data, 0);
219
- rb_define_method(cPatricia, "show_data", p_data, 0);
220
- rb_define_method(cPatricia, "network", p_network, 0);
221
- rb_define_method(cPatricia, "prefix", p_prefix, 0);
222
- rb_define_method(cPatricia, "prefixlen", p_prefixlen, 0);
343
+ rb_define_method(cNode, "data", p_data, 0);
344
+ rb_define_method(cNode, "show_data", p_data, 0);
345
+ rb_define_method(cNode, "network", p_network, 0);
346
+ rb_define_method(cNode, "prefix", p_prefix, 0);
347
+ rb_define_method(cNode, "prefixlen", p_prefixlen, 0);
223
348
  // rb_define_method(cPatricia, "family", p_family, 0);
224
349
 
225
350
  }
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = %q{rpatricia}
6
- s.version = %q{0.05} # remember to update Changes if this is changed
6
+ s.version = %q{0.06} # remember to update Changes if this is changed
7
7
 
8
8
  s.homepage = "http://www.goto.info.waseda.ac.jp/~tatsuya/rpatricia/"
9
9
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpatricia
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.05"
4
+ hash: 7
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 6
9
+ version: "0.06"
5
10
  platform: ruby
6
11
  authors:
7
12
  - Tatsuya Mori
@@ -10,7 +15,7 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2010-03-15 00:00:00 +00:00
18
+ date: 2010-09-15 00:00:00 +00:00
14
19
  default_executable:
15
20
  dependencies: []
16
21
 
@@ -51,21 +56,27 @@ rdoc_options: []
51
56
  require_paths:
52
57
  - lib
53
58
  required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
54
60
  requirements:
55
61
  - - ">="
56
62
  - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
57
66
  version: "0"
58
- version:
59
67
  required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
60
69
  requirements:
61
70
  - - ">="
62
71
  - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
63
75
  version: "0"
64
- version:
65
76
  requirements: []
66
77
 
67
78
  rubyforge_project:
68
- rubygems_version: 1.3.5
79
+ rubygems_version: 1.3.7
69
80
  signing_key:
70
81
  specification_version: 3
71
82
  summary: module for fast IP address/prefix lookups