Annotation of parser3/src/lib/sdbm/sdbm.c, revision 1.7

1.1       paf         1: /* ====================================================================
                      2:  * The Apache Software License, Version 1.1
                      3:  *
                      4:  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
                      5:  * reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  *
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  *
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in
                     16:  *    the documentation and/or other materials provided with the
                     17:  *    distribution.
                     18:  *
                     19:  * 3. The end-user documentation included with the redistribution,
                     20:  *    if any, must include the following acknowledgment:
                     21:  *       "This product includes software developed by the
                     22:  *        Apache Software Foundation (http://www.apache.org/)."
                     23:  *    Alternately, this acknowledgment may appear in the software itself,
                     24:  *    if and wherever such third-party acknowledgments normally appear.
                     25:  *
                     26:  * 4. The names "Apache" and "Apache Software Foundation" must
                     27:  *    not be used to endorse or promote products derived from this
                     28:  *    software without prior written permission. For written
                     29:  *    permission, please contact apache@apache.org.
                     30:  *
                     31:  * 5. Products derived from this software may not be called "Apache",
                     32:  *    nor may "Apache" appear in their name, without prior written
                     33:  *    permission of the Apache Software Foundation.
                     34:  *
                     35:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
                     36:  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     37:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
                     38:  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
                     39:  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     40:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     41:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
                     42:  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
                     43:  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     44:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
                     45:  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     46:  * SUCH DAMAGE.
                     47:  * ====================================================================
                     48:  *
                     49:  * This software consists of voluntary contributions made by many
                     50:  * individuals on behalf of the Apache Software Foundation.  For more
                     51:  * information on the Apache Software Foundation, please see
                     52:  * <http://www.apache.org/>.
                     53:  */
                     54: 
                     55: /*
                     56:  * sdbm - ndbm work-alike hashed database library
                     57:  * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978).
                     58:  * author: oz@nexus.yorku.ca
                     59:  * ex-public domain, ported to APR for Apache 2
                     60:  * core routines
                     61:  */
                     62: 
1.5       moko       63: #include "pa_apr.h"
                     64: #include "pa_file_io.h"
                     65: #include "pa_strings.h"
                     66: #include "pa_errno.h"
                     67: #include "pa_sdbm.h"
1.1       paf        68: 
                     69: #include "sdbm_tune.h"
                     70: #include "sdbm_pair.h"
                     71: #include "sdbm_private.h"
                     72: 
                     73: /*
                     74:  * forward
                     75:  */
1.5       moko       76: static int getdbit (pa_sdbm_t *, long);
                     77: static pa_status_t setdbit(pa_sdbm_t *, long);
                     78: static pa_status_t getpage(pa_sdbm_t *db, long);
                     79: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db);
                     80: static pa_status_t makroom(pa_sdbm_t *, long, int);
1.1       paf        81: 
                     82: /*
                     83:  * useful macros
                     84:  */
                     85: #define bad(x)         ((x).dptr == NULL || (x).dsize <= 0)
                     86: #define exhash(item)   sdbm_hash((item).dptr, (item).dsize)
                     87: 
                     88: /* ### Does anything need these externally? */
                     89: #define sdbm_dirfno(db)        ((db)->dirf)
                     90: #define sdbm_pagfno(db)        ((db)->pagf)
                     91: 
1.5       moko       92: #define OFF_PAG(off)   (pa_off_t) (off) * PBLKSIZ
                     93: #define OFF_DIR(off)   (pa_off_t) (off) * DBLKSIZ
1.1       paf        94: 
                     95: static long masks[] = {
                     96:         000000000000, 000000000001, 000000000003, 000000000007,
                     97:         000000000017, 000000000037, 000000000077, 000000000177,
                     98:         000000000377, 000000000777, 000000001777, 000000003777,
                     99:         000000007777, 000000017777, 000000037777, 000000077777,
                    100:         000000177777, 000000377777, 000000777777, 000001777777,
                    101:         000003777777, 000007777777, 000017777777, 000037777777,
                    102:         000077777777, 000177777777, 000377777777, 000777777777,
                    103:         001777777777, 003777777777, 007777777777, 017777777777
                    104: };
                    105: 
