core/database: Add support for defragmenting databases

Removing items from the database leaves a NULL pointer "hole" that is
never filled in.  This doesn't affect correctness, but it could be
wasteful as items are added and removed.  This patch adds a function to
defragment the database without changing the order of items.

Implements #66: Add support for rebalancing databases
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2016-08-12 13:47:30 -04:00
parent 8d9139aea5
commit 86d7fe43ed
4 changed files with 44 additions and 9 deletions

9
TODO
View File

@ -12,15 +12,6 @@ Future work:
- Save a user's playlist as a group:
- Library defragment:
Ocarina 6.0 will leave holes in the library when tracks are
deleted, potentially leading to fragmentation and larger-than-
needed file sizes. A "defragment" utility can be created to
clean up unused slots.
To help with fixing groups, a mapping of (old values) ->
(new values) should be kept.
- Fix track durations:
Some tracks in my library are tagged with the wrong duration,
so fix them as they are played.

View File

@ -165,6 +165,31 @@ void db_remove(struct database *db, struct db_entry *item)
db_autosave(db);
}
bool db_defrag(struct database *db)
{
struct db_entry *dbe;
unsigned int i, cur;
if (db->db_size == db_actual_size(db))
return false;
for (cur = 0, i = 1; cur < db->db_size; cur++) {
if (DB_ENTRY_AT(db, cur))
continue;
while (i <= cur || !DB_ENTRY_AT(db, i))
i++;
dbe = DB_ENTRY_AT(db, i);
g_ptr_array_index(db->db_entries, i) = NULL;
g_ptr_array_index(db->db_entries, cur) = dbe;
dbe->dbe_index = cur;
}
g_ptr_array_set_size(db->db_entries, db->db_size);
db_autosave(db);
return true;
}
unsigned int db_actual_size(const struct database *db)
{
if (db->db_entries)

View File

@ -123,6 +123,12 @@ 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 *);
/* Returns the database item at the requested index. */
struct db_entry *db_at(const struct database *, unsigned int);

View File

@ -219,6 +219,19 @@ static void test_database(gconstpointer arg)
}
g_assert_cmpuint(i, ==, N + 1);
/* db_defrag(): Remove all NULL pointers */
i = 0;
g_assert_true(db_defrag(&db));
g_assert_cmpuint(db.db_size, ==, N / 2);
g_assert_cmpuint(db_actual_size(&db), ==, N / 2);
db_for_each(dbe, next, &db) {
g_assert_nonnull(dbe);
g_assert_cmpuint(INT_ENTRY(dbe)->ie_val, ==, (i * 2) + 1);
g_assert(dbe == db_at(&db, i));
i++;
}
g_assert_false(db_defrag(&db));
db_deinit(&db);
g_assert_cmpuint(db.db_size, ==, 0);
g_assert_cmpuint(db_actual_size(&db), ==, 0);