/** * Read value from database file, returning a pointer to the allocated * deserialized data. These data can be modified freely and stored back, * but their lifetime will not exceed that of the next call to a dbmw * operation on the same descriptor. * * User code does not need to bother with freeing the allocated data, this * is managed directly by the DBM wrapper. * * @param dw the DBM wrapper * @param key the key (constant-width, determined at open time) * @param lenptr if non-NULL, writes length of (deserialized) value * * @return pointer to value, or NULL if it was either not found or the * deserialization failed. */ G_GNUC_HOT gpointer dbmw_read(dbmw_t *dw, gconstpointer key, size_t *lenptr) { struct cached *entry; dbmap_datum_t dval; dbmw_check(dw); g_assert(key); dw->r_access++; entry = map_lookup(dw->values, key); if (entry) { dw->r_hits++; if (lenptr) *lenptr = entry->len; return entry->data; } /* * Not cached, must read from DB. */ dw->ioerr = FALSE; dval = dbmap_lookup(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; g_warning("DBMW \"%s\" I/O error whilst reading entry: %s", dw->name, dbmap_strerror(dw->dm)); return NULL; } else if (NULL == dval.data) return NULL; /* Not found in DB */ /* * Value was found, allocate a cache entry object for it. */ WALLOC0(entry); /* * Deserialize data if needed. */ if (dw->unpack) { /* * Allocate cache entry arena to hold the deserialized version. */ entry->data = walloc(dw->value_size); entry->len = dw->value_size; bstr_reset(dw->bs, dval.data, dval.len, BSTR_F_ERROR); if (!dbmw_deserialize(dw, dw->bs, entry->data, dw->value_size)) { g_carp("DBMW \"%s\" deserialization error in %s(): %s", dw->name, stacktrace_routine_name(func_to_pointer(dw->unpack), FALSE), bstr_error(dw->bs)); /* Not calling value free routine on deserialization failures */ wfree(entry->data, dw->value_size); WFREE(entry); return NULL; } if (lenptr) *lenptr = dw->value_size; } else { g_assert(dw->value_size >= dval.len); if (dval.len) { entry->len = dval.len; entry->data = wcopy(dval.data, dval.len); } else { entry->data = NULL; entry->len = 0; } if (lenptr) *lenptr = dval.len; } g_assert((entry->len != 0) == (entry->data != NULL)); /* * Insert into cache. */ (void) allocate_entry(dw, key, entry); return entry->data; }
/** * Read value from database file, returning a pointer to the allocated * deserialized data. These data can be modified freely and stored back, * but their lifetime will not exceed that of the next call to a dbmw * operation on the same descriptor. * * User code does not need to bother with freeing the allocated data, this * is managed directly by the DBM wrapper. * * @param dw the DBM wrapper * @param key the key (constant-width, determined at open time) * @param lenptr if non-NULL, writes length of (deserialized) value * * @return pointer to value, or NULL if it was either not found or the * deserialization failed. */ G_GNUC_HOT void * dbmw_read(dbmw_t *dw, const void *key, size_t *lenptr) { struct cached *entry; dbmap_datum_t dval; dbmw_check(dw); g_assert(key); dw->r_access++; entry = map_lookup(dw->values, key); if (entry) { if (dbg_ds_debugging(dw->dbg, 5, DBG_DSF_CACHING | DBG_DSF_ACCESS)) { dbg_ds_log(dw->dbg, dw, "%s: read cache hit on %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (absent)" : ""); } dw->r_hits++; if (lenptr) *lenptr = entry->len; return entry->data; } /* * Not cached, must read from DB. */ dw->ioerr = FALSE; dval = dbmap_lookup(dw->dm, key); if (dbmap_has_ioerr(dw->dm)) { dw->ioerr = TRUE; dw->error = errno; s_warning_once_per(LOG_PERIOD_SECOND, "DBMW \"%s\" I/O error whilst reading entry: %s", dw->name, dbmap_strerror(dw->dm)); return NULL; } else if (NULL == dval.data) return NULL; /* Not found in DB */ /* * Value was found, allocate a cache entry object for it. */ WALLOC0(entry); /* * Deserialize data if needed. */ if (dw->unpack) { /* * Allocate cache entry arena to hold the deserialized version. */ entry->data = walloc(dw->value_size); entry->len = dw->value_size; bstr_reset(dw->bs, dval.data, dval.len, BSTR_F_ERROR); if (!dbmw_deserialize(dw, dw->bs, entry->data, dw->value_size)) { s_critical("DBMW \"%s\" deserialization error in %s(): %s", dw->name, stacktrace_function_name(dw->unpack), bstr_error(dw->bs)); /* Not calling value free routine on deserialization failures */ wfree(entry->data, dw->value_size); WFREE(entry); return NULL; } if (lenptr) *lenptr = dw->value_size; } else { g_assert(dw->value_size >= dval.len); if (dval.len) { entry->len = dval.len; entry->data = wcopy(dval.data, dval.len); } else { entry->data = NULL; entry->len = 0; } if (lenptr) *lenptr = dval.len; } g_assert((entry->len != 0) == (entry->data != NULL)); /* * Insert into cache. */ (void) allocate_entry(dw, key, entry); if (dbg_ds_debugging(dw->dbg, 4, DBG_DSF_CACHING)) { dbg_ds_log(dw->dbg, dw, "%s: cached %s key=%s%s", G_STRFUNC, entry->dirty ? "dirty" : "clean", dbg_ds_keystr(dw->dbg, key, (size_t) -1), entry->absent ? " (absent)" : ""); } return entry->data; }