/* * Copyright 2013 (c) Anna Schumaker. * * Databases are a generic store for information used by Ocarina. Users must * provide a struct db_ops when initializing a database. * * When writing a database to disk, databases will store their size in additon * to valid bits and values at each index. For example, let's say we have a * database with the following values: { 1, 2, NULL, 4 }. The resulting file * would look like: * * 4 * 1 1 * 1 2 * 0 * 1 4 * * * The database class will add a newline after each struct db_entry. */ #ifndef OCARINA_CORE_CONTAINERS_DATABASE_H #define OCARINA_CORE_CONTAINERS_DATABASE_H #include struct db_entry { unsigned int dbe_index; /* The db_entry's position in the database. */ gchar *dbe_key; /* The db_entry's hash key. */ void *dbe_data; /* The db_entry's private data. */ }; static inline void dbe_init(struct db_entry *dbe, void *data) { dbe->dbe_index = 0; dbe->dbe_key = NULL; dbe->dbe_data = data; } static inline void *DBE_DATA(struct db_entry *dbe) { if (dbe) return dbe->dbe_data; return NULL; } struct db_ops { /* Allocate a new struct db_entry from a given key. */ struct db_entry *(*dbe_alloc)(const gchar *, unsigned int); /* Free a struct db_entry. */ void (*dbe_free)(struct db_entry *); /* Return a unique string representing a single struct db_entry. */ gchar *(*dbe_key)(struct db_entry *); /* Read a single struct db_entry from disk. */ struct db_entry *(*dbe_read)(struct file *, unsigned int); /* Write a single struct db_entry to disk. */ void (*dbe_write)(struct file *, struct db_entry *); }; struct database { unsigned int db_size; /* The database's count of valid entries. */ bool db_autosave; /* The database's automatic save setting. */ struct file db_file; /* The database's associated file object. */ GPtrArray *db_entries; /* The database's backing array. */ GHashTable *db_keys; /* The database's mapping of key -> value. */ const struct db_ops *db_ops; /* The database's operations vector. */ }; #define DB_INIT(fname, autosave, ops, fmin) \ { \ .db_size = 0, \ .db_autosave = autosave, \ .db_file = FILE_INIT_DATA("", fname, fmin), \ .db_entries = g_ptr_array_new(), \ .db_keys = g_hash_table_new(g_str_hash, g_str_equal), \ .db_ops = ops, \ } /* * Initialize a database using filepath as a location on disk to store data * and autosave as a hint for if this database should be automatically saved. */ void db_init(struct database *, const char *, bool, const struct db_ops *, unsigned int); /* Called to prevent memory leaks by freeing all remaining database entries. */ void db_deinit(struct database *); /* Called to write the database to disk. */ void db_save(struct database *); /* Save the database to disk iff database->db_autosave is set to True */ void db_autosave(struct database *); /* Called to read the database from disk. */ void db_load(struct database *); /* Returns the size of the backing std::vector. */ unsigned int db_actual_size(const struct database *); /* * Called to add a new item to the database. The caller MUST check if the * database already contains a similar item before calling this function. */ struct db_entry *db_insert(struct database *, const gchar *); /* Called to remove an item from the database. */ void db_remove(struct database *, struct db_entry *); /* * Called to shrink the database by removing any NULL pointers without * changing the order of items in the database. * Returns true if the database has been modified. */ bool db_defrag(struct database *); /* Called to change the key of a database entry. */ void db_rekey(struct database *, struct db_entry *); /* Returns the database item at the requested index. */ struct db_entry *db_at(const struct database *, unsigned int); /* Returns the database item with the specified key. */ struct db_entry *db_get(struct database *, const gchar *); /* * Similar to db_get(), but allocate and return a new item if the * database doesn't contain an item with the specified key. */ struct db_entry *db_find(struct database *, const gchar *); /* Returns the first valid database item. */ struct db_entry *db_first(const struct database *); /* Returns the next valid database item. */ struct db_entry *db_next(const struct database *, struct db_entry *); #define db_for_each(ent, next, db) \ for (ent = db_first(db), next = db_next(db, ent); \ ent != NULL; \ ent = next, next = db_next(db, ent)) #endif /* OCARINA_CORE_CONTAINERS_DATABASE_H */