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