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