core/database: De-templatize the database
We now have an interface for controlling everything through struct db_entry, so let's just have the database store a db_entry directly. Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
parent
1bef380ba7
commit
1c22ecfda4
|
@ -10,3 +10,175 @@ db_entry :: db_entry()
|
|||
}
|
||||
|
||||
db_entry :: ~db_entry() {}
|
||||
|
||||
static void __dbe_free(struct database *db, struct db_entry *dbe)
|
||||
{
|
||||
if (dbe) {
|
||||
db->db_keys.erase(dbe->dbe_key);
|
||||
g_free(dbe->dbe_key);
|
||||
|
||||
db->db_entries[dbe->dbe_index] = NULL;
|
||||
db->db_ops->dbe_free(dbe);
|
||||
|
||||
db->db_size--;
|
||||
}
|
||||
}
|
||||
|
||||
struct db_entry *__dbe_next(const struct database *db, unsigned int index)
|
||||
{
|
||||
for (; index < db->db_entries.size(); index++) {
|
||||
if (db->db_entries[index])
|
||||
return db->db_entries[index];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct db_entry *__dbe_read(struct database *db, unsigned int index)
|
||||
{
|
||||
struct db_entry *dbe = NULL;
|
||||
int valid;
|
||||
|
||||
file_readf(&db->db_file, "%d", &valid);
|
||||
if (valid)
|
||||
dbe = db->db_ops->dbe_read(&db->db_file);
|
||||
|
||||
db->db_entries[index] = dbe;
|
||||
return dbe;
|
||||
}
|
||||
|
||||
static void __dbe_setup(struct database *db, unsigned int index)
|
||||
{
|
||||
struct db_entry *dbe = db->db_entries[index];
|
||||
|
||||
if (dbe) {
|
||||
dbe->dbe_index = index;
|
||||
dbe->dbe_key = db->db_ops->dbe_key(dbe);
|
||||
db->db_keys[dbe->dbe_key] = index;
|
||||
db->db_size++;
|
||||
if (db->db_ops->dbe_setup)
|
||||
db->db_ops->dbe_setup(dbe);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
db->db_ops = ops;
|
||||
db->db_size = 0;
|
||||
db->db_autosave = autosave;
|
||||
file_init(&db->db_file, filepath, 0);
|
||||
}
|
||||
|
||||
void db_deinit(struct database *db)
|
||||
{
|
||||
struct db_entry *dbe, *next;
|
||||
db_for_each(dbe, next, db)
|
||||
__dbe_free(db, dbe);
|
||||
}
|
||||
|
||||
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->db_entries[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;
|
||||
|
||||
if (file_open(&db->db_file, OPEN_READ) == false)
|
||||
return;
|
||||
|
||||
file_readf(&db->db_file, "%u", &size);
|
||||
db->db_entries.resize(size);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (__dbe_read(db, i))
|
||||
__dbe_setup(db, i);
|
||||
}
|
||||
|
||||
file_close(&db->db_file);
|
||||
}
|
||||
|
||||
struct db_entry *db_insert(struct database *db, struct db_entry *item)
|
||||
{
|
||||
if (item) {
|
||||
db->db_entries.push_back(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);
|
||||
}
|
||||
|
||||
unsigned int db_actual_size(const struct database *db)
|
||||
{
|
||||
return db->db_entries.size();
|
||||
}
|
||||
|
||||
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(struct database *db, unsigned int index)
|
||||
{
|
||||
if (index >= db_actual_size(db))
|
||||
return NULL;
|
||||
return db->db_entries[index];
|
||||
}
|
||||
|
||||
struct db_entry *db_get(struct database *db, const gchar *key)
|
||||
{
|
||||
std::map<const std::string, unsigned int>::iterator it;
|
||||
it = db->db_keys.find(key);
|
||||
if (it == db->db_keys.end())
|
||||
return NULL;
|
||||
return db->db_entries[it->second];
|
||||
}
|
||||
|
||||
struct db_entry *db_find(struct database *db, const gchar *key)
|
||||
{
|
||||
struct db_entry *dbe = db_get(db, key);
|
||||
if (dbe)
|
||||
return dbe;
|
||||
dbe = db->db_ops->dbe_alloc(key);
|
||||
return db_insert(db, dbe);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ extern "C" {
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
static database<index_entry> filter_index;
|
||||
static struct database filter_index;
|
||||
|
||||
void filter_init() { index_init(&filter_index, "", false); }
|
||||
void filter_deinit() { db_deinit(&filter_index); }
|
||||
|
@ -49,7 +49,8 @@ void filter :: search(const std::string &text, struct set *res)
|
|||
if (end == std::string::npos)
|
||||
end = lc.size();
|
||||
|
||||
found = db_get(&filter_index, lc.substr(begin, end- begin).c_str());
|
||||
found = INDEX_ENTRY(db_get(&filter_index,
|
||||
lc.substr(begin, end- begin).c_str()));
|
||||
if (!found) {
|
||||
set_clear(res);
|
||||
return;
|
||||
|
|
|
@ -59,35 +59,33 @@ static const struct db_ops index_ops = {
|
|||
};
|
||||
|
||||
|
||||
void index_init(database<index_entry> *index, const gchar *filepath,
|
||||
bool autosave)
|
||||
void index_init(struct database *index, const gchar *filepath, bool autosave)
|
||||
{
|
||||
db_init(index, filepath, autosave, &index_ops);
|
||||
}
|
||||
|
||||
index_entry *index_insert(database<index_entry> *index, const gchar *key,
|
||||
index_entry *index_insert(struct database *index, const gchar *key,
|
||||
unsigned int value)
|
||||
{
|
||||
index_entry *it = db_find(index, key);
|
||||
struct index_entry *it = INDEX_ENTRY(db_find(index, key));
|
||||
|
||||
set_insert(&it->ie_set, value);
|
||||
db_autosave(index);
|
||||
return it;
|
||||
}
|
||||
|
||||
void index_remove(database<index_entry> *index, const gchar *key,
|
||||
unsigned int value)
|
||||
void index_remove(struct database *index, const gchar *key, unsigned int value)
|
||||
{
|
||||
index_entry *it = db_get(index, key);
|
||||
struct index_entry *it = INDEX_ENTRY(db_get(index, key));
|
||||
if (it) {
|
||||
set_remove(&it->ie_set, value);
|
||||
db_autosave(index);
|
||||
}
|
||||
}
|
||||
|
||||
bool index_has(database<index_entry> *index, const gchar *key, unsigned int value)
|
||||
bool index_has(struct database *index, const gchar *key, unsigned int value)
|
||||
{
|
||||
index_entry *it = db_get(index, key);
|
||||
struct index_entry *it = INDEX_ENTRY(db_get(index, key));
|
||||
|
||||
if (!it)
|
||||
return false;
|
||||
|
|
|
@ -119,9 +119,11 @@ static void scan_path(struct scan_info &scan)
|
|||
|
||||
static void validate_library(struct library *&library)
|
||||
{
|
||||
struct track *track, *next;
|
||||
struct db_entry *dbe, *next;
|
||||
struct track *track;
|
||||
|
||||
db_for_each(track, next, track_db_get()) {
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
track = TRACK(dbe);
|
||||
if (track->tr_library != library)
|
||||
continue;
|
||||
|
||||
|
@ -140,13 +142,13 @@ static void validate_library(struct library *&library)
|
|||
|
||||
void collection :: init()
|
||||
{
|
||||
struct track *track, *next;
|
||||
struct db_entry *track, *next;
|
||||
|
||||
library_q.load();
|
||||
|
||||
db_for_each(track, next, track_db_get()) {
|
||||
if (track->tr_library->li_enabled)
|
||||
library_q.add(track);
|
||||
if (TRACK(track)->tr_library->li_enabled)
|
||||
library_q.add(TRACK(track));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,22 +189,24 @@ void collection :: update(struct library *library)
|
|||
|
||||
void collection :: update_all()
|
||||
{
|
||||
struct library *library, *next;
|
||||
struct db_entry *library, *next;
|
||||
|
||||
db_for_each(library, next, library_db_get())
|
||||
update(library);
|
||||
update(LIBRARY(library));
|
||||
}
|
||||
|
||||
void collection :: set_enabled(struct library *library, bool enabled)
|
||||
{
|
||||
struct track *track, *next;
|
||||
struct db_entry *dbe, *next;
|
||||
struct track *track;
|
||||
|
||||
if (!library || (library->li_enabled == enabled))
|
||||
return;
|
||||
|
||||
library_set_enabled(library, enabled);
|
||||
|
||||
db_for_each(track, next, track_db_get()) {
|
||||
db_for_each(dbe, next, track_db_get()) {
|
||||
track = TRACK(dbe);
|
||||
if (track->tr_library == library) {
|
||||
if (enabled)
|
||||
library_q.add(track);
|
||||
|
|
|
@ -33,11 +33,11 @@ public:
|
|||
|
||||
unsigned int find_average_count()
|
||||
{
|
||||
struct track *track, *next;
|
||||
struct db_entry *track, *next;
|
||||
unsigned int total = 0, count = 0;
|
||||
|
||||
db_for_each(track, next, track_db_get()) {
|
||||
total += track->tr_count;
|
||||
total += TRACK(track)->tr_count;
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public:
|
|||
|
||||
void dynamic_fill(const std::string &name)
|
||||
{
|
||||
struct track *track, *next;
|
||||
struct db_entry *track, *next;
|
||||
unsigned int avg = 0;
|
||||
|
||||
if ((name == "Most Played") || (name == "Least Played"))
|
||||
|
@ -66,13 +66,13 @@ public:
|
|||
|
||||
clear();
|
||||
db_for_each(track, next, track_db_get())
|
||||
dynamic_add(name, track, avg);
|
||||
dynamic_add(name, TRACK(track), avg);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
static database<index_entry> playlist_db;
|
||||
static struct database playlist_db;
|
||||
static PlaylistQueue playlist_q;
|
||||
static std::string cur_plist;
|
||||
|
||||
|
@ -122,7 +122,7 @@ void playlist :: del(struct track *track, const std::string &name)
|
|||
|
||||
void playlist :: select(const std::string &name)
|
||||
{
|
||||
index_entry *ent = db_get(&playlist_db, name.c_str());
|
||||
index_entry *ent = INDEX_ENTRY(db_get(&playlist_db, name.c_str()));
|
||||
|
||||
if (ent != NULL)
|
||||
playlist_q.fill(ent);
|
||||
|
@ -134,7 +134,7 @@ void playlist :: select(const std::string &name)
|
|||
|
||||
index_entry *playlist :: get_tracks(const std::string &name)
|
||||
{
|
||||
return db_get(&playlist_db, name.c_str());
|
||||
return INDEX_ENTRY(db_get(&playlist_db, name.c_str()));
|
||||
}
|
||||
|
||||
Queue *playlist :: get_queue()
|
||||
|
|
|
@ -7,7 +7,7 @@ extern "C" {
|
|||
#include <core/tags/album.h>
|
||||
|
||||
|
||||
static database<album> album_db;
|
||||
static struct database album_db;
|
||||
|
||||
static gchar *__album_key(const std::string &name, unsigned int year)
|
||||
{
|
||||
|
@ -89,14 +89,14 @@ void album_db_deinit()
|
|||
struct album *album_find(const std::string &name, unsigned int year)
|
||||
{
|
||||
gchar *key = __album_key(name, year);
|
||||
struct album *album = db_find(&album_db, key);
|
||||
struct album *album = ALBUM(db_find(&album_db, key));
|
||||
g_free(key);
|
||||
return album;
|
||||
}
|
||||
|
||||
struct album *album_get(const unsigned int index)
|
||||
{
|
||||
return db_at(&album_db, index);
|
||||
return ALBUM(db_at(&album_db, index));
|
||||
}
|
||||
|
||||
int album_compare(struct album *lhs, struct album *rhs)
|
||||
|
|
|
@ -7,7 +7,7 @@ extern "C" {
|
|||
#include <core/tags/artist.h>
|
||||
|
||||
|
||||
static database<struct artist> artist_db;
|
||||
static struct database artist_db;
|
||||
|
||||
|
||||
artist :: artist() {}
|
||||
|
@ -73,12 +73,12 @@ void artist_db_deinit()
|
|||
|
||||
struct artist *artist_find(const std::string &name)
|
||||
{
|
||||
return db_find(&artist_db, name.c_str());
|
||||
return ARTIST(db_find(&artist_db, name.c_str()));
|
||||
}
|
||||
|
||||
struct artist *artist_get(const unsigned int index)
|
||||
{
|
||||
return db_at(&artist_db, index);
|
||||
return ARTIST(db_at(&artist_db, index));
|
||||
}
|
||||
|
||||
int artist_compare(struct artist *lhs, struct artist *rhs)
|
||||
|
|
|
@ -7,7 +7,7 @@ extern "C" {
|
|||
#include <core/tags/genre.h>
|
||||
|
||||
|
||||
static database<struct genre> genre_db;
|
||||
static struct database genre_db;
|
||||
|
||||
genre :: genre() {}
|
||||
|
||||
|
@ -72,12 +72,12 @@ void genre_db_deinit()
|
|||
|
||||
struct genre *genre_find(const std::string &name)
|
||||
{
|
||||
return db_find(&genre_db, name.c_str());
|
||||
return GENRE(db_find(&genre_db, name.c_str()));
|
||||
}
|
||||
|
||||
struct genre *genre_get(const unsigned int index)
|
||||
{
|
||||
return db_at(&genre_db, index);
|
||||
return GENRE(db_at(&genre_db, index));
|
||||
}
|
||||
|
||||
int genre_compare(struct genre *lhs, struct genre *rhs)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <core/tags/library.h>
|
||||
|
||||
|
||||
static database<struct library> library_db;
|
||||
static struct database library_db;
|
||||
|
||||
|
||||
library :: library()
|
||||
|
@ -76,19 +76,19 @@ void library_db_deinit()
|
|||
db_deinit(&library_db);
|
||||
}
|
||||
|
||||
const database<struct library> *library_db_get()
|
||||
const struct database *library_db_get()
|
||||
{
|
||||
return &library_db;
|
||||
}
|
||||
|
||||
struct library *library_find(const std::string &path)
|
||||
{
|
||||
return db_find(&library_db, path.c_str());
|
||||
return LIBRARY(db_find(&library_db, path.c_str()));
|
||||
}
|
||||
|
||||
struct library *library_get(const unsigned int index)
|
||||
{
|
||||
return db_at(&library_db, index);
|
||||
return LIBRARY(db_at(&library_db, index));
|
||||
}
|
||||
|
||||
void library_remove(struct library *library)
|
||||
|
|
|
@ -11,7 +11,7 @@ extern "C" {
|
|||
#include <glib.h>
|
||||
|
||||
|
||||
static database<struct track> track_db;
|
||||
static struct database track_db;
|
||||
|
||||
static gchar *__track_key(struct library *library, const std::string &path)
|
||||
{
|
||||
|
@ -164,7 +164,7 @@ void track_db_commit()
|
|||
db_save(&track_db);
|
||||
}
|
||||
|
||||
const database<struct track> *track_db_get()
|
||||
const struct database *track_db_get()
|
||||
{
|
||||
return &track_db;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ struct track *track_add(struct library *library, const std::string &filepath)
|
|||
struct track *track = NULL;
|
||||
|
||||
if (!db_get(&track_db, key))
|
||||
track = db_insert(&track_db, new struct track(key));
|
||||
track = TRACK(db_insert(&track_db, new struct track(key)));
|
||||
|
||||
g_free(key);
|
||||
return track;
|
||||
|
@ -189,10 +189,10 @@ void track_remove(struct track *track)
|
|||
|
||||
void track_remove_all(struct library *library)
|
||||
{
|
||||
struct track *it, *next;
|
||||
struct db_entry *it, *next;
|
||||
|
||||
db_for_each(it, next, &track_db) {
|
||||
if (it->tr_library == library)
|
||||
if (TRACK(it)->tr_library == library)
|
||||
db_remove(&track_db, it);
|
||||
}
|
||||
track_db_commit();
|
||||
|
@ -200,7 +200,7 @@ void track_remove_all(struct library *library)
|
|||
|
||||
struct track *track_get(const unsigned int index)
|
||||
{
|
||||
return db_at(&track_db, index);
|
||||
return TRACK(db_at(&track_db, index));
|
||||
}
|
||||
|
||||
int track_compare(struct track *lhs, struct track *rhs)
|
||||
|
|
|
@ -174,7 +174,7 @@ static void on_toggled(const Glib::ustring &str)
|
|||
|
||||
void manager :: init()
|
||||
{
|
||||
struct library *library, *next;
|
||||
struct db_entry *library, *next;
|
||||
|
||||
c_add = gui :: get_widget<Gtk::Button>("colmgr_add");
|
||||
c_update = gui :: get_widget<Gtk::Button>("colmgr_update");
|
||||
|
@ -195,5 +195,5 @@ void manager :: init()
|
|||
c_treeview->signal_key_press_event().connect(sigc::ptr_fun(on_key_pressed));
|
||||
|
||||
db_for_each(library, next, library_db_get())
|
||||
list_path(library);
|
||||
list_path(LIBRARY(library));
|
||||
}
|
||||
|
|
|
@ -63,14 +63,13 @@ struct db_ops {
|
|||
*
|
||||
* The Database class will add a newline after each DatabaseEntry.
|
||||
*/
|
||||
template <class T>
|
||||
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. */
|
||||
|
||||
const struct db_ops *db_ops; /* The database's operations vector. */
|
||||
std::vector<T *> db_entries;
|
||||
std::vector<struct db_entry *> db_entries;
|
||||
std::map<const std::string, unsigned int> db_keys;
|
||||
};
|
||||
|
||||
|
@ -79,65 +78,52 @@ struct database {
|
|||
* 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.
|
||||
*/
|
||||
template <class T>
|
||||
void db_init(struct database<T> *, const char *, bool, const struct db_ops *);
|
||||
void db_init(struct database *, const char *, bool, const struct db_ops *);
|
||||
|
||||
/* Called to prevent memory leaks by freeing all remaining database entries. */
|
||||
template <class T>
|
||||
void db_deinit(struct database<T> *);
|
||||
void db_deinit(struct database *);
|
||||
|
||||
|
||||
/* Called to write the database to disk. */
|
||||
template <class T>
|
||||
void db_save(struct database<T> *);
|
||||
void db_save(struct database *);
|
||||
|
||||
/* Save the database to disk iff database->db_autosave is set to True */
|
||||
template <class T>
|
||||
void db_autosave(struct database<T> *);
|
||||
void db_autosave(struct database *);
|
||||
|
||||
/* Called to read the database from disk. */
|
||||
template <class T>
|
||||
void db_load(struct database<T> *);
|
||||
void db_load(struct database *);
|
||||
|
||||
/* Returns the size of the backing std::vector. */
|
||||
template <class T>
|
||||
unsigned int db_actual_size(const struct database<T> *);
|
||||
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.
|
||||
*/
|
||||
template <class T>
|
||||
T *db_insert(struct database<T> *, T *);
|
||||
struct db_entry *db_insert(struct database *, struct db_entry *);
|
||||
|
||||
/* Called to remove an item from the database. */
|
||||
template <class T>
|
||||
void db_remove(struct database<T> *, T *);
|
||||
void db_remove(struct database *, struct db_entry *);
|
||||
|
||||
|
||||
/* Returns the database item at the requested index. */
|
||||
template <class T>
|
||||
T *db_at(struct database<T> *, unsigned int);
|
||||
struct db_entry *db_at(struct database *, unsigned int);
|
||||
|
||||
/* Returns the database item with the specified key. */
|
||||
template <class T>
|
||||
T *db_get(struct database<T> *, const gchar *);
|
||||
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.
|
||||
*/
|
||||
template <class T>
|
||||
T *db_find(struct database<T> *, const gchar *);
|
||||
struct db_entry *db_find(struct database *, const gchar *);
|
||||
|
||||
/* Returns the first valid database item. */
|
||||
template <class T>
|
||||
T *db_first(const struct database<T> *);
|
||||
struct db_entry *db_first(const struct database *);
|
||||
|
||||
/* Returns the next valid database item. */
|
||||
template <class T>
|
||||
T *db_next(const struct database<T> *, T *);
|
||||
struct db_entry *db_next(const struct database *, struct db_entry *);
|
||||
|
||||
|
||||
#define db_for_each(ent, next, db) \
|
||||
|
@ -145,7 +131,4 @@ T *db_next(const struct database<T> *, T *);
|
|||
ent != NULL; \
|
||||
ent = next, next = db_next(db, ent))
|
||||
|
||||
|
||||
#include "database.hpp"
|
||||
|
||||
#endif /* OCARINA_CORE_DATABASE_H */
|
||||
|
|
|
@ -1,199 +0,0 @@
|
|||
/**
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*
|
||||
* DO NOT INCLUDE THIS FILE DIRECTLY. THIS IS A TEMPLATE DEFINITION FILE
|
||||
* AND ONLY MEANT TO BE INCLUDED BY include/database.h!
|
||||
*/
|
||||
#ifndef OCARINA_DATABASE_HPP
|
||||
#define OCARINA_DATABASE_HPP
|
||||
|
||||
template <class T>
|
||||
static inline void __dbe_free(struct database<T> *db, struct db_entry *dbe)
|
||||
{
|
||||
if (dbe) {
|
||||
db->db_keys.erase(dbe->dbe_key);
|
||||
g_free(dbe->dbe_key);
|
||||
|
||||
db->db_entries[dbe->dbe_index] = NULL;
|
||||
db->db_ops->dbe_free(dbe);
|
||||
|
||||
db->db_size--;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline struct db_entry *__dbe_read(struct database<T> *db, unsigned int index)
|
||||
{
|
||||
struct db_entry *dbe = NULL;
|
||||
int valid;
|
||||
|
||||
file_readf(&db->db_file, "%d", &valid);
|
||||
if (valid)
|
||||
dbe = db->db_ops->dbe_read(&db->db_file);
|
||||
|
||||
db->db_entries[index] = (T *)dbe;
|
||||
return dbe;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline void __dbe_setup(struct database<T> *db, unsigned int index)
|
||||
{
|
||||
struct db_entry *dbe = db->db_entries[index];
|
||||
|
||||
if (dbe) {
|
||||
dbe->dbe_index = index;
|
||||
dbe->dbe_key = db->db_ops->dbe_key(dbe);
|
||||
db->db_keys[dbe->dbe_key] = index;
|
||||
db->db_size++;
|
||||
if (db->db_ops->dbe_setup)
|
||||
db->db_ops->dbe_setup(dbe);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline void __dbe_write(struct database<T> *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");
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_init(struct database<T> *db, const char *filepath, bool autosave,
|
||||
const struct db_ops *ops)
|
||||
{
|
||||
db->db_ops = ops;
|
||||
db->db_size = 0;
|
||||
db->db_autosave = autosave;
|
||||
file_init(&db->db_file, filepath, 0);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_deinit(struct database<T> *db)
|
||||
{
|
||||
T *dbe, *next;
|
||||
db_for_each(dbe, next, db)
|
||||
__dbe_free(db, dbe);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_save(struct database<T> *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->db_entries[i]);
|
||||
|
||||
file_close(&db->db_file);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_autosave(struct database<T> *db)
|
||||
{
|
||||
if (db->db_autosave == true)
|
||||
db_save(db);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_load(struct database<T> *db)
|
||||
{
|
||||
unsigned int size;
|
||||
|
||||
if (file_open(&db->db_file, OPEN_READ) == false)
|
||||
return;
|
||||
|
||||
file_readf(&db->db_file, "%u", &size);
|
||||
db->db_entries.resize(size);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (__dbe_read(db, i))
|
||||
__dbe_setup(db, i);
|
||||
}
|
||||
|
||||
file_close(&db->db_file);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_insert(struct database<T> *db, T *item)
|
||||
{
|
||||
if (item) {
|
||||
db->db_entries.push_back(item);
|
||||
__dbe_setup(db, db_actual_size(db) - 1);
|
||||
db_autosave(db);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void db_remove(struct database<T> *db, T *item)
|
||||
{
|
||||
if (item == NULL)
|
||||
return;
|
||||
if (db_at(db, item->dbe_index) != item)
|
||||
return;
|
||||
__dbe_free(db, item);
|
||||
db_autosave(db);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
unsigned int db_actual_size(const struct database<T> *db)
|
||||
{
|
||||
return db->db_entries.size();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *__db_next(const struct database<T> *db, unsigned int index)
|
||||
{
|
||||
for (; index < db->db_entries.size(); index++) {
|
||||
if (db->db_entries[index])
|
||||
return db->db_entries[index];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_first(const struct database<T> *db)
|
||||
{
|
||||
return __db_next(db, 0);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_next(const struct database<T> *db, T *ent)
|
||||
{
|
||||
if (ent)
|
||||
return __db_next(db, ent->dbe_index + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_at(struct database<T> *db, unsigned int index)
|
||||
{
|
||||
if (index >= db_actual_size(db))
|
||||
return NULL;
|
||||
return db->db_entries[index];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_get(struct database<T> *db, const gchar *key)
|
||||
{
|
||||
std::map<const std::string, unsigned int>::iterator it;
|
||||
it = db->db_keys.find(key);
|
||||
if (it == db->db_keys.end())
|
||||
return NULL;
|
||||
return db->db_entries[it->second];
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T *db_find(struct database<T> *db, const gchar *key)
|
||||
{
|
||||
T *dbe = db_get(db, key);
|
||||
if (dbe)
|
||||
return dbe;
|
||||
dbe = (T *)db->db_ops->dbe_alloc(key);
|
||||
return db_insert(db, dbe);
|
||||
}
|
||||
#endif /* OCARINA_DATABASE_HPP */
|
|
@ -23,18 +23,20 @@ struct index_entry : public db_entry {
|
|||
index_entry(); /**< Create an empty IndexEntry. */
|
||||
};
|
||||
|
||||
#define INDEX_ENTRY(dbe) ((struct index_entry *)dbe)
|
||||
|
||||
|
||||
/* Initialize a database for use as an index. */
|
||||
void index_init(database<index_entry> *, const gchar *, bool);
|
||||
void index_init(struct database *, const gchar *, bool);
|
||||
|
||||
/* Add a value to an index item with the specified key. */
|
||||
index_entry *index_insert(database<index_entry> *, const gchar *, unsigned int);
|
||||
index_entry *index_insert(struct database *, const gchar *, unsigned int);
|
||||
|
||||
/* Remove a value from an index item with the specified key. */
|
||||
void index_remove(database<index_entry> *, const gchar *, unsigned int);
|
||||
void index_remove(struct database *, const gchar *, unsigned int);
|
||||
|
||||
/* Called to check if the index has the specified (key, value) pair. */
|
||||
bool index_has(database<index_entry> *, const gchar *, unsigned int);
|
||||
bool index_has(struct database *, const gchar *, unsigned int);
|
||||
|
||||
#ifdef CONFIG_TESTING
|
||||
const struct db_ops *test_index_ops();
|
||||
|
|
|
@ -25,6 +25,8 @@ struct album : public db_entry {
|
|||
album(); /**< Album tag constructor */
|
||||
};
|
||||
|
||||
#define ALBUM(dbe) ((struct album *)dbe);
|
||||
|
||||
|
||||
/* Called to initialize the album database. */
|
||||
void album_db_init();
|
||||
|
|
|
@ -17,6 +17,8 @@ struct artist : public db_entry {
|
|||
artist(); /**< Artist tag constructor. */
|
||||
};
|
||||
|
||||
#define ARTIST(dbe) ((struct artist *)dbe)
|
||||
|
||||
|
||||
/* Called to initialize the artist database. */
|
||||
void artist_db_init();
|
||||
|
|
|
@ -18,6 +18,8 @@ public:
|
|||
genre(); /**< Genre tag constructor. */
|
||||
};
|
||||
|
||||
#define GENRE(dbe) ((struct genre *)dbe)
|
||||
|
||||
|
||||
/* Called to initialize the genre database. */
|
||||
void genre_db_init();
|
||||
|
|
|
@ -25,6 +25,8 @@ struct library : public db_entry {
|
|||
library(); /**< Library tag constructor. */
|
||||
};
|
||||
|
||||
#define LIBRARY(dbe) ((struct library *)dbe)
|
||||
|
||||
|
||||
/* Called to initialize the library database. */
|
||||
void library_db_init();
|
||||
|
@ -33,7 +35,7 @@ void library_db_init();
|
|||
void library_db_deinit();
|
||||
|
||||
/* Called to access the library database. */
|
||||
const database<struct library> *library_db_get();
|
||||
const struct database *library_db_get();
|
||||
|
||||
/* Called to find a library tag by library path. */
|
||||
struct library *library_find(const std::string &);
|
||||
|
|
|
@ -37,6 +37,8 @@ struct track : public db_entry {
|
|||
track(); /**< Track constructor. */
|
||||
};
|
||||
|
||||
#define TRACK(dbe) ((struct track *)dbe)
|
||||
|
||||
|
||||
/* Called to initialize the track database. */
|
||||
void track_db_init();
|
||||
|
@ -48,7 +50,7 @@ void track_db_deinit();
|
|||
void track_db_commit();
|
||||
|
||||
/* Called to access the track database. */
|
||||
const database<struct track> *track_db_get();
|
||||
const struct database *track_db_get();
|
||||
|
||||
/* Called to add a track tag to the database. */
|
||||
struct track *track_add(struct library *, const std::string &);
|
||||
|
|
|
@ -19,6 +19,7 @@ struct int_entry : public db_entry {
|
|||
int_entry(unsigned int v) : ie_val(v) {};
|
||||
};
|
||||
|
||||
#define INT_ENTRY(dbe) ((struct int_entry *)dbe)
|
||||
static unsigned int test_free_count = 0;
|
||||
static unsigned int test_setup_count = 0;
|
||||
|
||||
|
@ -102,7 +103,7 @@ static void test_db_entry()
|
|||
|
||||
static void test_init()
|
||||
{
|
||||
database<struct int_entry> db;
|
||||
struct database db;
|
||||
|
||||
db_init(&db, "init.db", false, &int_ops);
|
||||
|
||||
|
@ -119,9 +120,10 @@ static void test_init()
|
|||
|
||||
static void test_stress(unsigned int N)
|
||||
{
|
||||
database<struct int_entry> db;
|
||||
std::vector<struct int_entry *> ptrs;
|
||||
struct int_entry *dbe, *next, rmv(42);
|
||||
struct db_entry *dbe, *next;
|
||||
struct int_entry rmv(42);
|
||||
struct database db;
|
||||
unsigned int i;
|
||||
gchar *key;
|
||||
|
||||
|
@ -136,8 +138,8 @@ static void test_stress(unsigned int N)
|
|||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe->dbe_index, i, i);
|
||||
test_loop_equal(dbe->dbe_key, key, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
ptrs.push_back(dbe);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
ptrs.push_back(INT_ENTRY(dbe));
|
||||
g_free(key);
|
||||
} test_loop_passed();
|
||||
|
||||
|
@ -152,7 +154,7 @@ static void test_stress(unsigned int N)
|
|||
dbe = db_at(&db, i);
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
} test_loop_passed();
|
||||
test_equal(db_at(&db, N), NULL);
|
||||
|
||||
|
@ -163,7 +165,7 @@ static void test_stress(unsigned int N)
|
|||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
g_free(key);
|
||||
} test_loop_passed();
|
||||
key = g_strdup_printf("%u", N);
|
||||
|
@ -176,7 +178,7 @@ static void test_stress(unsigned int N)
|
|||
dbe = db_find(&db, key);
|
||||
test_loop_not_equal(dbe, NULL, i);
|
||||
test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
if (i < N)
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
g_free(key);
|
||||
|
@ -206,10 +208,10 @@ static void test_stress(unsigned int N)
|
|||
test_loop_equal(next, NULL, i);
|
||||
} else {
|
||||
test_loop_not_equal(next, NULL, i);
|
||||
test_loop_equal(next->ie_val, i + 2, i);
|
||||
test_loop_equal(INT_ENTRY(next)->ie_val, i + 2, i);
|
||||
}
|
||||
test_loop_equal(dbe, ptrs.at(i), i);
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
test_loop_equal(db_next(&db, dbe), next, i);
|
||||
i += 2;
|
||||
} test_loop_passed();
|
||||
|
@ -228,9 +230,9 @@ static void test_stress_100K() { test_stress(100000); }
|
|||
|
||||
static void test_save_load()
|
||||
{
|
||||
database<struct int_entry> db1, db2;
|
||||
struct int_entry *dbe, *next;
|
||||
struct db_entry *dbe, *next;
|
||||
const unsigned int N = 10;
|
||||
struct database db1, db2;
|
||||
unsigned int i;
|
||||
|
||||
db_init(&db1, "save_load.db", true, &int_ops);
|
||||
|
@ -245,7 +247,7 @@ static void test_save_load()
|
|||
test_equal(db2.db_size, N);
|
||||
test_equal(db_actual_size(&db2), N);
|
||||
db_for_each(dbe, next, &db2) {
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
i++;
|
||||
} test_loop_passed();
|
||||
|
||||
|
@ -261,7 +263,7 @@ static void test_save_load()
|
|||
|
||||
i = 1;
|
||||
db_for_each(dbe, next, &db2) {
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
i += 2;
|
||||
} test_loop_passed();
|
||||
|
||||
|
@ -284,7 +286,7 @@ static void test_save_load()
|
|||
test_equal(db2.db_size, N);
|
||||
test_equal(db_actual_size(&db2), 2 * N);
|
||||
db_for_each(dbe, next, &db2) {
|
||||
test_loop_equal(dbe->ie_val, i, i);
|
||||
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
|
||||
i += 2;
|
||||
} test_loop_passed();
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ static void test_entry()
|
|||
static void test_stress(unsigned int N)
|
||||
{
|
||||
const struct db_ops *index_ops = test_index_ops();
|
||||
database<index_entry> index;
|
||||
struct database index;
|
||||
index_entry *ie, *ie2;
|
||||
std::string key;
|
||||
unsigned int i;
|
||||
|
@ -91,7 +91,7 @@ static void test_stress(unsigned int N)
|
|||
key = c;
|
||||
for (i = 0; i < N; i++)
|
||||
index_remove(&index, key.c_str(), i);
|
||||
ie = db_find(&index, key.c_str());
|
||||
ie = INDEX_ENTRY(db_find(&index, key.c_str()));
|
||||
test_loop_not_equal(ie, NULL, c - 'a');
|
||||
test_loop_equal(set_size(&ie->ie_set), 0, c - 'a');
|
||||
} test_loop_passed();
|
||||
|
|
|
@ -68,7 +68,7 @@ static void test_album_compare()
|
|||
static void test_album_db()
|
||||
{
|
||||
const struct db_ops *album_ops = test_album_ops();
|
||||
database<struct album> album_db;
|
||||
struct database album_db;
|
||||
struct album *album;
|
||||
|
||||
album_db_init();
|
||||
|
|
|
@ -69,7 +69,7 @@ static void test_artist_compare()
|
|||
static void test_artist_db()
|
||||
{
|
||||
const struct db_ops *artist_ops = test_artist_ops();
|
||||
database<struct artist> artist_db;
|
||||
struct database artist_db;
|
||||
struct artist *artist;
|
||||
|
||||
artist_db_init();
|
||||
|
|
|
@ -69,7 +69,7 @@ static void test_genre_compare()
|
|||
static void test_genere_db()
|
||||
{
|
||||
const struct db_ops *genre_ops = test_genre_ops();
|
||||
database<struct genre> genre_db;
|
||||
struct database genre_db;
|
||||
struct genre *genre;
|
||||
|
||||
genre_db_init();
|
||||
|
|
|
@ -62,8 +62,8 @@ static void test_library()
|
|||
static void test_library_db()
|
||||
{
|
||||
const struct db_ops *library_ops = test_library_ops();
|
||||
database<struct library> library_db;
|
||||
struct library *library, *library2;
|
||||
struct database library_db;
|
||||
|
||||
test_not_equal(library_db_get(), NULL);
|
||||
test_equal(library_db_get()->db_size, 0);
|
||||
|
@ -82,7 +82,7 @@ static void test_library_db()
|
|||
test_equal(library_db.db_size, 1);
|
||||
test_equal(db_actual_size(&library_db), 1);
|
||||
|
||||
library2 = db_at(&library_db, 0);
|
||||
library2 = LIBRARY(db_at(&library_db, 0));
|
||||
test_not_equal(library2, NULL);
|
||||
test_verify_zelda(library2);
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ static void test_track_db()
|
|||
std::string path = "tests/Music/Hyrule Symphony/01 - Title Theme.ogg";
|
||||
struct library *library = library_find("tests/Music");
|
||||
const struct db_ops *track_ops = test_track_ops();
|
||||
database<struct track> track_db;
|
||||
struct database track_db;
|
||||
struct track *track;
|
||||
|
||||
track = track_add(library, path);
|
||||
|
@ -188,7 +188,7 @@ static void test_track_db()
|
|||
track_db_commit();
|
||||
db_load(&track_db);
|
||||
test_equal(track_db.db_size, 1);
|
||||
test_verify_track(db_first(&track_db));
|
||||
test_verify_track(TRACK(db_first(&track_db)));
|
||||
|
||||
track_remove(track);
|
||||
test_equal(track_db_get()->db_size, 0);
|
||||
|
|
Loading…
Reference in New Issue