ocarina/include/core/database.h

155 lines
4.6 KiB
C++

/*
* 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
* <valid> <data>
*
* 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 <core/file.h>
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 */