radix_tree 1.0.0 → 1.1.0.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README +2 -6
  2. data/lib/radix_tree.rb +80 -77
  3. metadata +4 -10
data/README CHANGED
@@ -7,17 +7,13 @@ to avoid DoS via Algorithmic Complexity Attacks.
7
7
 
8
8
  == Performance
9
9
 
10
- * 25 times slower for 10 bytes key, 100000 elements insertion
10
+ * 20 times slower for 10 bytes key, 100000 elements insertion
11
11
  * 10 times slower for 10 bytes key, 100000 elements retrieval
12
12
 
13
13
 
14
14
  == TODO
15
15
 
16
- Implement following features for utilizing strength of Radix Tree.
17
- * find predecessor
18
- * find successor
19
- * find_all by start string
20
- * delete_all by start string
16
+ See comments in lib/radix_tree.rb
21
17
 
22
18
 
23
19
  == Author
data/lib/radix_tree.rb CHANGED
@@ -30,11 +30,16 @@ class RadixTree
30
30
  class Node
31
31
  UNDEFINED = Object.new
32
32
 
33
- attr_reader :key, :value
33
+ attr_reader :key, :index
34
+ attr_reader :value
34
35
  attr_reader :children
35
36
 
36
- def initialize(key, value = UNDEFINED, children = nil)
37
- @key, @value, @children = key, value, children
37
+ def initialize(key, index, value = UNDEFINED, children = nil)
38
+ @key, @index, @value, @children = key, index, value, children
39
+ end
40
+
41
+ def label
42
+ @key[0, @index]
38
43
  end
39
44
 
40
45
  def undefined?
@@ -54,49 +59,63 @@ class RadixTree
54
59
  end
55
60
  end
56
61
 
57
- def each(prefix, &block)
58
- prefix += @key
62
+ def each(&block)
59
63
  if @value != UNDEFINED
60
- block.call(prefix, @value)
64
+ block.call [label, @value]
61
65
  end
62
66
  if @children
63
- @children.each do |key, child|
64
- child.each(prefix, &block)
67
+ @children.each_value do |child|
68
+ child.each(&block)
65
69
  end
66
70
  end
67
71
  end
72
+
73
+ def each_key
74
+ each do |k, v|
75
+ yield k
76
+ end
77
+ end
78
+
79
+ def each_value
80
+ each do |k, v|
81
+ yield v
82
+ end
83
+ end
68
84
 
69
- def keys(prefix)
70
- collect(prefix) { |k, v| k }
85
+ def keys
86
+ collect { |k, v| k }
71
87
  end
72
88
 
73
- def values(prefix)
74
- collect(prefix) { |k, v| v }
89
+ def values
90
+ collect { |k, v| v }
75
91
  end
76
92
 
77
93
  def store(key, value)
78
- if @key == key
94
+ if same_key?(key)
79
95
  @value = value
80
96
  else
81
- index = head_match_length(key)
82
- if index == @key.size
83
- push(key[index..-1], value)
97
+ pos = head_match_length(key)
98
+ if pos == @index
99
+ push(key, value)
84
100
  else
85
- split(index)
86
- # search again after split the node
87
- store(key, value)
101
+ split(pos)
102
+ if same_key?(key)
103
+ @value = value
104
+ else
105
+ push(key, value)
106
+ end
88
107
  end
89
108
  end
90
109
  end
91
110
 
92
111
  def retrieve(key)
93
- if @key == key
112
+ if same_key?(key)
94
113
  @value
95
114
  elsif !@children
96
115
  UNDEFINED
97
116
  else
98
- key = child_key(key)
99
- if child = find_child(key)
117
+ pos = head_match_length(key)
118
+ if child = find_child(key[pos])
100
119
  child.retrieve(key)
101
120
  else
102
121
  UNDEFINED
@@ -105,14 +124,14 @@ class RadixTree
105
124
  end
106
125
 
107
126
  def delete(key)
108
- if @key == key
127
+ if same_key?(key)
109
128
  value, @value = @value, UNDEFINED
110
129
  value
111
130
  elsif !@children
112
131
  nil
113
132
  else
114
- key = child_key(key)
115
- if child = find_child(key)
133
+ pos = head_match_length(key)
134
+ if child = find_child(key[pos])
116
135
  value = child.delete(key)
117
136
  if value and child.undefined?
118
137
  reap(child)
@@ -122,44 +141,46 @@ class RadixTree
122
141
  end
123
142
  end
124
143
 
125
- def dump_tree(io, prefix = '', indent = '')
126
- prefix += @key
144
+ def dump_tree(io, indent = '')
127
145
  indent += ' '
128
146
  if undefined?
129
- io << sprintf("#<%s:0x%010x %s>", self.class.name, __id__, prefix.inspect)
147
+ io << sprintf("#<%s:0x%010x %s>", self.class.name, __id__, label.inspect)
130
148
  else
131
- io << sprintf("#<%s:0x%010x %s> => %s", self.class.name, __id__, prefix.inspect, @value.inspect)
149
+ io << sprintf("#<%s:0x%010x %s> => %s", self.class.name, __id__, label.inspect, @value.inspect)
132
150
  end
133
151
  if @children
134
152
  @children.each do |k, v|
135
153
  io << $/ + indent
136
- v.dump_tree(io, prefix, indent)
154
+ v.dump_tree(io, indent)
137
155
  end
138
156
  end
139
157
  end
140
158
 
141
159
  private
142
160
 
