/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #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); }