/** * Copyright 2013 (c) Anna Schumaker. */ #ifndef OCARINA_CORE_DATABASE_H #define OCARINA_CORE_DATABASE_H extern "C" { #include } /** * The DatabaseEntry class is the base class for storing * generic data inside a Database. */ 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 *); /* 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 *); /* Set up a struct db_entry after adding to the database. */ void (*dbe_setup)(struct db_entry *); /* Write a single struct db_entry to disk. */ void (*dbe_write)(struct file *, struct db_entry *); }; /** * Databases are a generic store for information used by Ocarina. Users * need to inherit from a DatabaseEntry class to properly use a Database. * * When writing a Database to disk, Databases need to store their size in * addition to values and valid bits at each index. For example, let's say * we have a Database with the following values: { 1, 2, NULL, 4 }. On * disk this would look like: * * 4 * 0 true 1 * 1 true 2 * 2 false * 3 true 4 * * * The Database class will add a newline after each DatabaseEntry. */ struct database { unsigned int db_size; /* The database's count of valid entries. */ bool db_autosave; /* True if the database saves when changed. */ 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. */ }; /* * 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 *); /* 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 *, struct db_entry *); /* Called to remove an item from the database. */ void db_remove(struct database *, struct db_entry *); /* Returns the database item at the requested index. */ struct db_entry *db_at(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_DATABASE_H */