rubysl-sdbm 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f749712537e38df1d36f99b235803d31ee3e731e
4
+ data.tar.gz: c916491362e0edc9004efa8e6f0e52e7e03e4f4e
5
+ SHA512:
6
+ metadata.gz: 7d703fd20c9b29936ba70cd4b54e8f7e0609554a826c81d35a655b7a0d430e7255f9f6b87074b5f48e78d9b60a983921c3e4e743315240e39b2f9470264704c7
7
+ data.tar.gz: f7fe2567012ab5e2db635e2c0c6954be738c5b6891c4f6055f72bb4c1c9292f0d855798030cd3533d0e0af906e173c9273d6c4fd97a4e6b3db0843f7f336444b
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ env:
3
+ - RUBYLIB=lib
4
+ script: bundle exec mspec
5
+ rvm:
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-sdbm.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Sdbm
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-sdbm'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-sdbm
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,929 @@
1
+ /*
2
+ * sdbm - ndbm work-alike hashed database library
3
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
4
+ * author: oz@nexus.yorku.ca
5
+ * status: public domain.
6
+ *
7
+ * core routines
8
+ */
9
+
10
+ #ifndef lint
11
+ /*char sdbm_rcsid[] = "$Id$";*/
12
+ #endif
13
+
14
+ #include "ruby/config.h"
15
+ #include "ruby/defines.h"
16
+
17
+ #ifdef HAVE_UNISTD_H
18
+ #include <unistd.h>
19
+ #endif
20
+
21
+ #include "sdbm.h"
22
+
23
+ /*
24
+ * sdbm - ndbm work-alike hashed database library
25
+ * tuning and portability constructs [not nearly enough]
26
+ * author: oz@nexus.yorku.ca
27
+ */
28
+
29
+ #define BYTESIZ 8
30
+
31
+ #ifdef BSD42
32
+ #define SEEK_SET L_SET
33
+ #define memset(s,c,n) bzero((s), (n)) /* only when c is zero */
34
+ #define memcpy(s1,s2,n) bcopy((s2), (s1), (n))
35
+ #define memcmp(s1,s2,n) bcmp((s1),(s2),(n))
36
+ #endif
37
+
38
+ /*
39
+ * important tuning parms (hah)
40
+ */
41
+
42
+ #ifndef SEEDUPS
43
+ #define SEEDUPS 1 /* always detect duplicates */
44
+ #endif
45
+ #ifndef BADMESS
46
+ #define BADMESS 1 /* generate a message for worst case:
47
+ cannot make room after SPLTMAX splits */
48
+ #endif
49
+
50
+ /*
51
+ * misc
52
+ */
53
+ #ifdef DEBUG
54
+ #define debug(x) printf x
55
+ #else
56
+ #define debug(x)
57
+ #endif
58
+
59
+ #ifdef BIG_E
60
+ #define GET_SHORT(p, i) (((unsigned)((unsigned char *)(p))[(i)*2] << 8) + (((unsigned char *)(p))[(i)*2 + 1]))
61
+ #define PUT_SHORT(p, i, s) (((unsigned char *)(p))[(i)*2] = (unsigned char)((s) >> 8), ((unsigned char *)(p))[(i)*2 + 1] = (unsigned char)(s))
62
+ #else
63
+ #define GET_SHORT(p, i) ((p)[(i)])
64
+ #define PUT_SHORT(p, i, s) ((p)[(i)] = (s))
65
+ #endif
66
+
67
+ /*#include "pair.h"*/
68
+ static int fitpair proto((char *, int));
69
+ static void putpair proto((char *, datum, datum));
70
+ static datum getpair proto((char *, datum));
71
+ static int delpair proto((char *, datum));
72
+ static int chkpage proto((char *));
73
+ static datum getnkey proto((char *, int));
74
+ static void splpage proto((char *, char *, long));
75
+ #if SEEDUPS
76
+ static int duppair proto((char *, datum));
77
+ #endif
78
+
79
+ #include <stdio.h>
80
+ #include <stdlib.h>
81
+ #ifdef DOSISH
82
+ #include <io.h>
83
+ #endif
84
+ #include <sys/types.h>
85
+ #include <sys/stat.h>
86
+ #ifdef BSD42
87
+ #include <sys/file.h>
88
+ #else
89
+ #include <fcntl.h>
90
+ /*#include <memory.h>*/
91
+ #endif
92
+ #ifndef O_BINARY
93
+ #define O_BINARY 0
94
+ #endif
95
+
96
+ #include <errno.h>
97
+ #ifndef EPERM
98
+ #define EPERM EACCES
99
+ #endif
100
+ #include <string.h>
101
+
102
+ #ifdef __STDC__
103
+ #include <stddef.h>
104
+ #endif
105
+
106
+ #ifndef NULL
107
+ #define NULL 0
108
+ #endif
109
+
110
+ /*
111
+ * externals
112
+ */
113
+ #if !defined sun && !defined _WIN32 && !defined __CYGWIN__ && !defined(errno)
114
+ extern int errno;
115
+ #endif
116
+
117
+ /*
118
+ * forward
119
+ */
120
+ static int getdbit proto((DBM *, long));
121
+ static int setdbit proto((DBM *, long));
122
+ static int getpage proto((DBM *, long));
123
+ static datum getnext proto((DBM *));
124
+ static int makroom proto((DBM *, long, int));
125
+
126
+ /*
127
+ * useful macros
128
+ */
129
+ #define bad(x) ((x).dptr == NULL || (x).dsize < 0)
130
+ #define exhash(item) sdbm_hash((item).dptr, (item).dsize)
131
+ #define ioerr(db) ((db)->flags |= DBM_IOERR)
132
+
133
+ #define OFF_PAG(off) (long) (off) * PBLKSIZ
134
+ #define OFF_DIR(off) (long) (off) * DBLKSIZ
135
+
136
+ static long masks[] = {
137
+ 000000000000L, 000000000001L, 000000000003L,
138
+ 000000000007L, 000000000017L, 000000000037L,
139
+ 000000000077L, 000000000177L, 000000000377L,
140
+ 000000000777L, 000000001777L, 000000003777L,
141
+ 000000007777L, 000000017777L, 000000037777L,
142
+ 000000077777L, 000000177777L, 000000377777L,
143
+ 000000777777L, 000001777777L, 000003777777L,
144
+ 000007777777L, 000017777777L, 000037777777L,
145
+ 000077777777L, 000177777777L, 000377777777L,
146
+ 000777777777L, 001777777777L, 003777777777L,
147
+ 007777777777L, 017777777777L
148
+ };
149
+
150
+ datum nullitem = {NULL, 0};
151
+
152
+ DBM *
153
+ sdbm_open(register char *file, register int flags, register int mode)
154
+ {
155
+ register DBM *db;
156
+ register char *dirname;
157
+ register char *pagname;
158
+ register size_t n;
159
+
160
+ if (file == NULL || !*file)
161
+ return errno = EINVAL, (DBM *) NULL;
162
+ /*
163
+ * need space for two seperate filenames
164
+ */
165
+ n = strlen(file) * 2 + strlen(DIRFEXT) + strlen(PAGFEXT) + 2;
166
+
167
+ if ((dirname = malloc(n)) == NULL)
168
+ return errno = ENOMEM, (DBM *) NULL;
169
+ /*
170
+ * build the file names
171
+ */
172
+ dirname = strcat(strcpy(dirname, file), DIRFEXT);
173
+ pagname = strcpy(dirname + strlen(dirname) + 1, file);
174
+ pagname = strcat(pagname, PAGFEXT);
175
+
176
+ db = sdbm_prep(dirname, pagname, flags, mode);
177
+ free((char *) dirname);
178
+ return db;
179
+ }
180
+
181
+ DBM *
182
+ sdbm_prep(char *dirname, char *pagname, int flags, int mode)
183
+ {
184
+ register DBM *db;
185
+ struct stat dstat;
186
+
187
+ if ((db = (DBM *) malloc(sizeof(DBM))) == NULL)
188
+ return errno = ENOMEM, (DBM *) NULL;
189
+
190
+ db->flags = 0;
191
+ db->hmask = 0;
192
+ db->blkptr = 0;
193
+ db->keyptr = 0;
194
+ /*
195
+ * adjust user flags so that WRONLY becomes RDWR,
196
+ * as required by this package. Also set our internal
197
+ * flag for RDONLY.
198
+ */
199
+ if (flags & O_WRONLY)
200
+ flags = (flags & ~O_WRONLY) | O_RDWR;
201
+ if (flags & O_RDONLY)
202
+ db->flags = DBM_RDONLY;
203
+ /*
204
+ * open the files in sequence, and stat the dirfile.
205
+ * If we fail anywhere, undo everything, return NULL.
206
+ */
207
+ flags |= O_BINARY;
208
+ if ((db->pagf = open(pagname, flags, mode)) > -1) {
209
+ if ((db->dirf = open(dirname, flags, mode)) > -1) {
210
+ /*
211
+ * need the dirfile size to establish max bit number.
212
+ */
213
+ if (fstat(db->dirf, &dstat) == 0) {
214
+ /*
215
+ * zero size: either a fresh database, or one with a single,
216
+ * unsplit data page: dirpage is all zeros.
217
+ */
218
+ db->dirbno = (!dstat.st_size) ? 0 : -1;
219
+ db->pagbno = -1;
220
+ db->maxbno = dstat.st_size * (long) BYTESIZ;
221
+
222
+ (void) memset(db->pagbuf, 0, PBLKSIZ);
223
+ (void) memset(db->dirbuf, 0, DBLKSIZ);
224
+ /*
225
+ * success
226
+ */
227
+ return db;
228
+ }
229
+ (void) close(db->dirf);
230
+ }
231
+ (void) close(db->pagf);
232
+ }
233
+ free((char *) db);
234
+ return (DBM *) NULL;
235
+ }
236
+
237
+ void
238
+ sdbm_close(register DBM *db)
239
+ {
240
+ if (db == NULL)
241
+ errno = EINVAL;
242
+ else {
243
+ (void) close(db->dirf);
244
+ (void) close(db->pagf);
245
+ free((char *) db);
246
+ }
247
+ }
248
+
249
+ datum
250
+ sdbm_fetch(register DBM *db, datum key)
251
+ {
252
+ if (db == NULL || bad(key))
253
+ return errno = EINVAL, nullitem;
254
+
255
+ if (getpage(db, exhash(key)))
256
+ return getpair(db->pagbuf, key);
257
+
258
+ return ioerr(db), nullitem;
259
+ }
260
+
261
+ int
262
+ sdbm_delete(register DBM *db, datum key)
263
+ {
264
+ if (db == NULL || bad(key))
265
+ return errno = EINVAL, -1;
266
+ if (sdbm_rdonly(db))
267
+ return errno = EPERM, -1;
268
+
269
+ if (getpage(db, exhash(key))) {
270
+ if (!delpair(db->pagbuf, key))
271
+ return -1;
272
+ /*
273
+ * update the page file
274
+ */
275
+ if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
276
+ || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
277
+ return ioerr(db), -1;
278
+
279
+ return 0;
280
+ }
281
+
282
+ return ioerr(db), -1;
283
+ }
284
+
285
+ int
286
+ sdbm_store(register DBM *db, datum key, datum val, int flags)
287
+ {
288
+ int need;
289
+ register long hash;
290
+
291
+ if (db == NULL || bad(key))
292
+ return errno = EINVAL, -1;
293
+ if (sdbm_rdonly(db))
294
+ return errno = EPERM, -1;
295
+
296
+ need = key.dsize + val.dsize;
297
+ /*
298
+ * is the pair too big (or too small) for this database ??
299
+ */
300
+ if (need < 0 || need > PAIRMAX)
301
+ return errno = EINVAL, -1;
302
+
303
+ if (getpage(db, (hash = exhash(key)))) {
304
+ /*
305
+ * if we need to replace, delete the key/data pair
306
+ * first. If it is not there, ignore.
307
+ */
308
+ if (flags == DBM_REPLACE)
309
+ (void) delpair(db->pagbuf, key);
310
+ #if SEEDUPS
311
+ else if (duppair(db->pagbuf, key))
312
+ return 1;
313
+ #endif
314
+ /*
315
+ * if we do not have enough room, we have to split.
316
+ */
317
+ if (!fitpair(db->pagbuf, need))
318
+ if (!makroom(db, hash, need))
319
+ return ioerr(db), -1;
320
+ /*
321
+ * we have enough room or split is successful. insert the key,
322
+ * and update the page file.
323
+ */
324
+ (void) putpair(db->pagbuf, key, val);
325
+
326
+ if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
327
+ || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
328
+ return ioerr(db), -1;
329
+ /*
330
+ * success
331
+ */
332
+ return 0;
333
+ }
334
+
335
+ return ioerr(db), -1;
336
+ }
337
+
338
+ /*
339
+ * makroom - make room by splitting the overfull page
340
+ * this routine will attempt to make room for SPLTMAX times before
341
+ * giving up.
342
+ */
343
+ static int
344
+ makroom(register DBM *db, long int hash, int need)
345
+ {
346
+ long newp;
347
+ char twin[PBLKSIZ];
348
+ #if defined _WIN32 && !defined __CYGWIN__
349
+ char zer[PBLKSIZ];
350
+ long oldtail;
351
+ #endif
352
+ char *pag = db->pagbuf;
353
+ char *new = twin;
354
+ register int smax = SPLTMAX;
355
+
356
+ do {
357
+ /*
358
+ * split the current page
359
+ */
360
+ (void) splpage(pag, new, db->hmask + 1);
361
+ /*
362
+ * address of the new page
363
+ */
364
+ newp = (hash & db->hmask) | (db->hmask + 1);
365
+ debug(("newp: %ld\n", newp));
366
+ /*
367
+ * write delay, read avoidence/cache shuffle:
368
+ * select the page for incoming pair: if key is to go to the new page,
369
+ * write out the previous one, and copy the new one over, thus making
370
+ * it the current page. If not, simply write the new page, and we are
371
+ * still looking at the page of interest. current page is not updated
372
+ * here, as sdbm_store will do so, after it inserts the incoming pair.
373
+ */
374
+
375
+ #if defined _WIN32 && !defined __CYGWIN__
376
+ /*
377
+ * Fill hole with 0 if made it.
378
+ * (hole is NOT read as 0)
379
+ */
380
+ oldtail = lseek(db->pagf, 0L, SEEK_END);
381
+ memset(zer, 0, PBLKSIZ);
382
+ while (OFF_PAG(newp) > oldtail) {
383
+ if (lseek(db->pagf, 0L, SEEK_END) < 0 ||
384
+ write(db->pagf, zer, PBLKSIZ) < 0) {
385
+
386
+ return 0;
387
+ }
388
+ oldtail += PBLKSIZ;
389
+ }
390
+ #endif
391
+
392
+ if (hash & (db->hmask + 1)) {
393
+ if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
394
+ || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
395
+ return 0;
396
+ db->pagbno = newp;
397
+ (void) memcpy(pag, new, PBLKSIZ);
398
+ }
399
+ else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0
400
+ || write(db->pagf, new, PBLKSIZ) < 0)
401
+ return 0;
402
+
403
+ if (!setdbit(db, db->curbit))
404
+ return 0;
405
+ /*
406
+ * see if we have enough room now
407
+ */
408
+ if (fitpair(pag, need))
409
+ return 1;
410
+ /*
411
+ * try again... update curbit and hmask as getpage would have
412
+ * done. because of our update of the current page, we do not
413
+ * need to read in anything. BUT we have to write the current
414
+ * [deferred] page out, as the window of failure is too great.
415
+ */
416
+ db->curbit = 2 * db->curbit +
417
+ ((hash & (db->hmask + 1)) ? 2 : 1);
418
+ db->hmask |= (db->hmask + 1);
419
+
420
+ if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
421
+ || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
422
+ return 0;
423
+
424
+ } while (--smax);
425
+ /*
426
+ * if we are here, this is real bad news. After SPLTMAX splits,
427
+ * we still cannot fit the key. say goodnight.
428
+ */
429
+ #if BADMESS
430
+ (void) (write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44) < 0);
431
+ #endif
432
+ return 0;
433
+
434
+ }
435
+
436
+ /*
437
+ * the following two routines will break if
438
+ * deletions aren't taken into account. (ndbm bug)
439
+ */
440
+ datum
441
+ sdbm_firstkey(register DBM *db)
442
+ {
443
+ if (db == NULL)
444
+ return errno = EINVAL, nullitem;
445
+ /*
446
+ * start at page 0
447
+ */
448
+ (void) memset(db->pagbuf, 0, PBLKSIZ);
449
+ if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0
450
+ || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
451
+ return ioerr(db), nullitem;
452
+ db->pagbno = 0;
453
+ db->blkptr = 0;
454
+ db->keyptr = 0;
455
+
456
+ return getnext(db);
457
+ }
458
+
459
+ datum
460
+ sdbm_nextkey(register DBM *db)
461
+ {
462
+ if (db == NULL)
463
+ return errno = EINVAL, nullitem;
464
+ return getnext(db);
465
+ }
466
+
467
+ /*
468
+ * all important binary trie traversal
469
+ */
470
+ static int
471
+ getpage(register DBM *db, register long int hash)
472
+ {
473
+ register int hbit;
474
+ register long dbit;
475
+ register long pagb;
476
+
477
+ dbit = 0;
478
+ hbit = 0;
479
+ while (dbit < db->maxbno && getdbit(db, dbit))
480
+ dbit = 2 * dbit + ((hash & ((long) 1 << hbit++)) ? 2 : 1);
481
+
482
+ debug(("dbit: %d...", dbit));
483
+
484
+ db->curbit = dbit;
485
+ db->hmask = masks[hbit];
486
+
487
+ pagb = hash & db->hmask;
488
+ /*
489
+ * see if the block we need is already in memory.
490
+ * note: this lookaside cache has about 10% hit rate.
491
+ */
492
+ if (pagb != db->pagbno) {
493
+ /*
494
+ * note: here, we assume a "hole" is read as 0s.
495
+ * if not, must zero pagbuf first.
496
+ */
497
+ (void) memset(db->pagbuf, 0, PBLKSIZ);
498
+
499
+ if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
500
+ || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
501
+ return 0;
502
+ if (!chkpage(db->pagbuf)) {
503
+ return 0;
504
+ }
505
+ db->pagbno = pagb;
506
+
507
+ debug(("pag read: %d\n", pagb));
508
+ }
509
+ return 1;
510
+ }
511
+
512
+ static int
513
+ getdbit(register DBM *db, register long int dbit)
514
+ {
515
+ register long c;
516
+ register long dirb;
517
+
518
+ c = dbit / BYTESIZ;
519
+ dirb = c / DBLKSIZ;
520
+
521
+ if (dirb != db->dirbno) {
522
+ if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
523
+ || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
524
+ return 0;
525
+ db->dirbno = dirb;
526
+
527
+ debug(("dir read: %d\n", dirb));
528
+ }
529
+
530
+ return db->dirbuf[c % DBLKSIZ] & (1 << (dbit % BYTESIZ));
531
+ }
532
+
533
+ static int
534
+ setdbit(register DBM *db, register long int dbit)
535
+ {
536
+ register long c;
537
+ register long dirb;
538
+
539
+ c = dbit / BYTESIZ;
540
+ dirb = c / DBLKSIZ;
541
+
542
+ if (dirb != db->dirbno) {
543
+ if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
544
+ || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
545
+ return 0;
546
+ db->dirbno = dirb;
547
+
548
+ debug(("dir read: %d\n", dirb));
549
+ }
550
+
551
+ db->dirbuf[c % DBLKSIZ] |= (1 << (dbit % BYTESIZ));
552
+
553
+ if (dbit >= db->maxbno)
554
+ db->maxbno += (long) DBLKSIZ * BYTESIZ;
555
+
556
+ if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
557
+ || write(db->dirf, db->dirbuf, DBLKSIZ) < 0)
558
+ return 0;
559
+
560
+ return 1;
561
+ }
562
+
563
+ /*
564
+ * getnext - get the next key in the page, and if done with
565
+ * the page, try the next page in sequence
566
+ */
567
+ static datum
568
+ getnext(register DBM *db)
569
+ {
570
+ datum key;
571
+
572
+ for (;;) {
573
+ db->keyptr++;
574
+ key = getnkey(db->pagbuf, db->keyptr);
575
+ if (key.dptr != NULL)
576
+ return key;
577
+ /*
578
+ * we either run out, or there is nothing on this page..
579
+ * try the next one... If we lost our position on the
580
+ * file, we will have to seek.
581
+ */
582
+ db->keyptr = 0;
583
+ if (db->pagbno != db->blkptr++)
584
+ if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0)
585
+ break;
586
+ db->pagbno = db->blkptr;
587
+ if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0)
588
+ break;
589
+ if (!chkpage(db->pagbuf)) {
590
+ break;
591
+ }
592
+ }
593
+
594
+ return ioerr(db), nullitem;
595
+ }
596
+
597
+ /* pair.c */
598
+ /*
599
+ * sdbm - ndbm work-alike hashed database library
600
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
601
+ * author: oz@nexus.yorku.ca
602
+ * status: public domain.
603
+ *
604
+ * page-level routines
605
+ */
606
+
607
+ #ifndef lint
608
+ /*char pair_rcsid[] = "$Id$";*/
609
+ #endif
610
+
611
+ #ifndef BSD42
612
+ /*#include <memory.h>*/
613
+ #endif
614
+
615
+ #define exhash(item) sdbm_hash((item).dptr, (item).dsize)
616
+
617
+ /*
618
+ * forward
619
+ */
620
+ static int seepair proto((char *, int, char *, int));
621
+
622
+ /*
623
+ * page format:
624
+ * +------------------------------+
625
+ * ino | n | keyoff | datoff | keyoff |
626
+ * +------------+--------+--------+
627
+ * | datoff | - - - ----> |
628
+ * +--------+---------------------+
629
+ * | F R E E A R E A |
630
+ * +--------------+---------------+
631
+ * | <---- - - - | data |
632
+ * +--------+-----+----+----------+
633
+ * | key | data | key |
634
+ * +--------+----------+----------+
635
+ *
636
+ * calculating the offsets for free area: if the number
637
+ * of entries (ino[0]) is zero, the offset to the END of
638
+ * the free area is the block size. Otherwise, it is the
639
+ * nth (ino[ino[0]]) entry's offset.
640
+ */
641
+
642
+ static int
643
+ fitpair(char *pag, int need)
644
+ {
645
+ register int n;
646
+ register int off;
647
+ register int free;
648
+ register short *ino = (short *) pag;
649
+
650
+ off = ((n = GET_SHORT(ino,0)) > 0) ? GET_SHORT(ino,n) : PBLKSIZ;
651
+ free = off - (n + 1) * sizeof(short);
652
+ need += 2 * sizeof(short);
653
+
654
+ debug(("free %d need %d\n", free, need));
655
+
656
+ return need <= free;
657
+ }
658
+
659
+ static void
660
+ putpair(char *pag, datum key, datum val)
661
+ {
662
+ register int n;
663
+ register int off;
664
+ register short *ino = (short *) pag;
665
+
666
+ off = ((n = GET_SHORT(ino,0)) > 0) ? GET_SHORT(ino,n) : PBLKSIZ;
667
+ /*
668
+ * enter the key first
669
+ */
670
+ off -= key.dsize;
671
+ if (key.dsize)
672
+ (void) memcpy(pag + off, key.dptr, key.dsize);
673
+ PUT_SHORT(ino,n + 1,off);
674
+ /*
675
+ * now the data
676
+ */
677
+ off -= val.dsize;
678
+ if (val.dsize)
679
+ (void) memcpy(pag + off, val.dptr, val.dsize);
680
+ PUT_SHORT(ino,n + 2,off);
681
+ /*
682
+ * adjust item count
683
+ */
684
+ PUT_SHORT(ino,0,GET_SHORT(ino,0) + 2);
685
+ }
686
+
687
+ static datum
688
+ getpair(char *pag, datum key)
689
+ {
690
+ register int i;
691
+ register int n;
692
+ datum val;
693
+ register short *ino = (short *) pag;
694
+
695
+ if ((n = GET_SHORT(ino,0)) == 0)
696
+ return nullitem;
697
+
698
+ if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
699
+ return nullitem;
700
+
701
+ val.dptr = pag + GET_SHORT(ino,i + 1);
702
+ val.dsize = GET_SHORT(ino,i) - GET_SHORT(ino,i + 1);
703
+ return val;
704
+ }
705
+
706
+ #if SEEDUPS
707
+ static int
708
+ duppair(char *pag, datum key)
709
+ {
710
+ register short *ino = (short *) pag;
711
+ return GET_SHORT(ino,0) > 0 &&
712
+ seepair(pag, GET_SHORT(ino,0), key.dptr, key.dsize) > 0;
713
+ }
714
+ #endif
715
+
716
+ static datum
717
+ getnkey(char *pag, int num)
718
+ {
719
+ datum key;
720
+ register int off;
721
+ register short *ino = (short *) pag;
722
+
723
+ num = num * 2 - 1;
724
+ if (GET_SHORT(ino,0) == 0 || num > GET_SHORT(ino,0))
725
+ return nullitem;
726
+
727
+ off = (num > 1) ? GET_SHORT(ino,num - 1) : PBLKSIZ;
728
+
729
+ key.dptr = pag + GET_SHORT(ino,num);
730
+ key.dsize = off - GET_SHORT(ino,num);
731
+
732
+ return key;
733
+ }
734
+
735
+ static int
736
+ delpair(char *pag, datum key)
737
+ {
738
+ register int n;
739
+ register int i;
740
+ register short *ino = (short *) pag;
741
+
742
+ if ((n = GET_SHORT(ino,0)) == 0)
743
+ return 0;
744
+
745
+ if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0)
746
+ return 0;
747
+ /*
748
+ * found the key. if it is the last entry
749
+ * [i.e. i == n - 1] we just adjust the entry count.
750
+ * hard case: move all data down onto the deleted pair,
751
+ * shift offsets onto deleted offsets, and adjust them.
752
+ * [note: 0 < i < n]
753
+ */
754
+ if (i < n - 1) {
755
+ register int m;
756
+ register char *dst = pag + (i == 1 ? PBLKSIZ : GET_SHORT(ino,i - 1));
757
+ register char *src = pag + GET_SHORT(ino,i + 1);
758
+ register ptrdiff_t zoo = dst - src;
759
+
760
+ debug(("free-up %"PRIdPTRDIFF" ", zoo));
761
+ /*
762
+ * shift data/keys down
763
+ */
764
+ m = GET_SHORT(ino,i + 1) - GET_SHORT(ino,n);
765
+ #ifdef DUFF
766
+ #define MOVB *--dst = *--src
767
+
768
+ if (m > 0) {
769
+ register int loop = (m + 8 - 1) >> 3;
770
+
771
+ switch (m & (8 - 1)) {
772
+ case 0: do {
773
+ MOVB; case 7: MOVB;
774
+ case 6: MOVB; case 5: MOVB;
775
+ case 4: MOVB; case 3: MOVB;
776
+ case 2: MOVB; case 1: MOVB;
777
+ } while (--loop);
778
+ }
779
+ }
780
+ #else
781
+ #ifdef MEMMOVE
782
+ memmove(dst, src, m);
783
+ #else
784
+ while (m--)
785
+ *--dst = *--src;
786
+ #endif
787
+ #endif
788
+ /*
789
+ * adjust offset index up
790
+ */
791
+ while (i < n - 1) {
792
+ PUT_SHORT(ino,i, GET_SHORT(ino,i + 2) + zoo);
793
+ i++;
794
+ }
795
+ }
796
+ PUT_SHORT(ino, 0, GET_SHORT(ino, 0) - 2);
797
+ return 1;
798
+ }
799
+
800
+ /*
801
+ * search for the key in the page.
802
+ * return offset index in the range 0 < i < n.
803
+ * return 0 if not found.
804
+ */
805
+ static int
806
+ seepair(char *pag, register int n, register char *key, register int siz)
807
+ {
808
+ register int i;
809
+ register int off = PBLKSIZ;
810
+ register short *ino = (short *) pag;
811
+
812
+ for (i = 1; i < n; i += 2) {
813
+ if (siz == off - GET_SHORT(ino,i) &&
814
+ memcmp(key, pag + GET_SHORT(ino,i), siz) == 0)
815
+ return i;
816
+ off = GET_SHORT(ino,i + 1);
817
+ }
818
+ return 0;
819
+ }
820
+
821
+ static void
822
+ splpage(char *pag, char *new, long int sbit)
823
+ {
824
+ datum key;
825
+ datum val;
826
+
827
+ register int n;
828
+ register int off = PBLKSIZ;
829
+ char cur[PBLKSIZ];
830
+ register short *ino = (short *) cur;
831
+
832
+ (void) memcpy(cur, pag, PBLKSIZ);
833
+ (void) memset(pag, 0, PBLKSIZ);
834
+ (void) memset(new, 0, PBLKSIZ);
835
+
836
+ n = GET_SHORT(ino,0);
837
+ for (ino++; n > 0; ino += 2) {
838
+ key.dptr = cur + GET_SHORT(ino,0);
839
+ key.dsize = off - GET_SHORT(ino,0);
840
+ val.dptr = cur + GET_SHORT(ino,1);
841
+ val.dsize = GET_SHORT(ino,0) - GET_SHORT(ino,1);
842
+ /*
843
+ * select the page pointer (by looking at sbit) and insert
844
+ */
845
+ (void) putpair((exhash(key) & sbit) ? new : pag, key, val);
846
+
847
+ off = GET_SHORT(ino,1);
848
+ n -= 2;
849
+ }
850
+
851
+ debug(("%d split %d/%d\n", ((short *) cur)[0] / 2,
852
+ ((short *) new)[0] / 2,
853
+ ((short *) pag)[0] / 2));
854
+ }
855
+
856
+ /*
857
+ * check page sanity:
858
+ * number of entries should be something
859
+ * reasonable, and all offsets in the index should be in order.
860
+ * this could be made more rigorous.
861
+ */
862
+ static int
863
+ chkpage(char *pag)
864
+ {
865
+ register int n;
866
+ register int off;
867
+ register short *ino = (short *) pag;
868
+
869
+ if ((n = GET_SHORT(ino,0)) < 0 || n > PBLKSIZ / (int)sizeof(short))
870
+ return 0;
871
+
872
+ if (n > 0) {
873
+ off = PBLKSIZ;
874
+ for (ino++; n > 0; ino += 2) {
875
+ if (GET_SHORT(ino,0) > off || GET_SHORT(ino,1) > off ||
876
+ GET_SHORT(ino,1) > GET_SHORT(ino,0))
877
+ return 0;
878
+ off = GET_SHORT(ino,1);
879
+ n -= 2;
880
+ }
881
+ }
882
+ return 1;
883
+ }
884
+
885
+ /* hash.c */
886
+ /*
887
+ * sdbm - ndbm work-alike hashed database library
888
+ * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
889
+ * author: oz@nexus.yorku.ca
890
+ * status: public domain. keep it that way.
891
+ *
892
+ * hashing routine
893
+ */
894
+
895
+ /*
896
+ * polynomial conversion ignoring overflows
897
+ * [this seems to work remarkably well, in fact better
898
+ * then the ndbm hash function. Replace at your own risk]
899
+ * use: 65599 nice.
900
+ * 65587 even better.
901
+ */
902
+ long
903
+ sdbm_hash(register char *str, register int len)
904
+ {
905
+ register unsigned long n = 0;
906
+
907
+ #ifdef DUFF
908
+
909
+ #define HASHC n = *str++ + 65599 * n
910
+
911
+ if (len > 0) {
912
+ register int loop = (len + 8 - 1) >> 3;
913
+
914
+ switch(len & (8 - 1)) {
915
+ case 0: do {
916
+ HASHC; case 7: HASHC;
917
+ case 6: HASHC; case 5: HASHC;
918
+ case 4: HASHC; case 3: HASHC;
919
+ case 2: HASHC; case 1: HASHC;
920
+ } while (--loop);
921
+ }
922
+
923
+ }
924
+ #else
925
+ while (len--)
926
+ n = ((*str++) & 255) + 65587L * n;
927
+ #endif
928
+ return n;
929
+ }