ocarina/core/database.c

233 lines
4.9 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/database.h>
#include <core/idle.h>
#define DB_ENTRY_AT(db, index) \
(struct db_entry *)g_ptr_array_index(db->db_entries, index)
static void __dbe_free(struct database *db, struct db_entry *dbe)
{
if (dbe) {
g_hash_table_remove(db->db_keys, dbe->dbe_key);
g_free(dbe->dbe_key);
g_ptr_array_index(db->db_entries, dbe->dbe_index) = NULL;
db->db_ops->dbe_free(dbe);
db->db_size--;
}
}
static struct db_entry *__dbe_next(const struct database *db, unsigned int index)
{
if (!db->db_entries)
return NULL;
for (; index < db->db_entries->len; index++) {
if (DB_ENTRY_AT(db, index))
return DB_ENTRY_AT(db, index);
}
return NULL;
}
static struct db_entry *__dbe_read(struct database *db, unsigned int index)
{
struct db_entry *dbe = NULL;
if (file_readd(&db->db_file))
dbe = db->db_ops->dbe_read(&db->db_file, index);
g_ptr_array_index(db->db_entries, index) = dbe;
return dbe;
}
static void __dbe_setup(struct database *db, unsigned int index)
{
struct db_entry *dbe = DB_ENTRY_AT(db, index);
if (dbe) {
dbe->dbe_index = index;
dbe->dbe_key = db->db_ops->dbe_key(dbe);
g_hash_table_insert(db->db_keys, dbe->dbe_key, dbe);
db->db_size++;
}
}
static void __dbe_write(struct database *db, struct db_entry *dbe)
{
if (dbe) {
file_writef(&db->db_file, "%u ", true);
db->db_ops->dbe_write(&db->db_file, dbe);
} else
file_writef(&db->db_file, "%u", false);
file_writef(&db->db_file, "\n");
}
void db_init(struct database *db, const char *filepath, bool autosave,
const struct db_ops *ops, unsigned int fmin)
{
db->db_ops = ops;
db->db_size = 0;
db->db_autosave = autosave;
db->db_entries = g_ptr_array_new();
db->db_keys = g_hash_table_new(g_str_hash, g_str_equal);
file_init_data(&db->db_file, "", filepath, fmin);
}
void db_deinit(struct database *db)
{
struct db_entry *dbe, *next;
db_for_each(dbe, next, db)
__dbe_free(db, dbe);
g_ptr_array_free(db->db_entries, true);
g_hash_table_destroy(db->db_keys);
db->db_entries = NULL;
db->db_keys = NULL;
}
void db_save(struct database *db)
{
if (file_open(&db->db_file, OPEN_WRITE) == false)
return;
file_writef(&db->db_file, "%u\n", db_actual_size(db));
for (unsigned int i = 0; i < db_actual_size(db); i++)
__dbe_write(db, DB_ENTRY_AT(db, i));
file_close(&db->db_file);
}
void db_autosave(struct database *db)
{
if (db->db_autosave == true)
db_save(db);
}
void db_load(struct database *db)
{
unsigned int size;
bool save;
if (file_open(&db->db_file, OPEN_READ) == false)
return;
size = file_readu(&db->db_file);
g_ptr_array_set_size(db->db_entries, size);
for (unsigned int i = 0; i < size; i++) {
if (__dbe_read(db, i))
__dbe_setup(db, i);
}
save = file_version(&db->db_file) != OCARINA_MINOR_VERSION;
file_close(&db->db_file);
if (save)
db_autosave(db);
}
struct db_entry *db_insert(struct database *db, const gchar *key)
{
struct db_entry *item = NULL;
if (key)
item = db->db_ops->dbe_alloc(key, db_actual_size(db));
if (item) {
g_ptr_array_add(db->db_entries, item);
__dbe_setup(db, db_actual_size(db) - 1);
db_autosave(db);
}
return item;
}
void db_remove(struct database *db, struct db_entry *item)
{
if (item == NULL)
return;
if (db_at(db, item->dbe_index) != item)
return;
__dbe_free(db, item);
db_autosave(db);
}
bool db_defrag(struct database *db)
{
struct db_entry *dbe;
unsigned int i, cur;
if (db->db_size == db_actual_size(db))
return false;
for (cur = 0, i = 1; cur < db->db_size; cur++) {
if (DB_ENTRY_AT(db, cur))
continue;
while (i <= cur || !DB_ENTRY_AT(db, i))
i++;
dbe = DB_ENTRY_AT(db, i);
g_ptr_array_index(db->db_entries, i) = NULL;
g_ptr_array_index(db->db_entries, cur) = dbe;
dbe->dbe_index = cur;
}
g_ptr_array_set_size(db->db_entries, db->db_size);
db_autosave(db);
return true;
}
void db_rekey(struct database *db, struct db_entry *dbe)
{
if (dbe == NULL)
return;
g_hash_table_remove(db->db_keys, dbe->dbe_key);
g_free(dbe->dbe_key);
dbe->dbe_key = db->db_ops->dbe_key(dbe);
g_hash_table_insert(db->db_keys, dbe->dbe_key, dbe);
}
unsigned int db_actual_size(const struct database *db)
{
if (db->db_entries)
return db->db_entries->len;
return 0;
}
struct db_entry *db_first(const struct database *db)
{
return __dbe_next(db, 0);
}
struct db_entry *db_next(const struct database *db, struct db_entry *ent)
{
if (ent)
return __dbe_next(db, ent->dbe_index + 1);
return NULL;
}
struct db_entry *db_at(const struct database *db, unsigned int index)
{
if (index >= db_actual_size(db))
return NULL;
return DB_ENTRY_AT(db, index);
}
struct db_entry *db_get(struct database *db, const gchar *key)
{
return (struct db_entry *)g_hash_table_lookup(db->db_keys, key);
}
struct db_entry *db_find(struct database *db, const gchar *key)
{
struct db_entry *dbe = db_get(db, key);
if (dbe)
return dbe;
return db_insert(db, key);
}