datum getnkey(DBM *db, char *pag, int num) { datum key; int i; int off; const unsigned short *ino = (const unsigned short *) pag; g_assert(num > 0); i = num * 2 - 1; if (ino[0] == 0 || i > ino[0]) return nullitem; off = (i > 1) ? offset(ino[i - 1]) : DBM_PBLKSIZ; key.dptr = pag + offset(ino[i]); key.dsize = off - offset(ino[i]); #ifdef BIGDATA if (is_big(ino[i])) { size_t dsize = big_length(key.dptr); key.dptr = bigkey_get(db, key.dptr, key.dsize); key.dsize = (NULL == key.dptr) ? 0 : dsize; } #else (void) db; #endif return key; }
datum getpair(DBM *db, char *pag, datum key) { int i; unsigned n; datum val; const unsigned short *ino = (const unsigned short *) pag; if ((n = ino[0]) == 0) return nullitem; if ((i = seepair(db, pag, n, key.dptr, key.dsize)) == 0) return nullitem; val.dptr = pag + offset(ino[i + 1]); val.dsize = offset(ino[i]) - offset(ino[i + 1]); #ifdef BIGDATA if (is_big(ino[i + 1])) { size_t dsize = big_length(val.dptr); val.dptr = bigval_get(db, val.dptr, val.dsize); val.dsize = (NULL == val.dptr) ? 0 : dsize; } #endif return val; }
/** * Get value for the num-th key in the page. */ datum getnval(DBM *db, char *pag, int num) { int i; int n; datum val; const unsigned short *ino = (const unsigned short *) pag; g_assert(num > 0); i = num * 2 - 1; if ((n = ino[0]) == 0 || i >= n) return nullitem; val.dptr = pag + offset(ino[i + 1]); val.dsize = offset(ino[i]) - offset(ino[i + 1]); #ifdef BIGDATA if (is_big(ino[i + 1])) { size_t dsize = big_length(val.dptr); val.dptr = bigval_get(db, val.dptr, val.dsize); val.dsize = (NULL == val.dptr) ? 0 : dsize; } #else (void) db; #endif return val; }
/** * Get information about a key: length of its value and index within the page. * * @return TRUE if key was found, value length via *length, index via *idx, * and whether value is stored in a .dat file via *big. */ bool infopair(DBM *db, char *pag, datum key, size_t *length, int *idx, bool *big) { int i; unsigned n; size_t dsize; const unsigned short *ino = (const unsigned short *) pag; if ((n = ino[0]) == 0) return FALSE; if ((i = seepair(db, pag, n, key.dptr, key.dsize)) == 0) return FALSE; dsize = offset(ino[i]) - offset(ino[i + 1]); #ifdef BIGDATA if (is_big(ino[i + 1])) { g_assert(dsize >= sizeof(uint32)); dsize = big_length(pag + offset(ino[i + 1])); } #endif if (length != NULL) *length = dsize; if (idx != NULL) *idx = i; if (big != NULL) *big = is_big(ino[i + 1]); return TRUE; /* Key exists */ }
/** * 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; }
/** * Replace value data in-place. * * @param db the sdbm database * @param bval start of big value in the page * @param data the new value * @param len length of data * * @return 0 if OK, -1 on error with errno set. */ int big_replace(DBM *db, char *bval, const char *data, size_t len) { size_t old_len = big_length(bval); g_assert(size_is_non_negative(len)); g_assert(bigblocks(old_len) == bigblocks(len)); g_assert(len <= MAX_INT_VAL(guint32)); /* * Write data on the same blocks as before, since we know it will fit. */ poke_be32(bval, (guint32) len); /* First 4 bytes: real data length */ return big_store(db, bigval_blocks(bval), data, len); }
/** * 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; }
/** * Is a big key stored at bkey in an SDBM .pag file equal to a siz-byte key? * * @param db the sdbm database * @param bkey start of big key in the page * @param blen length of big key in the page * @param key the key we're trying to match against * @param siz length of the key * * @return TRUE if the key matches. */ gboolean bigkey_eq(DBM *db, const char *bkey, size_t blen, const char *key, size_t siz) { size_t len = big_length(bkey); DBMBIG *dbg = db->big; g_assert(bigkey_length(len) == blen); /* * Comparing a key in memory with a big key on disk is potentially a * costly operation because it requires some I/O to fetch the key from * the .dat file, which may involve several system calls, simply to find * out that the key is not matching. * * To avoid useless reads as much as possible, we store the length of * the big key at the beginning of the .pag key indirection data. If the * size does not match, there's no need to go further. * * Then we keep the first and last BIG_KEYSAVED bytes of the key as part * of the .pag data so that we may quickly filter out keys that are * obviously not matching. * * Only when things look like it could be a match do we engage in the * process of fetching the big key data to perform the actual comparison. * * Nonetheless this means fetching data indexed by large keys requires * extra I/Os and therefore large keys should be avoided if possible. * In practice, keys are shorthand to the actual data and therefore are * likely to be kept short enough so that they are always expanded in the * .pag data and never stored as big keys. */ if (siz != len) return FALSE; dbg->key_matching++; if (0 != memcmp(key, bigkey_head(bkey), BIG_KEYSAVED)) return FALSE; if (0 != memcmp(key + (siz-BIG_KEYSAVED), bigkey_tail(bkey), BIG_KEYSAVED)) return FALSE; dbg->key_short_match++; /* * Need to read the key to make sure it's an exact match. * * There is a high probability as the head and the tail already match, * and the length is the same. */ if (-1 == big_fetch(db, bigkey_blocks(bkey), siz)) return FALSE; /* * Data stored in the .dat file must match what the .pag had for the key */ if ( 0 != memcmp(db->big->scratch, bigkey_head(bkey), BIG_KEYSAVED) || 0 != memcmp(db->big->scratch + (siz-BIG_KEYSAVED), bigkey_tail(bkey), BIG_KEYSAVED) ) { g_warning("sdbm: \"%s\": found %lu-byte key page/data inconsistency", sdbm_name(db), (unsigned long) siz); return FALSE; } if (0 == memcmp(db->big->scratch, key, siz)) { dbg->key_full_match++; return TRUE; } return FALSE; }