radix_tree 1.0.0 → 1.1.0.dev

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.
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