jackowayed-rufus-tokyo 0.1.13.1 → 0.1.13.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,388 @@
1
+ #--
2
+ # Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ require 'rufus/tokyo/hmethods'
27
+
28
+
29
+ module Rufus::Tokyo
30
+
31
+ #
32
+ # A Tokyo Cabinet in-memory (tcutil.h) map
33
+ #
34
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
35
+ #
36
+ class Map
37
+
38
+ include HashMethods
39
+
40
+ #
41
+ # Creates an empty instance of a Tokyo Cabinet in-memory map
42
+ #
43
+ # (It's OK to pass the pointer of a C map directly, this is in fact
44
+ # used in rufus/tokyo/table when retrieving entries)
45
+ #
46
+ def initialize (pointer = nil)
47
+ @map = pointer || clib.tcmapnew
48
+ end
49
+
50
+ #
51
+ # a shortcut
52
+ #
53
+ def clib
54
+ CabinetLib
55
+ end
56
+
57
+ #
58
+ # Inserts key/value pair
59
+ #
60
+ def []= (k, v)
61
+ clib.tcmapput2(m, k, v)
62
+ v
63
+ end
64
+
65
+ #
66
+ # Deletes an entry
67
+ #
68
+ def delete (k)
69
+ v = self[k]
70
+ return nil unless v
71
+ (clib.tcmapout2(m, k) == 1) || raise("failed to remove key '#{k}'")
72
+ v
73
+ end
74
+
75
+ #
76
+ # Empties the map
77
+ #
78
+ def clear
79
+ clib.tcmapclear(m)
80
+ end
81
+
82
+ #
83
+ # (the actual #[] method is provided by HashMethods)
84
+ #
85
+ def get (k)
86
+ m; clib.tcmapget2(m, k) rescue nil
87
+ end
88
+ protected :get
89
+
90
+ #
91
+ # Returns an array of all the keys in the map
92
+ #
93
+ def keys
94
+ a = []
95
+ clib.tcmapiterinit(m)
96
+ while (k = (clib.tcmapiternext2(m) rescue nil)); a << k; end
97
+ a
98
+ end
99
+
100
+ #
101
+ # Returns the count of entries in the map
102
+ #
103
+ def size
104
+ clib.tcmaprnum(m)
105
+ end
106
+
107
+ alias :length :size
108
+
109
+ #
110
+ # Frees the map (nukes it from memory)
111
+ #
112
+ def free
113
+ clib.tcmapdel(@map)
114
+ @map = nil
115
+ end
116
+
117
+ alias :destroy :free
118
+ alias :close :free
119
+
120
+ #
121
+ # Returns the pointer to the underlying Tokyo Cabinet map
122
+ #
123
+ def pointer
124
+ @map || raise('map got freed, cannot use anymore')
125
+ end
126
+
127
+ alias :m :pointer
128
+
129
+ #
130
+ # Turns a given Tokyo map structure into a Ruby Hash. By default
131
+ # (free = true) will dispose of the map before replying with the Ruby
132
+ # Hash.
133
+ #
134
+ def self.to_h (map_pointer, free=true)
135
+ m = self.new(map_pointer)
136
+ h = m.to_h
137
+ m.free if free
138
+ h
139
+ end
140
+
141
+ #
142
+ # Turns a Ruby hash into a Tokyo Cabinet Map and returns it
143
+ # (don't forget to free the map when you're done with it !)
144
+ #
145
+ def self.from_h (h)
146
+ h.inject(Map.new) { |m, (k, v)| m[k] = v; m }
147
+ end
148
+
149
+ #
150
+ # Behaves much like Hash#[] but outputs a Rufus::Tokyo::Map
151
+ # (don't forget to free the map when you're done with it !)
152
+ #
153
+ def self.[] (*h_or_a)
154
+
155
+ if h_or_a.is_a?(Array) && h_or_a.size == 1 && h_or_a.first.is_a?(Array)
156
+ h_or_a = h_or_a.first
157
+ end
158
+
159
+ from_h(::Hash[*h_or_a])
160
+ end
161
+ end
162
+
163
+ #
164
+ # A Tokyo Cabinet in-memory (tcutil.h) list
165
+ #
166
+ # http://tokyocabinet.sourceforge.net/spex-en.html#tcutilapi
167
+ #
168
+ class List
169
+ include Enumerable
170
+
171
+ #
172
+ # Creates a new Tokyo Cabinet list.
173
+ #
174
+ # (by passing a list pointer, one can wrap an existing list pointer
175
+ # into a handy instance of this class)
176
+ #
177
+ def initialize (list_pointer=nil)
178
+
179
+ if list_pointer.is_a?(FFI::Pointer)
180
+ @list = list_pointer
181
+ else
182
+ @list = clib.tclistnew
183
+ list_pointer.each { |e| self << e } if list_pointer
184
+ end
185
+ end
186
+
187
+ #
188
+ # a shortcut
189
+ #
190
+ def clib
191
+ CabinetLib
192
+ end
193
+
194
+ #
195
+ # Inserts an element in the list (note that the lib will raise an
196
+ # ArgumentError if s is not a String)
197
+ #
198
+ def << (s)
199
+ clib.tclistpush2(@list, s)
200
+ self
201
+ end
202
+
203
+ #
204
+ # Pushes an argument or a list of arguments to this list
205
+ #
206
+ def push (*args)
207
+ args.each { |a| self << a }
208
+ self
209
+ end
210
+
211
+ #
212
+ # Pops the last element in the list
213
+ #
214
+ def pop
215
+ clib.tclistpop2(@list) rescue nil
216
+ end
217
+
218
+ #
219
+ # Removes and returns the first element in a list
220
+ #
221
+ def shift
222
+ clib.tclistshift2(@list) rescue nil
223
+ end
224
+
225
+ #
226
+ # Inserts a string at the beginning of the list
227
+ #
228
+ def unshift (s)
229
+ clib.tclistunshift2(@list, s)
230
+ self
231
+ end
232
+
233
+ def []= (a, b, c=nil)
234
+
235
+ i, s = c.nil? ? [ a, b ] : [ [a, b], c ]
236
+
237
+ range = if i.is_a?(Range)
238
+ i
239
+ elsif i.is_a?(Array)
240
+ start, count = i
241
+ (start..start + count - 1)
242
+ else
243
+ [ i ]
244
+ end
245
+
246
+ range = norm(range)
247
+
248
+ values = s.is_a?(Array) ? s : [ s ]
249
+ # not "values = Array(s)"
250
+
251
+ range.each_with_index do |offset, index|
252
+ val = values[index]
253
+ if val
254
+ clib.tclistover2(@list, offset, val)
255
+ else
256
+ clib.tclistremove2(@list, values.size)
257
+ end
258
+ end
259
+
260
+ self
261
+ end
262
+
263
+ #
264
+ # Removes the value at a given index and returns the value
265
+ # (returns nil if no value available)
266
+ #
267
+ def delete_at (i)
268
+ clib.tclistremove2(@list, i)
269
+ end
270
+
271
+ def delete_if
272
+ # TODO
273
+ end
274
+
275
+ def slice
276
+ # TODO
277
+ end
278
+ def slice!
279
+ # TODO
280
+ end
281
+
282
+ #
283
+ # Returns the size of this Tokyo Cabinet list
284
+ #
285
+ def size
286
+ clib.tclistnum(@list)
287
+ end
288
+
289
+ alias :length :size
290
+
291
+ #
292
+ # The equivalent of Ruby Array#[]
293
+ #
294
+ def [] (i, count=nil)
295
+
296
+ return nil if (count != nil) && count < 1
297
+
298
+ len = self.size
299
+
300
+ range = if count.nil?
301
+ i.is_a?(Range) ? i : [i]
302
+ else
303
+ (i..i + count - 1)
304
+ end
305
+
306
+ r = norm(range).collect { |i| clib.tclistval2(@list, i) rescue nil }
307
+
308
+ range.first == range.last ? r.first : r
309
+ end
310
+
311
+ def clear
312
+ clib.tclistclear(@list)
313
+ end
314
+
315
+ def each
316
+ (0..self.size - 1).each { |i| yield self[i] }
317
+ end
318
+
319
+ #
320
+ # Turns this Tokyo Cabinet list into a Ruby array
321
+ #
322
+ def to_a
323
+ self.collect { |e| e }
324
+ end
325
+
326
+ #
327
+ # Closes (frees) this list
328
+ #
329
+ def free
330
+ self.class.free(@list)
331
+ @list = nil
332
+ end
333
+
334
+ alias :close :free
335
+ alias :destroy :free
336
+
337
+ #
338
+ # Frees (closes) the given 'native' (FFI) list (memory pointer)
339
+ #
340
+ def self.free (list_pointer)
341
+
342
+ CabinetLib.tclistdel(list_pointer)
343
+ end
344
+
345
+ #
346
+ # Closes (frees memory from it) this list and returns the ruby version
347
+ # of it
348
+ #
349
+ def release
350
+
351
+ a = self.to_a
352
+ self.close
353
+ a
354
+ end
355
+
356
+ #
357
+ # Returns the underlying 'native' (FFI) memory pointer
358
+ #
359
+ def pointer
360
+
361
+ @list
362
+ end
363
+
364
+ #
365
+ # Turns a list pointer into a Ruby Array instance (and makes sure to
366
+ # release the pointer
367
+ #
368
+ def self.release (list_pointer)
369
+
370
+ Rufus::Tokyo::List.new(list_pointer).release
371
+ end
372
+
373
+ protected
374
+
375
+ #
376
+ # Makes sure this offset/range fits the size of the list
377
+ #
378
+ def norm (i)
379
+ l = self.length
380
+ case i
381
+ when Range then ((i.first % l)..(i.last % l))
382
+ when Array then [ i.first % l ]
383
+ else i % l
384
+ end
385
+ end
386
+ end
387
+ end
388
+
@@ -0,0 +1,161 @@
1
+ #--
2
+ # Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ module Rufus
27
+ module Tokyo
28
+
29
+ #
30
+ # Methods for setting up / tuning a Cabinet.
31
+ #
32
+ module CabinetConfig
33
+
34
+ protected
35
+
36
+ # Given a path, a hash of parameters and a suffix,
37
+ #
38
+ # a) makes sure that the path has the given suffix or raises an exception
39
+ # b) gathers params found in the path (#) or in params
40
+ # c) determines the config as set by the parameters
41
+ #
42
+ # Suffix is optional, if present, it will be enforced.
43
+ #
44
+ def determine_conf (path, params, required_type=nil)
45
+
46
+ if path.index('#')
47
+
48
+ ss = path.split('#')
49
+ path = ss.shift
50
+
51
+ ss.each { |p| pp = p.split('='); params[pp[0]] = pp[1] }
52
+ end
53
+
54
+ params = params.inject({}) { |h, (k, v)| h[k.to_sym] = v; h }
55
+
56
+ [
57
+ {
58
+ :params => params,
59
+ :mode => determine_open_mode(params),
60
+ :mutex => (params[:mutex].to_s == 'true'),
61
+ #:indexes => params[:idx] || params[:indexes],
62
+ :xmsiz => (params[:xmsiz] || 67108864).to_i,
63
+ },
64
+ determine_type_and_path(path, params, required_type),
65
+ determine_tuning_values(params),
66
+ determine_cache_values(params)
67
+
68
+ ].inject({}) { |h, hh| h.merge(hh) }
69
+ end
70
+
71
+ def determine_open_mode (params) #:nodoc#
72
+
73
+ mode = params[:mode].to_s
74
+ mode = 'wc' if mode.size < 1
75
+
76
+ {
77
+ 'r' => (1 << 0), # open as a reader
78
+ 'w' => (1 << 1), # open as a writer
79
+ 'c' => (1 << 2), # writer creating
80
+ 't' => (1 << 3), # writer truncating
81
+ 'e' => (1 << 4), # open without locking
82
+ 'f' => (1 << 5), # lock without blocking
83
+ 's' => (1 << 6), # synchronize every transaction (tctdb.h)
84
+
85
+ }.inject(0) { |r, (c, v)|
86
+
87
+ r = r | v if mode.index(c); r
88
+ }
89
+ end
90
+
91
+ def determine_tuning_values (params) #:nodoc#
92
+
93
+ o = params[:opts] || ''
94
+ o = {
95
+ 'l' => 1 << 0, # large
96
+ 'd' => 1 << 1, # deflate
97
+ 'b' => 1 << 2, # bzip2
98
+ 't' => 1 << 3, # tcbs
99
+ 'x' => 1 << 4
100
+ }.inject(0) { |i, (k, v)| i = i | v if o.index(k); i }
101
+
102
+ {
103
+ :bnum => (params[:bnum] || 131071).to_i,
104
+ :apow => (params[:apow] || 4).to_i,
105
+ :fpow => (params[:fpow] || 10).to_i,
106
+ :opts => o,
107
+
108
+ :lmemb => (params[:lmemb] || 128).to_i,
109
+ # number of members in each leaf page (:btree)
110
+ :nmemb => (params[:nmemb] || 256).to_i,
111
+ # number of members in each non-leaf page (:btree)
112
+
113
+ :width => (params[:width] || 255).to_i,
114
+ # width of the value of each record (:fixed)
115
+ :limsiz => (params[:limsiz] || 26_8435_456).to_i,
116
+ # limit size of the database file (:fixed)
117
+
118
+ :dfunit => (params[:dfunit] || 0).to_i
119
+ # unit step number. If it is not more than 0,
120
+ # the auto defragmentation is disabled.
121
+ }
122
+ end
123
+
124
+ def determine_cache_values (params) #:nodoc#
125
+
126
+ {
127
+ :rcnum => params[:rcnum].to_i,
128
+ :lcnum => (params[:lcnum] || 2048).to_i,
129
+ :ncnum => (params[:ncnum] || 512).to_i
130
+ }
131
+ end
132
+
133
+ CABINET_SUFFIXES = {
134
+ :hash => '.tch', :btree => '.tcb', :fixed => '.tcf', :table => '.tct'
135
+ }
136
+
137
+ CABINET_TYPES = CABINET_SUFFIXES.invert
138
+
139
+ def determine_type_and_path (path, params, required_type) #:nodoc#
140
+
141
+ type = required_type || params[:type]
142
+ ext = File.extname(path)
143
+
144
+ if ext == ''
145
+ suffix = CABINET_SUFFIXES[type]
146
+ path = path + suffix
147
+ else
148
+ suffix = ext
149
+ type ||= CABINET_TYPES[ext]
150
+ end
151
+
152
+ raise "path '#{path}' must be suffixed with #{suffix}" \
153
+ if suffix and File.extname(path) != suffix
154
+
155
+ { :path => path, :type => type }
156
+ end
157
+ end
158
+
159
+ end
160
+ end
161
+