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.
- checksums.yaml +7 -0
- data/ext/sdbm/_sdbm.c +952 -0
- data/ext/sdbm/depend +25 -0
- data/ext/sdbm/extconf.rb +5 -0
- data/ext/sdbm/init.c +1067 -0
- data/ext/sdbm/sdbm.h +86 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -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
|
data/ext/sdbm/_sdbm.c
ADDED
@@ -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
|
+
}
|