diff --git a/TODO b/TODO index fde09f47..72479f8b 100644 --- a/TODO +++ b/TODO @@ -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. diff --git a/core/database.c b/core/database.c index 051ec47e..20fe3474 100644 --- a/core/database.c +++ b/core/database.c @@ -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) diff --git a/include/core/database.h b/include/core/database.h index 7b21fa54..0672bc1f 100644 --- a/include/core/database.h +++ b/include/core/database.h @@ -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); diff --git a/tests/core/database.c b/tests/core/database.c index c781fa9a..691674b2 100644 --- a/tests/core/database.c +++ b/tests/core/database.c @@ -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);