1.5       moko      106: const pa_sdbm_datum_t sdbm_nullitem = { NULL, 0 };
1.1       paf       107: 
1.5       moko      108: static pa_status_t database_cleanup(void *data)
1.1       paf       109: {
1.5       moko      110:     pa_sdbm_t *db = data;
1.1       paf       111: 
                    112:     /*
1.5       moko      113:      * Can't rely on pa_sdbm_unlock, since it will merely
1.1       paf       114:      * decrement the refcnt if several locks are held.
                    115:      */
                    116:     if (db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK))
1.5       moko      117:         (void) pa_file_unlock(db->dirf);
                    118:     (void) pa_file_close(db->dirf);
                    119:     (void) pa_file_close(db->pagf);
1.4       misha     120: //    free(db); // libcg will do it
1.1       paf       121: 
1.5       moko      122:     return PA_SUCCESS;
1.1       paf       123: }
                    124: 
1.5       moko      125: static pa_status_t prep(pa_sdbm_t **pdb, const char *dirname, const char *pagname,
                    126:                          pa_int32_t flags, pa_fileperms_t perms, pa_pool_t *p)
1.1       paf       127: {
1.5       moko      128:     pa_sdbm_t *db;
                    129:     pa_status_t status;
1.1       paf       130: 
                    131:     *pdb = NULL;
                    132: 
1.4       misha     133: //    db = malloc(sizeof(*db));
                    134: //    memset(db, 0, sizeof(*db));
1.6       moko      135:     db = pa_sdbm_malloc(sizeof(*db));
1.1       paf       136: 
                    137:     db->pool = p;
                    138: 
                    139:     /*
                    140:      * adjust user flags so that WRONLY becomes RDWR, 
                    141:      * as required by this package. Also set our internal
                    142:      * flag for RDONLY if needed.
                    143:      */
1.5       moko      144:     if (!(flags & PA_WRITE)) {
1.1       paf       145:         db->flags |= SDBM_RDONLY;
                    146:     }
                    147: 
                    148:     /*
                    149:      * adjust the file open flags so that we handle locking
                    150:      * on our own (don't rely on any locking behavior within
1.5       moko      151:      * an pa_file_t, in case it's ever introduced, and set
1.1       paf       152:      * our own flag.
                    153:      */
1.5       moko      154:     if (flags & PA_SHARELOCK) {
1.1       paf       155:         db->flags |= SDBM_SHARED;
1.5       moko      156:         flags &= ~PA_SHARELOCK;
1.1       paf       157:     }
                    158: 
1.5       moko      159:     flags |= PA_BINARY | PA_READ;
1.1       paf       160: 
                    161:     /*
                    162:      * open the files in sequence, and stat the dirfile.
                    163:      * If we fail anywhere, undo everything, return NULL.
                    164:      */
                    165: 
1.5       moko      166:     if ((status = pa_file_open(&db->dirf, dirname, flags, perms, p))
                    167:                 != PA_SUCCESS)
1.1       paf       168:         goto error;
                    169: 
1.5       moko      170:     if ((status = pa_file_open(&db->pagf, pagname, flags, perms, p))
                    171:                 != PA_SUCCESS)
1.1       paf       172:         goto error;
                    173: 
1.5       moko      174:     if ((status = pa_sdbm_lock(db, (db->flags & SDBM_RDONLY) 
                    175:                                         ? PA_FLOCK_SHARED
                    176:                                         : PA_FLOCK_EXCLUSIVE))
                    177:                 != PA_SUCCESS)
1.1       paf       178:         goto error;
                    179: 
1.5       moko      180:     /* pa_pcalloc zeroed the buffers
                    181:      * pa_sdbm_lock stated the dirf->size and invalidated the cache
1.1       paf       182:      */
                    183: 
                    184:     /*
                    185:      * if we are opened in SHARED mode, unlock ourself 
                    186:      */
                    187:     if (db->flags & SDBM_SHARED)
1.5       moko      188:         if ((status = pa_sdbm_unlock(db)) != PA_SUCCESS)
1.1       paf       189:             goto error;
                    190: 
                    191:     /* make sure that we close the database at some point */
1.5       moko      192:     //pa_pool_cleanup_register(p, db, database_cleanup, pa_pool_cleanup_null);
1.1       paf       193: 
                    194:     /* Done! */
                    195:     *pdb = db;
1.5       moko      196:     return PA_SUCCESS;
1.1       paf       197: 
                    198: error:
                    199:     if (db->dirf && db->pagf)
1.5       moko      200:         (void) pa_sdbm_unlock(db);
1.1       paf       201:     if (db->dirf != NULL)
1.5       moko      202:         (void) pa_file_close(db->dirf);
1.1       paf       203:     if (db->pagf != NULL) {
1.5       moko      204:         (void) pa_file_close(db->pagf);
1.1       paf       205:     }
1.4       misha     206: //    free(db); // libcg will do it
1.1       paf       207:     return status;
                    208: }
                    209: 
