ruby-qdbm 0.1

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/lib/depot.rb ADDED
@@ -0,0 +1,594 @@
1
+ #=================================================================================================
2
+ # Ruby API of Depot, the basic API of QDBM
3
+ # Copyright (C) 2000-2006 Mikio Hirabayashi
4
+ # This file is part of QDBM, Quick Database Manager.
5
+ # QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
6
+ # Lesser General Public License as published by the Free Software Foundation; either version
7
+ # 2.1 of the License or any later version. QDBM is distributed in the hope that it will be
8
+ # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
10
+ # details.
11
+ # You should have received a copy of the GNU Lesser General Public License along with QDBM; if
12
+ # not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
13
+ # 02111-1307 USA.
14
+ #=================================================================================================
15
+
16
+
17
+ require 'mod_depot'
18
+ require 'thread'
19
+
20
+
21
+
22
+ ##
23
+ # require 'depot'
24
+ # The library `depot' should be included in application codes.
25
+ # An instance of the class `Depot' is used as a database handle.
26
+ # `Depot' performs Mix-in of `Enumerable'.
27
+ # Each method of `Depot' throws an exception of `Depot::EANY' or its sub classes when an error
28
+ # occurs: `Depot::ENOERR', `Depot::EFATAL', `Depot::EMODE', `Depot::EBROKEN', `Depot::EKEEP',
29
+ # `Depot::ENOITEM', `Depot::EALLOC', `Depot::EMAP', `Depot::EOPEN', `Depot::ECLOSE',
30
+ # `Depot::ETRUNC', `Depot::ESYNC', `Depot::ESTAT', `Depot::ESEEK', `Depot::EREAD',
31
+ # `Depot::EWRITE', `Depot::ELOCK', `Depot::EUNLINK', `Depot::EMKDIR', `Depot::ERMDIR' and
32
+ # `Depot::EMISC'.
33
+ #
34
+ class Depot
35
+ include Mod_Depot, Enumerable
36
+ #----------------------------------------------------------------
37
+ # class constants
38
+ #----------------------------------------------------------------
39
+ MyMutex = Mutex::new()
40
+ #----------------------------------------------------------------
41
+ # class methods
42
+ #----------------------------------------------------------------
43
+ public
44
+ ##
45
+ # depot = Depot::new(name, omode, bnum)
46
+ # Constructor: Get a database handle.
47
+ # `name' specifies the name of a database file.
48
+ # `omode' specifies the connection mode: `Depot::OWRITER' as a writer, `Depot::OREADER' as a
49
+ # reader. If the mode is `Depot::OWRITER', the following may be added by bitwise or:
50
+ # `Depot::OCREAT', which means it creates a new database if not exist, `Depot::OTRUNC', which
51
+ # means it creates a new database regardless if one exists. Both of `Depot::OREADER' and
52
+ # `Depot::OWRITER' can be added to by bitwise or: `Depot::ONOLCK', which means it opens a
53
+ # database file without file locking, or `Depot::OLCKNB', which means locking is performed
54
+ # without blocking. `Depot::OCREAT' can be added to by bitwise or: `Depot::OSPARSE', which
55
+ # means it creates a database file as a sparse file. If it is omitted, `Depot::OREADER' is
56
+ # specified.
57
+ # `bnum' specifies the number of elements of the bucket array. If it is omitted or not more
58
+ # than 0, the default value is specified. The size of a bucket array is determined on
59
+ # creating, and can not be changed except for by optimization of the database. Suggested
60
+ # size of a bucket array is about from 0.5 to 4 times of the number of all records to store.
61
+ # The return value is the database handle.
62
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
63
+ # If a block parameter is given, this method works as an iterator. A database handle is
64
+ # opened and passed via the first argument of the block. The database handle is surely
65
+ # closed when the block is over.
66
+ # While connecting as a writer, an exclusive lock is invoked to the database file.
67
+ # While connecting as a reader, a shared lock is invoked to the database file. The thread
68
+ # blocks until the lock is achieved. If `Depot::ONOLCK' is used, the application is
69
+ # responsible for exclusion control.
70
+ #
71
+ #@ DEFINED IMPLICITLY
72
+ ##
73
+ # depot = Depot::open(name, omode, bnum)
74
+ # Constructor: An alias of `new'.
75
+ #
76
+ #@ DEFINED OUTSIDE
77
+ #----------------------------------------------------------------
78
+ # private methods
79
+ #----------------------------------------------------------------
80
+ private
81
+ #=
82
+ # initialize(name, omode, bnum)
83
+ # Method: Called implicitly by the constructor.
84
+ #
85
+ def initialize(name, omode = OREADER, bnum = -1)
86
+ @silent = false
87
+ MyMutex.synchronize() do
88
+ @index = mod_open(name, omode, bnum)
89
+ @name = name
90
+ end
91
+ if(iterator?)
92
+ begin
93
+ yield(self)
94
+ ensure
95
+ close()
96
+ end
97
+ end
98
+ self
99
+ end
100
+ #=
101
+ # clone()
102
+ # Method: Forbidden to use.
103
+ #
104
+ def clone
105
+ raise(DepotError)
106
+ end
107
+ #=
108
+ # dup()
109
+ # Method: Forbidden to use.
110
+ #
111
+ alias dup clone
112
+ #----------------------------------------------------------------
113
+ # public methods
114
+ #----------------------------------------------------------------
115
+ public
116
+ ##
117
+ # bool = depot.close()
118
+ # Method: Close the database handle.
119
+ # The return value is always true.
120
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
121
+ # Because the region of a closed handle is released, it becomes impossible to use the handle.
122
+ # Updating a database is assured to be written when the handle is closed. If a writer opens
123
+ # a database but does not close it appropriately, the database will be broken.
124
+ #
125
+ def close()
126
+ MyMutex.synchronize() do
127
+ begin
128
+ mod_close(@index)
129
+ ensure
130
+ @index = -1
131
+ end
132
+ end
133
+ end
134
+ ##
135
+ # depot.silent = bool
136
+ # Method: Set the flag whether to repress frequent exceptions.
137
+ # The return value is the assigned value.
138
+ #
139
+ def silent=(value)
140
+ @silent = value ? true : false
141
+ mod_setsilent(@index, silent ? 1 : 0)
142
+ @silent
143
+ end
144
+ def silent
145
+ @silent
146
+ end
147
+ ##
148
+ # bool = depot.put(key, val, dmode)
149
+ # Method: Store a record.
150
+ # `key' specifies a key. Although it must be an instance of String, binary data is okey.
151
+ # `val' specifies a value. Although it must be an instance of String, binary data is okey.
152
+ # `dmode' specifies behavior when the key overlaps, by the following values: `Depot::DOVER',
153
+ # which means the specified value overwrites the existing one, `Depot::DKEEP', which means
154
+ # the existing value is kept, `Depot::DCAT', which means the specified value is concatenated
155
+ # at the end of the existing value. If it is omitted, `Depot::DOVER' is specified.
156
+ # The return value is always true. However, if the silent flag is true and replace is
157
+ # cancelled, false is returned instead of exception.
158
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs or replace
159
+ # is cancelled.
160
+ #
161
+ def put(key, val, dmode = DOVER)
162
+ mod_put(@index, key, val, dmode)
163
+ end
164
+ ##
165
+ # bool = depot.store(key, val)
166
+ # Method: An alias of `put'.
167
+ #
168
+ alias store put
169
+ ##
170
+ # depot[key] = val
171
+ # Method: An alias of `put'.
172
+ #
173
+ alias []= put
174
+ ##
175
+ # bool = depot.out(key)
176
+ # Method: Delete a record.
177
+ # `key' specifies a key. Although it must be an instance of String, binary data is okey.
178
+ # The return value is always true. However, if the silent flag is true and no record
179
+ # corresponds, false is returned instead of exception.
180
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs or no record
181
+ # corresponds.
182
+ #
183
+ def out(key)
184
+ mod_out(@index, key)
185
+ end
186
+ ##
187
+ # bool = depot.delete(key)
188
+ # Method: An alias of `out'.
189
+ #
190
+ alias delete out
191
+ ##
192
+ # bool = depot.clear()
193
+ # Method: Delete all records.
194
+ # The return value is always true.
195
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
196
+ #
197
+ def clear
198
+ MyMutex.synchronize() do
199
+ iterinit()
200
+ while(rnum() > 0)
201
+ out(iternext())
202
+ end
203
+ end
204
+ true
205
+ end
206
+ ##
207
+ # str = depot.get(key, start, max)
208
+ # Method: Retrieve a record.
209
+ # `key' specifies a key. Although it must be an instance of String, binary data is okey.
210
+ # `start' specifies the offset address of the beginning of the region of the value to be read.
211
+ # If it is negative or omitted, the offset is specified as 0.
212
+ # `max' specifies the max size to be read. If it is negative or omitted, the size to read is
213
+ # unlimited.
214
+ # The return value is an instance of the value of the corresponding record. If the silent flag
215
+ # is true and no record corresponds, nil is returned instead of exception.
216
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs, no record
217
+ # corresponds, or the size of the value of the corresponding record is less than `max'.
218
+ #
219
+ def get(key, start = 0, max = -1)
220
+ mod_get(@index, key, start, max)
221
+ end
222
+ ##
223
+ # str = depot.fetch(key, defval)
224
+ # Method: Retrieve a record.
225
+ # `key' specifies a key. Although it must be an instance of String, binary data is okey.
226
+ # `defval' specifies the default value used when no record corresponds. If it is omitted, nil
227
+ # is specified.
228
+ # The return value is an instance of the value of the corresponding record, or the default
229
+ # value if no record corresponds.
230
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
231
+ #
232
+ def fetch(key, defval = nil)
233
+ if @silent
234
+ if val = mod_get(@index, key, 0, -1)
235
+ val
236
+ else
237
+ defval
238
+ end
239
+ else
240
+ begin
241
+ mod_get(@index, key, 0, -1)
242
+ rescue ENOITEM
243
+ defval
244
+ end
245
+ end
246
+ end
247
+ ##
248
+ # str = depot[key]
249
+ # Method: An alias of `fetch'.
250
+ #
251
+ alias [] fetch
252
+ ##
253
+ # num = depot.vsiz(key)
254
+ # Method: Get the size of the value of a record.
255
+ # `key' specifies a key. Although it must be an instance of String, binary data is okey.
256
+ # The return value is the size of the value of the corresponding record. If the silent flag
257
+ # is true and no record corresponds, -1 is returned instead of exception.
258
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs or no record
259
+ # corresponds.
260
+ # Because this method does not read the entity of a record, it is faster than `get'.
261
+ #
262
+ def vsiz(key)
263
+ mod_vsiz(@index, key)
264
+ end
265
+ ##
266
+ # bool = depot.iterinit()
267
+ # Method: Initialize the iterator of the database handle.
268
+ # The return value is always true.
269
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
270
+ # The iterator is used in order to access the key of every record stored in a database.
271
+ #
272
+ def iterinit()
273
+ mod_iterinit(@index)
274
+ end
275
+ ##
276
+ # str = depot.iternext()
277
+ # Method: Get the next key of the iterator.
278
+ # The return value is the value of the next key. If the silent flag is true and no record
279
+ # corresponds, nil is returned instead of exception.
280
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs or no record
281
+ # is to be get out of the iterator.
282
+ # It is possible to access every record by iteration of calling this method. However, it is
283
+ # not assured if updating the database is occurred while the iteration. Besides, the order
284
+ # of this traversal access method is arbitrary, so it is not assured that the order of
285
+ # storing matches the one of the traversal access.
286
+ #
287
+ def iternext()
288
+ mod_iternext(@index)
289
+ end
290
+ ##
291
+ # bool = depot.setalign(align)
292
+ # Method: Set alignment of the database handle.
293
+ # `align' specifies the basic size of alignment. If it is omitted, alignment is cleared.
294
+ # The return value is always true.
295
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
296
+ # If alignment is set to a database, the efficiency of overwriting values is improved.
297
+ # The size of alignment is suggested to be average size of the values of the records to be
298
+ # stored. If alignment is positive, padding whose size is multiple number of the alignment
299
+ # is placed. If alignment is negative, as `vsiz' is the size of a value, the size of padding
300
+ # is calculated with `(vsiz / pow(2, abs(align) - 1))'. Because alignment setting is not
301
+ # saved in a database, you should specify alignment every opening a database.
302
+ #
303
+ def setalign(align = 0)
304
+ mod_setalign(@index, align)
305
+ end
306
+ ##
307
+ # bool = depot.setfbpsiz(size);
308
+ # Method: Set the size of the free block pool.
309
+ # `size' specifies the size of the free block pool. If it is undef, the free block pool is not
310
+ # used.
311
+ # The return value is always true.
312
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
313
+ # The default size of the free block pool is 16. If the size is greater, the space efficiency
314
+ # of overwriting values is improved with the time efficiency sacrificed.
315
+ #
316
+ def setfbpsiz(size = 0)
317
+ mod_setfbpsiz(@index, size)
318
+ end
319
+ ##
320
+ # bool = depot.sync()
321
+ # Method: Synchronize updating contents with the file and the device.
322
+ # The return value is always true.
323
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
324
+ # This method is useful when another process uses the connected database file.
325
+ #
326
+ def sync()
327
+ mod_sync(@index)
328
+ end
329
+ ##
330
+ # bool = depot.optimize(bnum)
331
+ # Method: Optimize the database.
332
+ # `bnum' specifies the number of the elements of the bucket array. If it is omitted or not
333
+ # more than 0, the default value is specified.
334
+ # The return value is always true.
335
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
336
+ # In an alternating succession of deleting and storing with overwrite or concatenate,
337
+ # dispensable regions accumulate. This method is useful to do away with them.
338
+ #
339
+ def optimize(bnum = -1)
340
+ mod_optimize(@index, bnum)
341
+ end
342
+ ##
343
+ # num = depot.fsiz()
344
+ # Method: Get the size of the database file.
345
+ # The return value is the size of the database file.
346
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
347
+ #
348
+ def fsiz()
349
+ mod_fsiz(@index)
350
+ end
351
+ ##
352
+ # num = depot.bnum()
353
+ # Method: Get the number of the elements of the bucket array.
354
+ # The return value is the number of the elements of the bucket array
355
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
356
+ #
357
+ def bnum()
358
+ mod_bnum(@index)
359
+ end
360
+ ##
361
+ # num = depot.rnum()
362
+ # Method: Get the number of the records stored in the database.
363
+ # The return value is the number of the records stored in the database.
364
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
365
+ #
366
+ def rnum()
367
+ mod_rnum(@index)
368
+ end
369
+ ##
370
+ # num = depot.length()
371
+ # Method: An alias of `rnum'.
372
+ #
373
+ alias length rnum
374
+ ##
375
+ # num = depot.size()
376
+ # Method: An alias of `rnum'.
377
+ #
378
+ alias size rnum
379
+ ##
380
+ # bool = depot.writable()
381
+ # Method: Check whether the database handle is a writer or not.
382
+ # The return value is true if the handle is a writer, false if not.
383
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
384
+ #
385
+ def writable()
386
+ mod_writable(@index)
387
+ end
388
+ ##
389
+ # bool = depot.fatalerror()
390
+ # Method: Check whether the database has a fatal error or not.
391
+ # The return value is true if the database has a fatal error, false if not.
392
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
393
+ #
394
+ def fatalerror()
395
+ mod_fatalerror(@index)
396
+ end
397
+ ##
398
+ # depot.each() do |key, val| ... end
399
+ # Iterator Method: Iterate a process with a pair of a key and a value of each record.
400
+ #
401
+ def each()
402
+ MyMutex.synchronize() do
403
+ iterinit()
404
+ while(true)
405
+ begin
406
+ break unless key = iternext()
407
+ val = get(key)
408
+ rescue ENOITEM
409
+ break
410
+ end
411
+ yield(key, val)
412
+ end
413
+ iterinit()
414
+ end
415
+ self
416
+ end
417
+ ##
418
+ # depot.each_pair() do |key, val| ... end
419
+ # Iterator Method: An alias of `each'.
420
+ #
421
+ alias each_pair each
422
+ ##
423
+ # depot.each_key() do |key| ... end
424
+ # Iterator Method: Iterate a process with a key of each record.
425
+ #
426
+ def each_key()
427
+ MyMutex.synchronize() do
428
+ iterinit()
429
+ while(true)
430
+ begin
431
+ break unless key = iternext()
432
+ rescue ENOITEM
433
+ break
434
+ end
435
+ yield(key)
436
+ end
437
+ iterinit()
438
+ end
439
+ self
440
+ end
441
+ ##
442
+ # depot.each_value() do |val| ... end
443
+ # Iterator Method: Iterate a process with a value of each record.
444
+ #
445
+ def each_value()
446
+ MyMutex.synchronize() do
447
+ iterinit()
448
+ while(true)
449
+ begin
450
+ break unless key = iternext()
451
+ val = get(key)
452
+ rescue ENOITEM
453
+ break
454
+ end
455
+ yield(val)
456
+ end
457
+ iterinit()
458
+ end
459
+ self
460
+ end
461
+ ##
462
+ # ary = depot.keys()
463
+ # Method: Get an array of all keys.
464
+ # The return value is an array of all keys.
465
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
466
+ #
467
+ def keys()
468
+ ary = Array::new(rnum())
469
+ MyMutex.synchronize() do
470
+ iterinit()
471
+ 0.upto(ary.length - 1) do |i|
472
+ ary[i] = iternext()
473
+ end
474
+ iterinit()
475
+ end
476
+ ary
477
+ end
478
+ ##
479
+ # ary = depot.values()
480
+ # Method: Get an array of all values.
481
+ # The return value is an array of all values.
482
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs.
483
+ #
484
+ def values()
485
+ ary = Array::new(rnum())
486
+ MyMutex.synchronize() do
487
+ iterinit()
488
+ 0.upto(ary.length - 1) do |i|
489
+ ary[i] = get(iternext())
490
+ end
491
+ iterinit()
492
+ end
493
+ ary
494
+ end
495
+ ##
496
+ # str = depot.index(val)
497
+ # Method: Retrieve a record with a value.
498
+ # `val' specifies a value. Although it must be an instance of String, binary data is okey.
499
+ # The return value is the key of the record with the specified value.
500
+ # An exception of `Depot::EANY' or its sub classes is thrown if an error occurs or no record
501
+ # corresponds.
502
+ # If two or more records correspond, the first found record is selected.
503
+ #
504
+ def index(val)
505
+ key = nil
506
+ MyMutex.synchronize() do
507
+ iterinit()
508
+ while(true)
509
+ break unless key = iternext()
510
+ (get(key) == val) && break
511
+ end
512
+ iterinit()
513
+ end
514
+ key
515
+ end
516
+ ##
517
+ # num = depot.to_int()
518
+ # Method: An alias of `rnum'.
519
+ #
520
+ alias to_int rnum
521
+ ##
522
+ # num = depot.to_i()
523
+ # Method: An alias of `to_int'.
524
+ #
525
+ alias to_i to_int
526
+ ##
527
+ # str = depot.to_str()
528
+ # Method: Get string standing for the instance.
529
+ #
530
+ def to_str
531
+ if(@index != -1)
532
+ sprintf("#<Depot:%#x:name=%s:state=open:bnum=%d:rnum=%d>",
533
+ object_id(), @name, bnum(), rnum())
534
+ else
535
+ sprintf("#<Depot:%#x:name=%s:state=closed>", object_id(), @name)
536
+ end
537
+ end
538
+ ##
539
+ # str = depot.to_s()
540
+ # Method: An alias of `to_str'.
541
+ #
542
+ alias to_s to_str
543
+ ##
544
+ # ary = depot.to_ary()
545
+ # Method: Get an array of alternation of each pair of a key and a value.
546
+ #
547
+ def to_ary
548
+ ary = Array::new(rnum())
549
+ i = 0
550
+ each() do |key, val|
551
+ ary[i] = [key, val]
552
+ i += 1
553
+ end
554
+ ary
555
+ end
556
+ ##
557
+ # ary = depot.to_a()
558
+ # Method: An alias of `to_ary'.
559
+ #
560
+ alias to_a to_ary
561
+ ##
562
+ # hash = depot.to_hash()
563
+ # Method: Get a hash storing all records.
564
+ #
565
+ def to_hash
566
+ hash = Hash::new()
567
+ each() do |key, val|
568
+ hash[key] = val
569
+ end
570
+ hash
571
+ end
572
+ ##
573
+ # hash = depot.to_h()
574
+ # Method: An alias of `to_hash'.
575
+ #
576
+ alias to_h to_hash
577
+ ##
578
+ # str = depot.inspect()
579
+ # Method: An alias of `to_str'.
580
+ #
581
+ alias inspect to_str
582
+ end
583
+
584
+
585
+ #----------------------------------------------------------------
586
+ # Alias definition of class methods
587
+ #----------------------------------------------------------------
588
+ class << Depot
589
+ alias open new
590
+ end
591
+
592
+
593
+
594
+ # END OF FILE