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