hashery 1.5.0 → 2.0.0
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/.ruby +30 -17
- data/.yardopts +1 -0
- data/Config.rb +28 -0
- data/{QED.rdoc → DEMO.rdoc} +0 -0
- data/HISTORY.rdoc +37 -0
- data/LICENSE.txt +26 -0
- data/NOTICE.txt +46 -0
- data/README.rdoc +10 -7
- data/lib/hashery.rb +6 -6
- data/lib/hashery.yml +30 -17
- data/lib/hashery/association.rb +169 -109
- data/lib/hashery/casting_hash.rb +128 -135
- data/lib/hashery/core_ext.rb +89 -61
- data/lib/hashery/crud_hash.rb +365 -0
- data/lib/hashery/dictionary.rb +545 -345
- data/lib/hashery/fuzzy_hash.rb +177 -125
- data/lib/hashery/ini_hash.rb +321 -0
- data/lib/hashery/key_hash.rb +54 -179
- data/lib/hashery/linked_list.rb +245 -191
- data/lib/hashery/lru_hash.rb +292 -202
- data/lib/hashery/open_cascade.rb +133 -78
- data/lib/hashery/open_hash.rb +127 -61
- data/lib/hashery/ordered_hash.rb +128 -122
- data/lib/hashery/path_hash.rb +238 -0
- data/lib/hashery/property_hash.rb +144 -80
- data/lib/hashery/query_hash.rb +85 -29
- data/lib/hashery/stash.rb +7 -3
- data/lib/hashery/static_hash.rb +46 -41
- data/test/case_association.rb +65 -4
- data/test/case_dictionary.rb +149 -5
- data/test/{case_keyhash.rb → case_key_hash.rb} +20 -14
- data/test/case_lru_hash.rb +162 -0
- data/test/{case_opencascade.rb → case_open_cascade.rb} +4 -8
- data/test/case_open_hash.rb +87 -0
- data/test/case_query_hash.rb +226 -0
- data/test/helper.rb +8 -0
- metadata +33 -63
- data/COPYING.rdoc +0 -45
- data/lib/hashery/basic_object.rb +0 -74
- data/lib/hashery/basic_struct.rb +0 -288
- data/lib/hashery/basicobject.rb +0 -1
- data/lib/hashery/basicstruct.rb +0 -1
- data/lib/hashery/castinghash.rb +0 -1
- data/lib/hashery/fuzzyhash.rb +0 -1
- data/lib/hashery/ini.rb +0 -268
- data/lib/hashery/keyhash.rb +0 -1
- data/lib/hashery/linkedlist.rb +0 -1
- data/lib/hashery/lruhash.rb +0 -1
- data/lib/hashery/memoizer.rb +0 -64
- data/lib/hashery/open_object.rb +0 -1
- data/lib/hashery/opencascade.rb +0 -1
- data/lib/hashery/openhash.rb +0 -1
- data/lib/hashery/openobject.rb +0 -1
- data/lib/hashery/orderedhash.rb +0 -1
- data/lib/hashery/ostructable.rb +0 -186
- data/lib/hashery/propertyhash.rb +0 -1
- data/lib/hashery/queryhash.rb +0 -1
- data/lib/hashery/statichash.rb +0 -1
- data/qed/01_openhash.rdoc +0 -57
- data/qed/02_queryhash.rdoc +0 -21
- data/qed/03_castinghash.rdoc +0 -13
- data/qed/04_statichash.rdoc +0 -22
- data/qed/05_association.rdoc +0 -59
- data/qed/06_opencascade.rdoc +0 -58
- data/qed/07_fuzzyhash.rdoc +0 -141
- data/qed/08_properyhash.rdoc +0 -38
- data/qed/09_ostructable.rdoc +0 -56
- data/qed/applique/ae.rb +0 -1
- data/test/case_basicstruct.rb +0 -192
- data/test/case_openhash.rb +0 -22
data/lib/hashery/lru_hash.rb
CHANGED
@@ -1,273 +1,363 @@
|
|
1
1
|
require 'enumerator'
|
2
2
|
|
3
|
-
|
4
|
-
# LRUHash. When adding more elements old elements are removed according
|
5
|
-
# to LRU policy.
|
6
|
-
#
|
7
|
-
# http://github.com/rklemme/muppet-laboratories/blob/master/lib/lruhash.rb
|
8
|
-
# Copyright (c) 2010 Robert Klemme
|
3
|
+
module Hashery
|
9
4
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
5
|
+
# Hash with LRU expiry policy. There are at most max_size elements in a
|
6
|
+
# LRUHash. When adding more elements old elements are removed according
|
7
|
+
# to LRU policy.
|
8
|
+
#
|
9
|
+
# Based on Robert Klemme's LRUHash class.
|
10
|
+
#
|
11
|
+
# LRUHash, Copyright (c) 2010 Robert Klemme.
|
12
|
+
#
|
13
|
+
class LRUHash
|
14
|
+
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
attr_reader :max_size
|
18
|
+
|
19
|
+
attr_accessor :default
|
20
|
+
attr_accessor :default_proc
|
21
|
+
attr_accessor :release_proc
|
22
|
+
|
23
|
+
#
|
24
|
+
# Initialize new LRUHash instance.
|
25
|
+
#
|
26
|
+
# max_size -
|
27
|
+
# default_value -
|
28
|
+
# block -
|
29
|
+
#
|
30
|
+
def initialize(max_size, default_value=nil, &block)
|
31
|
+
@max_size = normalize_max(max_size)
|
32
|
+
@default = default_value
|
33
|
+
@default_proc = block
|
34
|
+
|
35
|
+
@h = {}
|
36
|
+
@head = Node.new
|
37
|
+
@tail = front(Node.new)
|
38
|
+
end
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
#
|
41
|
+
# Iterate over each pair.
|
42
|
+
#
|
43
|
+
def each_pair
|
44
|
+
if block_given?
|
45
|
+
each_node do |n|
|
46
|
+
yield [n.key, n.value]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
enum_for :each_pair
|
34
50
|
end
|
35
|
-
else
|
36
|
-
enum_for :each_pair
|
37
51
|
end
|
38
|
-
end
|
39
|
-
|
40
|
-
alias each each_pair
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
#
|
54
|
+
# Same as each pair.
|
55
|
+
#
|
56
|
+
alias each each_pair
|
57
|
+
|
58
|
+
#
|
59
|
+
# Iterate over each key.
|
60
|
+
#
|
61
|
+
def each_key
|
62
|
+
if block_given?
|
63
|
+
each_node do |n|
|
64
|
+
yield n.key
|
65
|
+
end
|
66
|
+
else
|
67
|
+
enum_for :each_key
|
46
68
|
end
|
47
|
-
else
|
48
|
-
enum_for :each_key
|
49
69
|
end
|
50
|
-
end
|
51
70
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
71
|
+
#
|
72
|
+
# Iterate over each value.
|
73
|
+
#
|
74
|
+
def each_value
|
75
|
+
if block_given?
|
76
|
+
each_node do |n|
|
77
|
+
yield n.value
|
78
|
+
end
|
79
|
+
else
|
80
|
+
enum_for :each_value
|
56
81
|
end
|
57
|
-
else
|
58
|
-
enum_for :each_value
|
59
82
|
end
|
60
|
-
end
|
61
83
|
|
62
|
-
|
63
|
-
|
64
|
-
|
84
|
+
#
|
85
|
+
# Size of the hash.
|
86
|
+
#
|
87
|
+
def size
|
88
|
+
@h.size
|
89
|
+
end
|
65
90
|
|
66
|
-
|
67
|
-
|
68
|
-
|
91
|
+
#
|
92
|
+
#
|
93
|
+
#
|
94
|
+
def empty?
|
95
|
+
@head.succ.equal? @tail
|
96
|
+
end
|
69
97
|
|
70
|
-
|
71
|
-
|
98
|
+
#
|
99
|
+
#
|
100
|
+
#
|
101
|
+
def fetch(key, &b)
|
102
|
+
n = @h[key]
|
72
103
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
104
|
+
if n
|
105
|
+
front(n).value
|
106
|
+
else
|
107
|
+
(b || FETCH)[key]
|
108
|
+
end
|
77
109
|
end
|
78
|
-
end
|
79
110
|
|
80
|
-
|
81
|
-
|
82
|
-
|
111
|
+
#
|
112
|
+
#
|
113
|
+
#
|
114
|
+
def [](key)
|
115
|
+
fetch(key) do |k|
|
116
|
+
@default_proc ? @default_proc[self, k] : default
|
117
|
+
end
|
83
118
|
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def keys
|
87
|
-
@h.keys
|
88
|
-
end
|
89
|
-
|
90
|
-
def values
|
91
|
-
@h.map {|k,n| n.value}
|
92
|
-
end
|
93
119
|
|
94
|
-
|
95
|
-
|
96
|
-
|
120
|
+
#
|
121
|
+
#
|
122
|
+
#
|
123
|
+
def keys
|
124
|
+
@h.keys
|
125
|
+
end
|
97
126
|
|
98
|
-
|
99
|
-
|
100
|
-
|
127
|
+
#
|
128
|
+
#
|
129
|
+
#
|
130
|
+
def values
|
131
|
+
@h.map {|k,n| n.value}
|
132
|
+
end
|
101
133
|
|
102
|
-
|
103
|
-
|
104
|
-
|
134
|
+
#
|
135
|
+
#
|
136
|
+
#
|
137
|
+
def has_key?(key)
|
138
|
+
@h.has_key? key
|
105
139
|
end
|
106
140
|
|
107
|
-
|
108
|
-
|
141
|
+
alias key? has_key?
|
142
|
+
alias member? has_key?
|
143
|
+
alias include? has_key?
|
109
144
|
|
110
|
-
|
145
|
+
#
|
146
|
+
#
|
147
|
+
#
|
148
|
+
def has_value?(value)
|
149
|
+
each_pair do |k, v|
|
150
|
+
return true if value.eql? v
|
151
|
+
end
|
111
152
|
|
112
|
-
|
113
|
-
|
114
|
-
end
|
153
|
+
false
|
154
|
+
end
|
115
155
|
|
116
|
-
|
117
|
-
n = @h[key]
|
156
|
+
alias value? has_value?
|
118
157
|
|
119
|
-
|
120
|
-
|
121
|
-
[n.key, n.value]
|
158
|
+
def values_at(*key_list)
|
159
|
+
key_list.map {|k| self[k]}
|
122
160
|
end
|
123
|
-
end
|
124
161
|
|
125
|
-
|
126
|
-
|
127
|
-
|
162
|
+
#
|
163
|
+
#
|
164
|
+
#
|
165
|
+
def assoc(key)
|
166
|
+
n = @h[key]
|
167
|
+
|
168
|
+
if n
|
128
169
|
front(n)
|
129
|
-
|
170
|
+
[n.key, n.value]
|
130
171
|
end
|
131
172
|
end
|
132
|
-
nil
|
133
|
-
end
|
134
173
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
174
|
+
#
|
175
|
+
#
|
176
|
+
#
|
177
|
+
def rassoc(value)
|
178
|
+
each_node do |n|
|
179
|
+
if value.eql? n.value
|
180
|
+
front(n)
|
181
|
+
return [n.key, n.value]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
nil
|
185
|
+
end
|
142
186
|
|
143
|
-
|
187
|
+
#
|
188
|
+
#
|
189
|
+
#
|
190
|
+
def key(value)
|
191
|
+
pair = rassoc(value) and pair.first
|
192
|
+
end
|
144
193
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
194
|
+
#
|
195
|
+
#
|
196
|
+
#
|
197
|
+
def store(key, value)
|
198
|
+
# same optimization as in Hash
|
199
|
+
key = key.dup.freeze if String === key && !key.frozen?
|
200
|
+
|
201
|
+
n = @h[key]
|
202
|
+
|
203
|
+
unless n
|
204
|
+
if size == max_size
|
205
|
+
# reuse node to optimize memory usage
|
206
|
+
n = delete_oldest
|
207
|
+
n.key = key
|
208
|
+
n.value = value
|
209
|
+
else
|
210
|
+
n = Node.new key, value
|
211
|
+
end
|
212
|
+
|
213
|
+
@h[key] = n
|
153
214
|
end
|
154
215
|
|
155
|
-
|
216
|
+
front(n).value = value
|
156
217
|
end
|
157
218
|
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
alias []= store
|
219
|
+
alias []= store
|
162
220
|
|
163
|
-
|
164
|
-
|
165
|
-
|
221
|
+
#
|
222
|
+
#
|
223
|
+
#
|
224
|
+
def delete(key)
|
225
|
+
n = @h[key] and remove_node(n).value
|
226
|
+
end
|
166
227
|
|
167
|
-
|
168
|
-
|
169
|
-
|
228
|
+
#
|
229
|
+
#
|
230
|
+
#
|
231
|
+
def delete_if
|
232
|
+
each_node do |n|
|
233
|
+
remove_node n if yield n.key, n.value
|
234
|
+
end
|
170
235
|
end
|
171
|
-
end
|
172
236
|
|
173
|
-
|
174
|
-
|
237
|
+
#
|
238
|
+
#
|
239
|
+
#
|
240
|
+
def max_size=(limit)
|
241
|
+
limit = normalize_max(limit)
|
175
242
|
|
176
|
-
|
177
|
-
|
243
|
+
while size > limit
|
244
|
+
delete_oldest
|
245
|
+
end
|
246
|
+
|
247
|
+
@max_size = limit
|
178
248
|
end
|
179
249
|
|
180
|
-
|
181
|
-
|
250
|
+
#
|
251
|
+
#
|
252
|
+
#
|
253
|
+
def clear
|
254
|
+
until empty?
|
255
|
+
delete_oldest
|
256
|
+
end
|
182
257
|
|
183
|
-
|
184
|
-
until empty?
|
185
|
-
delete_oldest
|
258
|
+
self
|
186
259
|
end
|
187
260
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
261
|
+
#
|
262
|
+
#
|
263
|
+
#
|
264
|
+
def to_s
|
265
|
+
s = nil
|
266
|
+
each_pair {|k, v| (s ? (s << ', ') : s = '{') << k.to_s << '=>' << v.to_s}
|
267
|
+
s ? (s << '}') : '{}'
|
268
|
+
end
|
196
269
|
|
197
|
-
|
270
|
+
alias inspect to_s
|
198
271
|
|
199
272
|
private
|
200
273
|
|
201
|
-
|
202
|
-
|
203
|
-
|
274
|
+
#
|
275
|
+
# Iterate nodes.
|
276
|
+
#
|
277
|
+
def each_node
|
278
|
+
n = @head.succ
|
279
|
+
|
280
|
+
until n.equal? @tail
|
281
|
+
succ = n.succ
|
282
|
+
yield n
|
283
|
+
n = succ
|
284
|
+
end
|
204
285
|
|
205
|
-
|
206
|
-
succ = n.succ
|
207
|
-
yield n
|
208
|
-
n = succ
|
286
|
+
self
|
209
287
|
end
|
210
288
|
|
211
|
-
|
212
|
-
|
289
|
+
#
|
290
|
+
# Move node to front.
|
291
|
+
#
|
292
|
+
# node - [Node]
|
293
|
+
#
|
294
|
+
def front(node)
|
295
|
+
node.insert_after(@head)
|
296
|
+
end
|
213
297
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
298
|
+
#
|
299
|
+
# Remove the node and invoke release_proc
|
300
|
+
# if set
|
301
|
+
#
|
302
|
+
# node - [Node]
|
303
|
+
#
|
304
|
+
def remove_node(node)
|
305
|
+
n = @h.delete(node.key)
|
306
|
+
n.unlink
|
307
|
+
release_proc and release_proc[n.key, n.value]
|
308
|
+
n
|
309
|
+
end
|
218
310
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
311
|
+
#
|
312
|
+
# Remove the oldest node returning the node
|
313
|
+
#
|
314
|
+
def delete_oldest
|
315
|
+
n = @tail.pred
|
316
|
+
raise "Cannot delete from empty hash" if @head.equal? n
|
317
|
+
remove_node n
|
318
|
+
end
|
227
319
|
|
228
|
-
|
229
|
-
|
230
|
-
n
|
231
|
-
|
232
|
-
|
233
|
-
|
320
|
+
#
|
321
|
+
# Normalize the argument in order to be usable as max_size
|
322
|
+
# criterion is that n.to_i must be an Integer and it must
|
323
|
+
# be larger than zero.
|
324
|
+
#
|
325
|
+
# n - [#to_i] max size
|
326
|
+
#
|
327
|
+
def normalize_max(n)
|
328
|
+
n = n.to_i
|
329
|
+
raise ArgumentError, 'Invalid max_size: %p' % n unless Integer === n && n > 0
|
330
|
+
n
|
331
|
+
end
|
234
332
|
|
235
|
-
|
236
|
-
|
237
|
-
# be larger than zero.
|
238
|
-
def normalize_max(n)
|
239
|
-
n = n.to_i
|
240
|
-
raise ArgumentError, 'Invalid max_size: %p' % n unless Integer === n && n > 0
|
241
|
-
n
|
242
|
-
end
|
333
|
+
#
|
334
|
+
FETCH = Proc.new {|k| raise KeyError, 'key not found'}
|
243
335
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
self.succ = self.pred = nil
|
253
|
-
self
|
254
|
-
end
|
336
|
+
# A single node in the doubly linked LRU list of nodes.
|
337
|
+
Node = Struct.new :key, :value, :pred, :succ do
|
338
|
+
def unlink
|
339
|
+
pred.succ = succ if pred
|
340
|
+
succ.pred = pred if succ
|
341
|
+
self.succ = self.pred = nil
|
342
|
+
self
|
343
|
+
end
|
255
344
|
|
256
|
-
|
257
|
-
|
258
|
-
|
345
|
+
def insert_after(node)
|
346
|
+
raise 'Cannot insert after self' if equal? node
|
347
|
+
return self if node.succ.equal? self
|
259
348
|
|
260
|
-
|
349
|
+
unlink
|
261
350
|
|
262
|
-
|
263
|
-
|
351
|
+
self.succ = node.succ
|
352
|
+
self.pred = node
|
264
353
|
|
265
|
-
|
266
|
-
|
354
|
+
node.succ.pred = self if node.succ
|
355
|
+
node.succ = self
|
267
356
|
|
268
|
-
|
357
|
+
self
|
358
|
+
end
|
269
359
|
end
|
360
|
+
|
270
361
|
end
|
271
362
|
|
272
363
|
end
|
273
|
-
|