1.5       moko      210: pa_status_t pa_sdbm_open(pa_sdbm_t **db, const char *file, 
                    211:                                         pa_int32_t flags, 
                    212:                                         pa_fileperms_t perms, pa_pool_t *p)
1.1       paf       213: {
1.5       moko      214:     char *dirname = pa_pstrcat(p, file, PA_SDBM_DIRFEXT, NULL);
                    215:     char *pagname = pa_pstrcat(p, file, PA_SDBM_PAGFEXT, NULL);
1.1       paf       216:     
                    217:     return prep(db, dirname, pagname, flags, perms, p);
                    218: }
                    219: 
1.5       moko      220: pa_status_t pa_sdbm_close(pa_sdbm_t *db)
1.1       paf       221: {
1.5       moko      222:        database_cleanup(db); return PA_SUCCESS;
                    223:     //return pa_pool_cleanup_run(db->pool, db, database_cleanup);
1.1       paf       224: }
                    225: 
1.5       moko      226: pa_status_t pa_sdbm_fetch(pa_sdbm_t *db, pa_sdbm_datum_t *val,
                    227:                                          pa_sdbm_datum_t key)
1.1       paf       228: {
1.5       moko      229:     pa_status_t status;
1.1       paf       230:     
                    231:     if (db == NULL || bad(key))
1.5       moko      232:         return PA_EINVAL;
1.1       paf       233: 
1.5       moko      234:     if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1       paf       235:         return status;
                    236: 
1.5       moko      237:     if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1       paf       238:         *val = getpair(db->pagbuf, key);
                    239:         /* ### do we want a not-found result? */
                    240:     }
                    241: 
1.5       moko      242:     (void) pa_sdbm_unlock(db);
1.1       paf       243: 
                    244:     return status;
                    245: }
                    246: 
1.5       moko      247: static pa_status_t write_page(pa_sdbm_t *db, const char *buf, long pagno)
1.1       paf       248: {
1.5       moko      249:     pa_status_t status;
                    250:     pa_off_t off = OFF_PAG(pagno);
1.1       paf       251:     
1.5       moko      252:     if ((status = pa_file_seek(db->pagf, PA_SET, &off)) == PA_SUCCESS)
                    253:         status = pa_file_write_full(db->pagf, buf, PBLKSIZ, NULL);
1.1       paf       254: 
                    255:     return status;
                    256: }
                    257: 
1.5       moko      258: pa_status_t pa_sdbm_delete(pa_sdbm_t *db, 
                    259:                                           const pa_sdbm_datum_t key)
