perl 5.000
[p5sagit/p5-mst-13.2.git] / ext / SDBM_File / sdbm / sdbm.c
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 static char rcsid[] = "$Id: sdbm.c,v 1.16 90/12/13 13:01:31 oz Exp $";
12 #endif
13
14 #include "config.h"
15 #include "sdbm.h"
16 #include "tune.h"
17 #include "pair.h"
18
19 #ifdef I_FCNTL
20 # include <fcntl.h>
21 #endif
22 #ifdef I_SYS_FILE
23 # include <sys/file.h>
24 #endif
25
26 #ifdef I_STRING
27 # include <string.h>
28 #else
29 # include <strings.h>
30 #endif
31
32 /*
33  * externals
34  */
35 #ifndef sun
36 extern int errno;
37 #endif
38
39 extern Malloc_t malloc proto((MEM_SIZE));
40 extern void free proto((void *));
41 extern Off_t lseek();
42
43 /*
44  * forward
45  */
46 static int getdbit proto((DBM *, long));
47 static int setdbit proto((DBM *, long));
48 static int getpage proto((DBM *, long));
49 static datum getnext proto((DBM *));
50 static int makroom proto((DBM *, long, int));
51
52 /*
53  * useful macros
54  */
55 #define bad(x)          ((x).dptr == NULL || (x).dsize < 0)
56 #define exhash(item)    sdbm_hash((item).dptr, (item).dsize)
57 #define ioerr(db)       ((db)->flags |= DBM_IOERR)
58
59 #define OFF_PAG(off)    (long) (off) * PBLKSIZ
60 #define OFF_DIR(off)    (long) (off) * DBLKSIZ
61
62 static long masks[] = {
63         000000000000, 000000000001, 000000000003, 000000000007,
64         000000000017, 000000000037, 000000000077, 000000000177,
65         000000000377, 000000000777, 000000001777, 000000003777,
66         000000007777, 000000017777, 000000037777, 000000077777,
67         000000177777, 000000377777, 000000777777, 000001777777,
68         000003777777, 000007777777, 000017777777, 000037777777,
69         000077777777, 000177777777, 000377777777, 000777777777,
70         001777777777, 003777777777, 007777777777, 017777777777
71 };
72
73 datum nullitem = {NULL, 0};
74
75 DBM *
76 sdbm_open(file, flags, mode)
77 register char *file;
78 register int flags;
79 register int mode;
80 {
81         register DBM *db;
82         register char *dirname;
83         register char *pagname;
84         register int n;
85
86         if (file == NULL || !*file)
87                 return errno = EINVAL, (DBM *) NULL;
88 /*
89  * need space for two seperate filenames
90  */
91         n = strlen(file) * 2 + strlen(DIRFEXT) + strlen(PAGFEXT) + 2;
92
93         if ((dirname = malloc((unsigned) n)) == NULL)
94                 return errno = ENOMEM, (DBM *) NULL;
95 /*
96  * build the file names
97  */
98         dirname = strcat(strcpy(dirname, file), DIRFEXT);
99         pagname = strcpy(dirname + strlen(dirname) + 1, file);
100         pagname = strcat(pagname, PAGFEXT);
101
102         db = sdbm_prep(dirname, pagname, flags, mode);
103         free((char *) dirname);
104         return db;
105 }
106
107 DBM *
108 sdbm_prep(dirname, pagname, flags, mode)
109 char *dirname;
110 char *pagname;
111 int flags;
112 int mode;
113 {
114         register DBM *db;
115         struct stat dstat;
116
117         if ((db = (DBM *) malloc(sizeof(DBM))) == NULL)
118                 return errno = ENOMEM, (DBM *) NULL;
119
120         db->flags = 0;
121         db->hmask = 0;
122         db->blkptr = 0;
123         db->keyptr = 0;
124 /*
125  * adjust user flags so that WRONLY becomes RDWR, 
126  * as required by this package. Also set our internal
127  * flag for RDONLY if needed.
128  */
129         if (flags & O_WRONLY)
130                 flags = (flags & ~O_WRONLY) | O_RDWR;
131
132         else if ((flags & 03) == O_RDONLY)
133                 db->flags = DBM_RDONLY;
134 /*
135  * open the files in sequence, and stat the dirfile.
136  * If we fail anywhere, undo everything, return NULL.
137  */
138         if ((db->pagf = open(pagname, flags, mode)) > -1) {
139                 if ((db->dirf = open(dirname, flags, mode)) > -1) {
140 /*
141  * need the dirfile size to establish max bit number.
142  */
143                         if (fstat(db->dirf, &dstat) == 0) {
144 /*
145  * zero size: either a fresh database, or one with a single,
146  * unsplit data page: dirpage is all zeros.
147  */
148                                 db->dirbno = (!dstat.st_size) ? 0 : -1;
149                                 db->pagbno = -1;
150                                 db->maxbno = dstat.st_size * BYTESIZ;
151
152                                 (void) memset(db->pagbuf, 0, PBLKSIZ);
153                                 (void) memset(db->dirbuf, 0, DBLKSIZ);
154                         /*
155                          * success
156                          */
157                                 return db;
158                         }
159                         (void) close(db->dirf);
160                 }
161                 (void) close(db->pagf);
162         }
163         free((char *) db);
164         return (DBM *) NULL;
165 }
166
167 void
168 sdbm_close(db)
169 register DBM *db;
170 {
171         if (db == NULL)
172                 errno = EINVAL;
173         else {
174                 (void) close(db->dirf);
175                 (void) close(db->pagf);
176                 free((char *) db);
177         }
178 }
179
180 datum
181 sdbm_fetch(db, key)
182 register DBM *db;
183 datum key;
184 {
185         if (db == NULL || bad(key))
186                 return errno = EINVAL, nullitem;
187
188         if (getpage(db, exhash(key)))
189                 return getpair(db->pagbuf, key);
190
191         return ioerr(db), nullitem;
192 }
193
194 int
195 sdbm_delete(db, key)
196 register DBM *db;
197 datum key;
198 {
199         if (db == NULL || bad(key))
200                 return errno = EINVAL, -1;
201         if (sdbm_rdonly(db))
202                 return errno = EPERM, -1;
203
204         if (getpage(db, exhash(key))) {
205                 if (!delpair(db->pagbuf, key))
206                         return -1;
207 /*
208  * update the page file
209  */
210                 if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
211                     || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
212                         return ioerr(db), -1;
213
214                 return 0;
215         }
216
217         return ioerr(db), -1;
218 }
219
220 int
221 sdbm_store(db, key, val, flags)
222 register DBM *db;
223 datum key;
224 datum val;
225 int flags;
226 {
227         int need;
228         register long hash;
229
230         if (db == NULL || bad(key))
231                 return errno = EINVAL, -1;
232         if (sdbm_rdonly(db))
233                 return errno = EPERM, -1;
234
235         need = key.dsize + val.dsize;
236 /*
237  * is the pair too big (or too small) for this database ??
238  */
239         if (need < 0 || need > PAIRMAX)
240                 return errno = EINVAL, -1;
241
242         if (getpage(db, (hash = exhash(key)))) {
243 /*
244  * if we need to replace, delete the key/data pair
245  * first. If it is not there, ignore.
246  */
247                 if (flags == DBM_REPLACE)
248                         (void) delpair(db->pagbuf, key);
249 #ifdef SEEDUPS
250                 else if (duppair(db->pagbuf, key))
251                         return 1;
252 #endif
253 /*
254  * if we do not have enough room, we have to split.
255  */
256                 if (!fitpair(db->pagbuf, need))
257                         if (!makroom(db, hash, need))
258                                 return ioerr(db), -1;
259 /*
260  * we have enough room or split is successful. insert the key,
261  * and update the page file.
262  */
263                 (void) putpair(db->pagbuf, key, val);
264
265                 if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
266                     || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
267                         return ioerr(db), -1;
268         /*
269          * success
270          */
271                 return 0;
272         }
273
274         return ioerr(db), -1;
275 }
276
277 /*
278  * makroom - make room by splitting the overfull page
279  * this routine will attempt to make room for SPLTMAX times before
280  * giving up.
281  */
282 static int
283 makroom(db, hash, need)
284 register DBM *db;
285 long hash;
286 int need;
287 {
288         long newp;
289         char twin[PBLKSIZ];
290         char *pag = db->pagbuf;
291         char *new = twin;
292         register int smax = SPLTMAX;
293
294         do {
295 /*
296  * split the current page
297  */
298                 (void) splpage(pag, new, db->hmask + 1);
299 /*
300  * address of the new page
301  */
302                 newp = (hash & db->hmask) | (db->hmask + 1);
303
304 /*
305  * write delay, read avoidence/cache shuffle:
306  * select the page for incoming pair: if key is to go to the new page,
307  * write out the previous one, and copy the new one over, thus making
308  * it the current page. If not, simply write the new page, and we are
309  * still looking at the page of interest. current page is not updated
310  * here, as sdbm_store will do so, after it inserts the incoming pair.
311  */
312                 if (hash & (db->hmask + 1)) {
313                         if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
314                             || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
315                                 return 0;
316                         db->pagbno = newp;
317                         (void) memcpy(pag, new, PBLKSIZ);
318                 }
319                 else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0
320                          || write(db->pagf, new, PBLKSIZ) < 0)
321                         return 0;
322
323                 if (!setdbit(db, db->curbit))
324                         return 0;
325 /*
326  * see if we have enough room now
327  */
328                 if (fitpair(pag, need))
329                         return 1;
330 /*
331  * try again... update curbit and hmask as getpage would have
332  * done. because of our update of the current page, we do not
333  * need to read in anything. BUT we have to write the current
334  * [deferred] page out, as the window of failure is too great.
335  */
336                 db->curbit = 2 * db->curbit +
337                         ((hash & (db->hmask + 1)) ? 2 : 1);
338                 db->hmask |= db->hmask + 1;
339
340                 if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0
341                     || write(db->pagf, db->pagbuf, PBLKSIZ) < 0)
342                         return 0;
343
344         } while (--smax);
345 /*
346  * if we are here, this is real bad news. After SPLTMAX splits,
347  * we still cannot fit the key. say goodnight.
348  */
349 #ifdef BADMESS
350         (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
351 #endif
352         return 0;
353
354 }
355
356 /*
357  * the following two routines will break if
358  * deletions aren't taken into account. (ndbm bug)
359  */
360 datum
361 sdbm_firstkey(db)
362 register DBM *db;
363 {
364         if (db == NULL)
365                 return errno = EINVAL, nullitem;
366 /*
367  * start at page 0
368  */
369         if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0
370             || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
371                 return ioerr(db), nullitem;
372         db->pagbno = 0;
373         db->blkptr = 0;
374         db->keyptr = 0;
375
376         return getnext(db);
377 }
378
379 datum
380 sdbm_nextkey(db)
381 register DBM *db;
382 {
383         if (db == NULL)
384                 return errno = EINVAL, nullitem;
385         return getnext(db);
386 }
387
388 /*
389  * all important binary trie traversal
390  */
391 static int
392 getpage(db, hash)
393 register DBM *db;
394 register long hash;
395 {
396         register int hbit;
397         register long dbit;
398         register long pagb;
399
400         dbit = 0;
401         hbit = 0;
402         while (dbit < db->maxbno && getdbit(db, dbit))
403                 dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
404
405         debug(("dbit: %d...", dbit));
406
407         db->curbit = dbit;
408         db->hmask = masks[hbit];
409
410         pagb = hash & db->hmask;
411 /*
412  * see if the block we need is already in memory.
413  * note: this lookaside cache has about 10% hit rate.
414  */
415         if (pagb != db->pagbno) { 
416 /*
417  * note: here, we assume a "hole" is read as 0s.
418  * if not, must zero pagbuf first.
419  */
420                 if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0
421                     || read(db->pagf, db->pagbuf, PBLKSIZ) < 0)
422                         return 0;
423                 if (!chkpage(db->pagbuf))
424                         return 0;
425                 db->pagbno = pagb;
426
427                 debug(("pag read: %d\n", pagb));
428         }
429         return 1;
430 }
431
432 static int
433 getdbit(db, dbit)
434 register DBM *db;
435 register long dbit;
436 {
437         register long c;
438         register long dirb;
439
440         c = dbit / BYTESIZ;
441         dirb = c / DBLKSIZ;
442
443         if (dirb != db->dirbno) {
444                 if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
445                     || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
446                         return 0;
447                 db->dirbno = dirb;
448
449                 debug(("dir read: %d\n", dirb));
450         }
451
452         return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
453 }
454
455 static int
456 setdbit(db, dbit)
457 register DBM *db;
458 register long dbit;
459 {
460         register long c;
461         register long dirb;
462
463         c = dbit / BYTESIZ;
464         dirb = c / DBLKSIZ;
465
466         if (dirb != db->dirbno) {
467                 if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
468                     || read(db->dirf, db->dirbuf, DBLKSIZ) < 0)
469                         return 0;
470                 db->dirbno = dirb;
471
472                 debug(("dir read: %d\n", dirb));
473         }
474
475         db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
476
477         if (dbit >= db->maxbno)
478                 db->maxbno += DBLKSIZ * BYTESIZ;
479
480         if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0
481             || write(db->dirf, db->dirbuf, DBLKSIZ) < 0)
482                 return 0;
483
484         return 1;
485 }
486
487 /*
488  * getnext - get the next key in the page, and if done with
489  * the page, try the next page in sequence
490  */
491 static datum
492 getnext(db)
493 register DBM *db;
494 {
495         datum key;
496
497         for (;;) {
498                 db->keyptr++;
499                 key = getnkey(db->pagbuf, db->keyptr);
500                 if (key.dptr != NULL)
501                         return key;
502 /*
503  * we either run out, or there is nothing on this page..
504  * try the next one... If we lost our position on the
505  * file, we will have to seek.
506  */
507                 db->keyptr = 0;
508                 if (db->pagbno != db->blkptr++)
509                         if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0)
510                                 break;
511                 db->pagbno = db->blkptr;
512                 if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0)
513                         break;
514                 if (!chkpage(db->pagbuf))
515                         break;
516         }
517
518         return ioerr(db), nullitem;
519 }
520