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