143
- def collect(prefix)
161
+ def same_key?(key)
162
+ @index == key.size and @key.start_with?(key)
163
+ end
164
+
165
+ def collect
144
166
  pool = []
145
- each(prefix) do |key, value|
167
+ each do |key, value|
146
168
  pool << yield(key, value)
147
169
  end
148
170
  pool
149
171
  end
150
172
 
151
173
  def push(key, value)
152
- if child = find_child(key)
174
+ if @children && child = find_child(key[@index])
153
175
  child.store(key, value)
154
176
  else
155
- add_child(Node.new(key, value))
177
+ add_child(Node.new(key, key.size, value))
156
178
  end
157
179
  end
158
180
 
159
- def split(index)
160
- @key, new_key = @key[0, index], @key[index..-1]
161
- child = Node.new(new_key, @value, @children)
162
- @value, @children = UNDEFINED, nil
181
+ def split(pos)
182
+ child = Node.new(@key, @index, @value, @children)
183
+ @index, @value, @children = pos, UNDEFINED, nil
163
184
  add_child(child)
164
185
  end
165
186
 
@@ -169,38 +190,32 @@ class RadixTree
169
190
  elsif child.children.size == 1
170
191
  # pull up the grand child as a child
171
192
  delete_child(child)
172
- grand = child.children.values.first
173
- add_child(Node.new(child.key + grand.key, grand.value, grand.children))
193
+ add_child(child.children.shift[1])
174
194
  end
175
195
  end
176
196
 
177
- def child_key(key)
178
- key[head_match_length(key)..-1]
179
- end
180
-
181
- # assert: check != @key
182
197
  def head_match_length(check)
183
- 0.upto(check.size) do |index|
184
- if check[index] != @key[index]
185
- return index
198
+ 0.upto(@index) do |idx|
199
+ if check[idx] != @key[idx]
200
+ return idx
186
201
  end
187
202
  end
188
- raise 'assert: check != @key'
203
+ @index
189
204
  end
190
205
 
191
- def find_child(key)
192
- if @children
193
- @children[key[0]]
194
- end
206
+ def find_child(char)
207
+ @children[char]
195
208
  end
196
209
 
197
210
  def add_child(child)
211
+ char = child.key[@index]
198
212
  @children ||= {}
199
- @children[child.key[0]] = child
213
+ @children[char] = child
200
214
  end
201
215
 
202
216
  def delete_child(child)
203
- @children.delete(child.key[0])
217
+ char = child.key[@index]
218
+ @children.delete(char)
204
219
  if @children.empty?
205
220
  @children = nil
206
221
  end
@@ -216,7 +231,7 @@ class RadixTree
216
231
  if block && default != DEFAULT
217
232
  raise ArgumentError, 'wrong number of arguments'
218
233
  end
219
- @root = Node.new('')
234
+ @root = Node.new('', 0)
220
235
  @default = default
221
236
  @default_proc = block
222
237
  end
@@ -232,58 +247,46 @@ class RadixTree
232
247
 
233
248
  def each(&block)
234
249
  if block_given?
235
- @root.each('', &block)
250
+ @root.each(&block)
236
251
  self
237
252
  else
238
- Enumerator.new { |yielder|
239
- @root.each('') do |k, v|
240
- yielder << [k, v]
241
- end
242
- }
253
+ Enumerator.new(@root)
243
254
  end
244
255
  end
245
256
  alias each_pair each
246
257
 
247
258
  def each_key
248
259
  if block_given?
249
- @root.each('') do |k, v|
260
+ @root.each do |k, v|
250
261
  yield k
251
262
  end
252
263
  self
253
264
  else
254
- Enumerator.new { |yielder|
255
- @root.each('') do |k, v|
256
- yielder << k
257
- end
258
- }
265
+ Enumerator.new(@root, :each_key)
259
266
  end
260
267
  end
261
268
 
262
269
  def each_value
263
270
  if block_given?
264
- @root.each('') do |k, v|
271
+ @root.each do |k, v|
265
272
  yield v
266
273
  end
267
274
  self
268
275
  else
269
- Enumerator.new { |yielder|
270
- @root.each('') do |k, v|
271
- yielder << v
272
- end
273
- }
276
+ Enumerator.new(@root, :each_value)
274
277
  end
275
278
  end
276
279
 
277
280
  def keys
278
- @root.keys('')
281
+ @root.keys
279
282
  end
280
283
 
281
284
  def values
282
- @root.values('')
285
+ @root.values
283
286
  end
284
287
 
285
288
  def clear
286
- @root = Node.new('')
289
+ @root = Node.new('', 0)
287
290
  end
288
291
 
289
292
  def []=(key, value)
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: radix_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
5
- prerelease:
4
+ version: 1.1.0.dev
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Hiroshi Nakamura
@@ -33,18 +33,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
33
33
  - - ! '>='
34
34
  - !ruby/object:Gem::Version
35
35
  version: '0'
36
- segments:
37
- - 0
38
- hash: -966188071225850011
39
36
  required_rubygems_version: !ruby/object:Gem::Requirement
40
37
  none: false
41
38
  requirements:
42
- - - ! '>='
39
+ - - ! '>'
43
40
  - !ruby/object:Gem::Version
44
- version: '0'
45
- segments:
46
- - 0
47
- hash: -966188071225850011
41
+ version: 1.3.1
48
42
  requirements: []
49
43
  rubyforge_project:
50
44
  rubygems_version: 1.8.11