/** * Read n-th bitmap page. * * @return TRUE on success. */ static gboolean fetch_bitbuf(DBM *db, long num) { DBMBIG *dbg = db->big; long bno = num * BIG_BITCOUNT; /* address of n-th bitmap in file */ dbg->bitfetch++; if (bno != dbg->bitbno) { ssize_t got; if (dbg->bitbuf_dirty && !flush_bitbuf(db)) return FALSE; dbg->bitread++; got = compat_pread(dbg->fd, dbg->bitbuf, BIG_BLKSIZE, OFF_DAT(bno)); if (got < 0) { g_warning("sdbm: \"%s\": could not read bitmap block #%ld: %s", sdbm_name(db), num, g_strerror(errno)); ioerr(db, FALSE); return FALSE; } if (0 == got) { memset(dbg->bitbuf, 0, BIG_BLKSIZE); } dbg->bitbno = bno; dbg->bitbuf_dirty = FALSE; } else { dbg->bitbno_hit++; } return TRUE; }
/** * Synchronize the .dat file, if opened. * * @return TRUE if OK, FALSE on error */ gboolean big_sync(DBM *db) { DBMBIG *dbg = db->big; if (-1 == dbg->fd) return TRUE; if (dbg->bitbuf_dirty && !flush_bitbuf(db)) return FALSE; return TRUE; }
void putbits(unsigned int n, unsigned int val, stream_t *str) { unsigned int rest; if (n <= str->bitrest) { str->bitbuf |= ((val & mask[n]) << (str->bitrest-n)); str->bitrest -= n; } else { rest = n-str->bitrest; str->bitbuf |= (val >> rest) & mask[n-rest]; flush_bitbuf(str); str->bitbuf |= (val & mask[rest]) << (32-rest); str->bitrest -= rest; } }
/** * End bitmap allocation checks that have been started by the usage of one * of the bigkey_mark_used() and bigval_mark_used() routines. * * @return the amount of corrections brought to the bitmap, 0 meaning * everything was consistent. */ size_t big_check_end(DBM *db) { DBMBIG *dbg = db->big; long i; size_t adjustments = 0; if (NULL == dbg->bitcheck) return 0; for (i = 0; i < dbg->bitmaps; i++) { if (!fetch_bitbuf(db, i)) { adjustments += BIG_BITCOUNT; /* Say, everything was wrong */ } else { guint8 *p = ptr_add_offset(dbg->bitcheck, i * BIG_BLKSIZE); guint8 *q = dbg->bitbuf; size_t j; size_t old_adjustments = adjustments; for (j = 0; j < BIG_BLKSIZE; j++, p++, q++) { guint8 mismatch = *p ^ *q; if (mismatch) { adjustments += bits_set(mismatch); *q = *p; } } if (old_adjustments != adjustments) { size_t adj = adjustments - old_adjustments; flush_bitbuf(db); g_warning("sdbm: \"%s\": adjusted %lu bit%s in bitmap #%ld", sdbm_name(db), (unsigned long) adj, 1 == adj ? "" : "s", i); } } } HFREE_NULL(dbg->bitcheck); return adjustments; }
/** * Allocate blocks (consecutive if possible) from the .dat file. * Block numbers are written back in the specified vector, in sequence. * * Blocks are always allocated with increasing block numbers, i.e. the list * of block numbers returned is guaranteed to be sorted. This will help * upper layers to quickly determine whether all the blocks are contiguous * for instance. * * The file is extended as necessary to be able to allocate the blocks but * this is only done when there are no more free blocks available. * * @param db the sdbm database * @param bvec vector where allocated block numbers will be stored * @param bcnt amount of blocks in vector (amount to allocate) * * @return TRUE if we were able to allocate all the requested blocks. */ static gboolean big_file_alloc(DBM *db, void *bvec, int bcnt) { DBMBIG *dbg = db->big; size_t first; int n; void *q; int bmap = 0; /* Initial bitmap from which we allocate */ g_assert(bcnt > 0); g_return_val_if_fail(NULL == dbg->bitcheck, FALSE); if (-1 == dbg->fd && -1 == big_open(dbg)) return FALSE; /* * First try to allocate all the blocks sequentially. */ retry: first = big_falloc_seq(db, bmap, bcnt); if (first != 0) { while (bcnt-- > 0) { bvec = poke_be32(bvec, first++); } goto success; } /* * There are no "bcnt" consecutive free blocks in the file. * * Before extending the file, we're going to fill the holes as much * as possible. */ for (first = 0, q = bvec, n = bcnt; n > 0; n--) { first = big_falloc(db, first + 1); if (0 == first) break; q = poke_be32(q, first); } if (0 == n) goto success; /* Found the requested "bcnt" free blocks */ /* * Free the incompletely allocated blocks: since we're about to extend * the file, we'll use consecutive blocks from the new chunk governed * by the added empty bitmap. */ for (q = bvec, n = bcnt - n; n > 0; n--) { first = peek_be32(q); big_ffree(db, first); q = ptr_add_offset(q, sizeof(guint32)); } /* * Extend the file by allocating another bitmap. */ g_assert(0 == bmap); /* Never retried yet */ if (dbg->bitbuf_dirty && !flush_bitbuf(db)) return FALSE; memset(dbg->bitbuf, 0, BIG_BLKSIZE); bit_field_set(dbg->bitbuf, 0); /* First page is the bitmap itself */ dbg->bitbno = dbg->bitmaps * BIG_BITCOUNT; dbg->bitmaps++; /* * Now retry starting to allocate blocks from the newly added bitmap. * * This will likely succeed if we're trying to allocate less than 8 MiB * worth of data (with 1 KiB blocks). */ bmap = dbg->bitmaps - 1; goto retry; success: /* * We successfully allocated blocks from the bitmap. * * If the database is not volatile, we need to flush the bitmap to disk * immediately in case of a crash, to avoid reusing these parts of the file. */ if (!db->is_volatile && dbg->bitbuf_dirty && !flush_bitbuf(db)) { /* Cannot flush -> cannot allocate the blocks: free them */ for (q = bvec, n = bcnt; n > 0; n--) { first = peek_be32(q); big_ffree(db, first); q = ptr_add_offset(q, sizeof(guint32)); } return FALSE; } return TRUE; /* Succeeded */ }