/** * Is value data of a given old size replaceable in situ with new data? */ bool replaceable(size_t old_size, size_t new_size, bool big) { #ifdef BIGDATA size_t ol = big ? bigval_length(old_size) : old_size; size_t nl = big ? bigval_length(new_size) : new_size; return ol == nl; #else /* !BIGDATA */ (void) big; return old_size == new_size; #endif /* BIGDATA */ }
/** * Put large value data into the .dat and fill in the supplied .pag buffer * with the block numbers where the value is stored and other size information. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page * @param val start of value data * @param vlen length of value * * @return TRUE if value was written successfully in the .dat file. */ gboolean bigval_put(DBM *db, char *bval, size_t blen, const char *val, size_t vlen) { g_assert(bigval_length(vlen) == blen); if (!big_file_alloc(db, bigval_blocks(bval), bigblocks(vlen))) return FALSE; /* * Write the value header: * * value size */ poke_be32(bval, (guint32) vlen); /* * And now the indirection block numbers of the value, pointing in .dat. */ if (0 != big_store(db, bigval_blocks(bval), val, vlen)) { big_file_free(db, bigval_blocks(bval), bigblocks(vlen)); return FALSE; } return TRUE; }
/** * Mark .dat blocks used to hold the value described in the .pag space as * being allocated in the bitmap checking array. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page */ void bigval_mark_used(DBM *db, const char *bval, size_t blen) { size_t len = big_length(bval); if (bigval_length(len) != blen) { g_carp("sdbm: \"%s\": %s: inconsistent value length %lu in .pag", sdbm_name(db), G_STRFUNC, (unsigned long) len); return; } big_file_mark_used(db, bigval_blocks(bval), bigblocks(len)); }
/** * Validate .dat blocks used to hold the value described in the .pag space. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page * * @return TRUE on success. */ gboolean bigval_check(DBM *db, const char *bval, size_t blen) { size_t len = big_length(bval); if (bigval_length(len) != blen) { g_warning("sdbm: \"%s\": found inconsistent value length %lu, " "would span %lu bytes in .pag instead of the %lu present", sdbm_name(db), (unsigned long) len, (unsigned long) bigkey_length(len), (unsigned long) blen); return FALSE; } return big_file_check("value", db, bigval_blocks(bval), bigblocks(len)); }
/** * Free .dat blocks used to hold the value described in the .pag space. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page * * @return TRUE on success. */ gboolean bigval_free(DBM *db, const char *bval, size_t blen) { size_t len = big_length(bval); if (bigval_length(len) != blen) { g_warning("sdbm: \"%s\": " "bigval_free: inconsistent key length %lu in .pag", sdbm_name(db), (unsigned long) len); return FALSE; } big_file_free(db, bigval_blocks(bval), bigblocks(len)); return TRUE; }
/** * Get value data from the block numbers held in the .pag value. * * @param db the sdbm database * @param bval start of big value in the page * @param blen length of big value in the page * * @return pointer to read value (scratch buffer) if OK, NULL on error. */ char * bigval_get(DBM *db, const char *bval, size_t blen) { size_t len = big_length(bval); DBMBIG *dbg = db->big; if (bigval_length(len) != blen) { g_warning("sdbm: \"%s\": " "bigval_get: inconsistent value length %lu in .pag", sdbm_name(db), (unsigned long) len); return NULL; } if (-1 == big_fetch(db, bigval_blocks(bval), len)) return NULL; return dbg->scratch; }
bool putpair(DBM *db, char *pag, datum key, datum val) { #ifdef BIGDATA /* * Our strategy for using big values is the following: if the key+value * won't fit in expanded form in the page, there's no question we have * to use a big value and/or big key. * * If it would fit however but the size of key+value is >= DBM_PAIRMAX/2 * and the value will waste less than half the .dat page then we force a * big value to be used. The rationale is to avoid filling-up the page * and ending up having to split it later on for the next hashing conflict. * * NOTE: any change to the logic below must also be reported to * sdbm_storage_needs(). */ if ( key.dsize <= DBM_PAIRMAX && DBM_PAIRMAX - key.dsize >= val.dsize && ( key.dsize + val.dsize < DBM_PAIRMAX / 2 || val.dsize < DBM_BBLKSIZ / 2 ) ) { /* Expand both the key and the value in the page */ putpair_ext(pag, key, FALSE, val, FALSE); } else { unsigned n; unsigned off; unsigned short *ino = (unsigned short *) pag; size_t vl; bool largeval; off = ((n = ino[0]) > 0) ? offset(ino[n]) : DBM_PBLKSIZ; /* * Avoid large keys if possible since comparisons involve extra I/Os. * Therefore try to see if we can get away with only storing the * value as a large item. */ vl = bigval_length(val.dsize); /* * Handle the key first. */ if (key.dsize > DBM_PAIRMAX || DBM_PAIRMAX - key.dsize < vl) { size_t kl = bigkey_length(key.dsize); /* Large key (and could use a large value as well) */ off -= kl; if (!bigkey_put(db, pag + off, kl, key.dptr, key.dsize)) return FALSE; ino[n + 1] = off | BIG_FLAG; largeval = val.dsize > DBM_PAIRMAX / 2 || val.dsize > DBM_PAIRMAX - bigkey_length(key.dsize); } else { /* Regular inlined key, only the value will be held in .dat */ off -= key.dsize; memcpy(pag + off, key.dptr, key.dsize); ino[n + 1] = off; largeval = TRUE; } /* * Now the data. */ if (largeval) { off -= vl; if (!bigval_put(db, pag + off, vl, val.dptr, val.dsize)) return FALSE; ino[n + 2] = off | BIG_FLAG; } else { off -= val.dsize; memcpy(pag + off, val.dptr, val.dsize); ino[n + 2] = off; } ino[0] += 2; /* Stored 2 items: 1 key, 1 value */ } #else (void) db; putpair_ext(pag, key, FALSE, val, FALSE); #endif /* BIGDATA */ return TRUE; }