1.1       paf       260: {
1.5       moko      261:     pa_status_t status;
1.1       paf       262:     
                    263:     if (db == NULL || bad(key))
1.5       moko      264:         return PA_EINVAL;
                    265:     if (pa_sdbm_rdonly(db))
                    266:         return PA_EINVAL;
1.1       paf       267:     
1.5       moko      268:     if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1       paf       269:         return status;
                    270: 
1.5       moko      271:     if ((status = getpage(db, exhash(key))) == PA_SUCCESS) {
1.1       paf       272:         if (!delpair(db->pagbuf, key))
                    273:             /* ### should we define some APRUTIL codes? */
1.5       moko      274:                        status = PA_SUCCESS; /* PAF: were PA_EGENERAL, contradicting comment in .h file :( */
1.1       paf       275:         else
                    276:             status = write_page(db, db->pagbuf, db->pagbno);
                    277:     }
                    278: 
1.5       moko      279:     (void) pa_sdbm_unlock(db);
1.1       paf       280: 
                    281:     return status;
                    282: }
                    283: 
1.5       moko      284: pa_status_t pa_sdbm_store(pa_sdbm_t *db, pa_sdbm_datum_t key,
                    285:                                          pa_sdbm_datum_t val, int flags)
1.1       paf       286: {
                    287:     int need;
                    288:     register long hash;
1.5       moko      289:     pa_status_t status;
1.1       paf       290:     
                    291:     if (db == NULL || bad(key))
1.5       moko      292:         return PA_EINVAL;
                    293:     if (pa_sdbm_rdonly(db))
                    294:         return PA_EINVAL;
1.1       paf       295:     need = key.dsize + val.dsize;
                    296:     /*
                    297:      * is the pair too big (or too small) for this database ??
                    298:      */
                    299:     if (need < 0 || need > PAIRMAX)
1.5       moko      300:         return PA_EINVAL;
1.1       paf       301: 
1.5       moko      302:     if ((status = pa_sdbm_lock(db, PA_FLOCK_EXCLUSIVE)) != PA_SUCCESS)
1.1       paf       303:         return status;
                    304: 
1.5       moko      305:     if ((status = getpage(db, (hash = exhash(key)))) == PA_SUCCESS) {
1.1       paf       306: 
                    307:         /*
                    308:          * if we need to replace, delete the key/data pair
                    309:          * first. If it is not there, ignore.
                    310:          */
1.5       moko      311:         if (flags == PA_SDBM_REPLACE)
1.1       paf       312:             (void) delpair(db->pagbuf, key);
1.5       moko      313:         else if (!(flags & PA_SDBM_INSERTDUP) && duppair(db->pagbuf, key)) {
                    314:             status = PA_EEXIST;
1.1       paf       315:             goto error;
                    316:         }
                    317:         /*
                    318:          * if we do not have enough room, we have to split.
                    319:          */
                    320:         if (!fitpair(db->pagbuf, need))
1.5       moko      321:             if ((status = makroom(db, hash, need)) != PA_SUCCESS)
1.1       paf       322:                 goto error;
                    323:         /*
                    324:          * we have enough room or split is successful. insert the key,
                    325:          * and update the page file.
                    326:          */
                    327:         (void) putpair(db->pagbuf, key, val);
                    328: 
                    329:         status = write_page(db, db->pagbuf, db->pagbno);
                    330:     }
                    331: 
                    332: error:
1.5       moko      333:     (void) pa_sdbm_unlock(db);    
1.1       paf       334: 
                    335:     return status;
                    336: }
                    337: 
                    338: /*
                    339:  * makroom - make room by splitting the overfull page
                    340:  * this routine will attempt to make room for SPLTMAX times before
                    341:  * giving up.
                    342:  */
