rpatricia 0.05 → 0.06

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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