ruby-qdbm 0.1

Sign up to get free protection for your applications and to get access to all the features.
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