1.5       moko      343: static pa_status_t makroom(pa_sdbm_t *db, long hash, int need)
1.1       paf       344: {
                    345:     long newp;
                    346:     char twin[PBLKSIZ];
                    347:     char *pag = db->pagbuf;
                    348:     char *new = twin;
                    349:     register int smax = SPLTMAX;
1.5       moko      350:     pa_status_t status;
1.1       paf       351: 
                    352:     do {
                    353:         /*
                    354:          * split the current page
                    355:          */
                    356:         (void) splpage(pag, new, db->hmask + 1);
                    357:         /*
                    358:          * address of the new page
                    359:          */
                    360:         newp = (hash & db->hmask) | (db->hmask + 1);
                    361: 
                    362:         /*
                    363:          * write delay, read avoidence/cache shuffle:
                    364:          * select the page for incoming pair: if key is to go to the new page,
                    365:          * write out the previous one, and copy the new one over, thus making
                    366:          * it the current page. If not, simply write the new page, and we are
                    367:          * still looking at the page of interest. current page is not updated
                    368:          * here, as sdbm_store will do so, after it inserts the incoming pair.
                    369:          */
                    370:         if (hash & (db->hmask + 1)) {
                    371:             if ((status = write_page(db, db->pagbuf, db->pagbno)) 
1.5       moko      372:                         != PA_SUCCESS)
1.1       paf       373:                 return status;
                    374:                     
                    375:             db->pagbno = newp;
                    376:             (void) memcpy(pag, new, PBLKSIZ);
                    377:         }
                    378:         else {
1.5       moko      379:             if ((status = write_page(db, new, newp)) != PA_SUCCESS)
1.1       paf       380:                 return status;
                    381:         }
                    382: 
1.5       moko      383:         if ((status = setdbit(db, db->curbit)) != PA_SUCCESS)
1.1       paf       384:             return status;
                    385:         /*
                    386:          * see if we have enough room now
                    387:          */
                    388:         if (fitpair(pag, need))
1.5       moko      389:             return PA_SUCCESS;
1.1       paf       390:         /*
                    391:          * try again... update curbit and hmask as getpage would have
                    392:          * done. because of our update of the current page, we do not
                    393:          * need to read in anything. BUT we have to write the current
                    394:          * [deferred] page out, as the window of failure is too great.
                    395:          */
                    396:         db->curbit = 2 * db->curbit
                    397:                    + ((hash & (db->hmask + 1)) ? 2 : 1);
                    398:         db->hmask |= db->hmask + 1;
                    399:             
                    400:         if ((status = write_page(db, db->pagbuf, db->pagbno))
1.5       moko      401:                     != PA_SUCCESS)
1.1       paf       402:             return status;
                    403:             
                    404:     } while (--smax);
                    405: 
                    406:     /*
                    407:      * if we are here, this is real bad news. After SPLTMAX splits,
                    408:      * we still cannot fit the key. say goodnight.
                    409:      */
                    410: #if 0
                    411:     (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44);
                    412: #endif
                    413:     /* ### ENOSPC not really appropriate but better than nothing */
1.5       moko      414:     return PA_ENOSPC;
1.1       paf       415: 
                    416: }
                    417: 
                    418: /* Reads 'len' bytes from file 'f' at offset 'off' into buf.
                    419:  * 'off' is given relative to the start of the file.
                    420:  * If EOF is returned while reading, this is taken as success.
                    421:  */
1.5       moko      422: static pa_status_t read_from(pa_file_t *f, void *buf, 
                    423:              pa_off_t off, pa_size_t len)
1.1       paf       424: {
1.5       moko      425:     pa_status_t status;
1.1       paf       426: 
1.5       moko      427:     if ((status = pa_file_seek(f, PA_SET, &off)) != PA_SUCCESS ||
                    428:         ((status = pa_file_read_full(f, buf, len, NULL)) != PA_SUCCESS)) {
1.1       paf       429:         /* if EOF is reached, pretend we read all zero's */
1.5       moko      430:         if (status == PA_EOF) {
1.1       paf       431:             memset(buf, 0, len);
1.5       moko      432:             status = PA_SUCCESS;
1.1       paf       433:         }
                    434:     }
                    435: 
                    436:     return status;
                    437: }
                    438: 
                    439: /*
                    440:  * the following two routines will break if
                    441:  * deletions aren't taken into account. (ndbm bug)
                    442:  */
