core/database: Replace database iterators with db_for_each()

This function is similar to Linux's list_for_each_safe(), and lets you
iterate over a database even if the current item is removed.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2015-09-18 09:37:48 -04:00
parent ffd38bd28b
commit 3ff992cf52
4 changed files with 47 additions and 50 deletions

View File

@ -171,11 +171,11 @@ void tags :: remove_track(Track *track)
void tags :: remove_library_tracks(Library *library)
{
database<Track>::iterator it;
Track *it, *next;
for (it = track_db.begin(); it != track_db.end(); it = track_db.next(it)) {
if ((*it)->library() == library)
db_remove(&track_db, *it);
db_for_each(it, next, &track_db) {
if (it->library() == library)
db_remove(&track_db, it);
}
tags :: commit_track_db();
}

View File

@ -86,10 +86,6 @@ struct database {
std::vector<T *> db_entries;
std::map<const std::string, unsigned int> db_keys;
/** Iterator access for our backing std::vector */
typedef typename std::vector<T *>::iterator iterator;
/** Const iterator access for our backing std::vector */
typedef typename std::vector<T *>::const_iterator const_iterator;
/**
* Initialize a Database using filepath as a location to store data
@ -107,23 +103,6 @@ struct database {
* Deletes all remaining entries in a Database to prevent memory leaks.
*/
~database();
/**
* @return An iterator pointing to the first valid entry in the Database.
* @return Return Database::end() if there are no valid entries.
*/
iterator begin();
/**
* @return An iterator pointing past the end of the database.
*/
iterator end();
/**
* @return An iterator pointing to the next valid item in the database.
*/
iterator next(iterator &);
};
@ -171,6 +150,21 @@ T *db_get(struct database<T> *, const gchar *);
template <class T>
T *db_find(struct database<T> *, const gchar *);
/* Returns the first valid database item. */
template <class T>
T *db_first(const struct database<T> *);
/* Returns the next valid database item. */
template <class T>
T *db_next(const struct database<T> *, T *);
#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))
#include "database.hpp"
#endif /* OCARINA_CORE_DATABASE_H */

View File

@ -17,9 +17,9 @@ database<T> :: database(std::string filepath, bool autosave)
template <class T>
database<T> :: ~database()
{
iterator it;
for (it = begin(); it != end(); it = next(it))
delete (*it);
T *t, *u;
db_for_each(t, u, this)
delete t;
}
template <class T>
@ -114,30 +114,27 @@ unsigned int db_actual_size(const struct database<T> *db)
}
template <class T>
typename database<T>::iterator database<T> :: begin()
T *__db_next(const struct database<T> *db, unsigned int index)
{
if (db_size == 0)
return end();
iterator it = db_entries.begin();
if ( (*it) != NULL )
return it;
return next(it);
for (; index < db->db_entries.size(); index++) {
if (db->db_entries[index])
return db->db_entries[index];
}
return NULL;
}
template <class T>
typename database<T>::iterator database<T> :: end()
T *db_first(const struct database<T> *db)
{
return db_entries.end();
return __db_next(db, 0);
}
template <class T>
typename database<T>::iterator database<T> :: next(iterator &it)
T *db_next(const struct database<T> *db, T *ent)
{
do {
it++;
} while ((it != end()) && (*it) == NULL);
return it;
if (ent)
return __db_next(db, ent->index() + 1);
return NULL;
}
template <class T>

View File

@ -72,9 +72,8 @@ static void test_init()
static void test_stress(unsigned int N)
{
database<struct int_entry> db("stress.db", false);
database<struct int_entry>::iterator it;
std::vector<struct int_entry *> ptrs;
struct int_entry *dbe, rmv(42);
struct int_entry *dbe, *next, rmv(42);
unsigned int i;
gchar *key;
@ -142,12 +141,19 @@ static void test_stress(unsigned int N)
test_equal(db.db_size, N / 2);
test_equal(db_actual_size(&db), N + 1);
/* database.first(), database.next(), database.end() */
/* db_for_each() (db_first() / db_next()) */
i = 1;
for (it = db.begin(); it != db.end(); it = db.next(it)) {
test_loop_not_equal(*it, NULL, i);
test_loop_equal(*it, ptrs.at(i), i);
test_loop_equal((*it)->ie_val, i, i);
db_for_each(dbe, next, &db) {
test_loop_not_equal(dbe, NULL, i);
if (i == (N - 1)) {
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(dbe, ptrs.at(i), i);
test_loop_equal(dbe->ie_val, i, i);
test_loop_equal(db_next(&db, dbe), next, i);
i += 2;
} test_loop_passed();
test_equal(i, N + 1);