1.5       moko      443: pa_status_t pa_sdbm_firstkey(pa_sdbm_t *db, 
                    444:                                             pa_sdbm_datum_t *key)
1.1       paf       445: {
1.5       moko      446:     pa_status_t status;
1.1       paf       447:     
1.5       moko      448:     if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1       paf       449:         return status;
                    450: 
                    451:     /*
                    452:      * start at page 0
                    453:      */
                    454:     if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ))
1.5       moko      455:                 == PA_SUCCESS) {
1.1       paf       456:         db->pagbno = 0;
                    457:         db->blkptr = 0;
                    458:         db->keyptr = 0;
                    459:         status = getnext(key, db);
                    460:     }
                    461: 
1.5       moko      462:     (void) pa_sdbm_unlock(db);
1.1       paf       463: 
                    464:     return status;
                    465: }
                    466: 
1.5       moko      467: pa_status_t pa_sdbm_nextkey(pa_sdbm_t *db, 
                    468:                                            pa_sdbm_datum_t *key)
1.1       paf       469: {
1.5       moko      470:     pa_status_t status;
1.1       paf       471:     
1.5       moko      472:     if ((status = pa_sdbm_lock(db, PA_FLOCK_SHARED)) != PA_SUCCESS)
1.1       paf       473:         return status;
                    474: 
                    475:     status = getnext(key, db);
                    476: 
1.5       moko      477:     (void) pa_sdbm_unlock(db);
1.1       paf       478: 
                    479:     return status;
                    480: }
                    481: 
                    482: /*
                    483:  * all important binary tree traversal
                    484:  */
1.5       moko      485: static pa_status_t getpage(pa_sdbm_t *db, long hash)
1.1       paf       486: {
                    487:     register int hbit;
                    488:     register long dbit;
                    489:     register long pagb;
1.5       moko      490:     pa_status_t status;
1.1       paf       491: 
                    492:     dbit = 0;
                    493:     hbit = 0;
                    494:     while (dbit < db->maxbno && getdbit(db, dbit))
                    495:     dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1);
                    496: 
                    497:     debug(("dbit: %d...", dbit));
                    498: 
                    499:     db->curbit = dbit;
                    500:     db->hmask = masks[hbit];
                    501: 
                    502:     pagb = hash & db->hmask;
                    503:     /*
                    504:      * see if the block we need is already in memory.
                    505:      * note: this lookaside cache has about 10% hit rate.
                    506:      */
                    507:     if (pagb != db->pagbno) { 
                    508:         /*
                    509:          * note: here, we assume a "hole" is read as 0s.
                    510:          * if not, must zero pagbuf first.
                    511:          * ### joe: this assumption was surely never correct? but
                    512:          * ### we make it so in read_from anyway.
                    513:          */
                    514:         if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(pagb), PBLKSIZ)) 
1.5       moko      515:                     != PA_SUCCESS)
1.1       paf       516:             return status;
                    517: 
                    518:         if (!chkpage(db->pagbuf))
1.5       moko      519:             return PA_ENOSPC; /* ### better error? */
1.1       paf       520:         db->pagbno = pagb;
                    521: 
                    522:         debug(("pag read: %d\n", pagb));
                    523:     }
1.5       moko      524:     return PA_SUCCESS;
1.1       paf       525: }
                    526: 
1.5       moko      527: static int getdbit(pa_sdbm_t *db, long dbit)
1.1       paf       528: {
                    529:     register long c;
                    530:     register long dirb;
                    531: 
                    532:     c = dbit / BYTESIZ;
                    533:     dirb = c / DBLKSIZ;
                    534: 
                    535:     if (dirb != db->dirbno) {
                    536:         if (read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ)
1.5       moko      537:                     != PA_SUCCESS)
1.1       paf       538:             return 0;
                    539: 
                    540:         db->dirbno = dirb;
                    541: 
                    542:         debug(("dir read: %d\n", dirb));
                    543:     }
                    544: 
                    545:     return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ);
                    546: }
                    547: 
1.5       moko      548: static pa_status_t setdbit(pa_sdbm_t *db, long dbit)
1.1       paf       549: {
                    550:     register long c;
                    551:     register long dirb;
1.5       moko      552:     pa_status_t status;
                    553:     pa_off_t off;
1.1       paf       554: 
                    555:     c = dbit / BYTESIZ;
                    556:     dirb = c / DBLKSIZ;
                    557: 
                    558:     if (dirb != db->dirbno) {
                    559:         if ((status = read_from(db->dirf, db->dirbuf, OFF_DIR(dirb), DBLKSIZ))
1.5       moko      560:                     != PA_SUCCESS)
1.1       paf       561:             return status;
                    562: 
                    563:         db->dirbno = dirb;
                    564:         
                    565:         debug(("dir read: %d\n", dirb));
                    566:     }
                    567: 
                    568:     db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ);
                    569: 
                    570:     if (dbit >= db->maxbno)
                    571:         db->maxbno += DBLKSIZ * BYTESIZ;
                    572: 
                    573:     off = OFF_DIR(dirb);
1.5       moko      574:     if ((status = pa_file_seek(db->dirf, PA_SET, &off)) == PA_SUCCESS)
                    575:         status = pa_file_write_full(db->dirf, db->dirbuf, DBLKSIZ, NULL);
1.1       paf       576: 
                    577:     return status;
                    578: }
                    579: 
                    580: /*
                    581: * getnext - get the next key in the page, and if done with
                    582: * the page, try the next page in sequence
                    583: */
1.5       moko      584: static pa_status_t getnext(pa_sdbm_datum_t *key, pa_sdbm_t *db)
1.1       paf       585: {
1.5       moko      586:     pa_status_t status;
1.1       paf       587:     for (;;) {
                    588:         db->keyptr++;
                    589:         *key = getnkey(db->pagbuf, db->keyptr);
                    590:         if (key->dptr != NULL)
1.5       moko      591:             return PA_SUCCESS;
1.1       paf       592:         /*
                    593:          * we either run out, or there is nothing on this page..
                    594:          * try the next one... If we lost our position on the
                    595:          * file, we will have to seek.
                    596:          */
                    597:         db->keyptr = 0;
                    598:         if (db->pagbno != db->blkptr++) {
1.5       moko      599:             pa_off_t off = OFF_PAG(db->blkptr);
                    600:             if ((status = pa_file_seek(db->pagf, PA_SET, &off) 
                    601:                         != PA_SUCCESS))
1.1       paf       602:                 return status;
                    603:         }
                    604: 
                    605:         db->pagbno = db->blkptr;
                    606:         /* ### EOF acceptable here too? */
1.5       moko      607:         if ((status = pa_file_read_full(db->pagf, db->pagbuf, PBLKSIZ, NULL))
                    608:                     != PA_SUCCESS)
1.1       paf       609:             return status;
                    610:         if (!chkpage(db->pagbuf))
1.5       moko      611:             return PA_EGENERAL;     /* ### need better error */
1.1       paf       612:     }
                    613: 
                    614:     /* NOTREACHED */
                    615: }
                    616: 
                    617: 
1.5       moko      618: int pa_sdbm_rdonly(pa_sdbm_t *db)
1.1       paf       619: {
                    620:     /* ### Should we return true if the first lock is a share lock,
1.5       moko      621:      *     to reflect that pa_sdbm_store and pa_sdbm_delete will fail?
1.1       paf       622:      */
                    623:     return (db->flags & SDBM_RDONLY) != 0;
                    624: }
                    625: 

E-mail: