core/database: Convert file to C

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2015-11-05 15:56:24 -05:00
parent 8dfe818a7a
commit 877d9ac1d7
10 changed files with 70 additions and 64 deletions

View File

@ -1,4 +1,4 @@
/** /*
* Copyright 2013 (c) Anna Schumaker. * Copyright 2013 (c) Anna Schumaker.
*/ */
#include <core/database.h> #include <core/database.h>

View File

@ -1,23 +1,36 @@
/** /*
* Copyright 2013 (c) Anna Schumaker. * 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_DATABASE_H #ifndef OCARINA_CORE_DATABASE_H
#define OCARINA_CORE_DATABASE_H #define OCARINA_CORE_DATABASE_H
extern "C" {
#include <core/file.h> #include <core/file.h>
}
/**
* The DatabaseEntry class is the base class for storing
* generic data inside a Database.
*/
struct db_entry { struct db_entry {
unsigned int dbe_index; /* The db_entry's position in the database. */ unsigned int dbe_index; /* The db_entry's position in the database. */
gchar *dbe_key; /* The db_entry's hash key. */ gchar *dbe_key; /* The db_entry's hash key. */
void *dbe_data; /* The db_entry's private data. */ void *dbe_data; /* The db_entry's private data. */
}; };
static inline void dbe_init(struct db_entry *dbe, void *data) static inline void dbe_init(struct db_entry *dbe, void *data)
{ {
dbe->dbe_index = 0; dbe->dbe_index = 0;
@ -54,27 +67,9 @@ struct db_ops {
}; };
/**
* Databases are a generic store for information used by Ocarina. Users
* need to inherit from a DatabaseEntry class to properly use a Database.
*
* When writing a Database to disk, Databases need to store their size in
* addition to values and valid bits at each index. For example, let's say
* we have a Database with the following values: { 1, 2, NULL, 4 }. On
* disk this would look like:
*
* 4
* 0 true 1
* 1 true 2
* 2 false
* 3 true 4
* <index> <valid> <data>
*
* The Database class will add a newline after each DatabaseEntry.
*/
struct database { struct database {
unsigned int db_size; /* The database's count of valid entries. */ unsigned int db_size; /* The database's count of valid entries. */
bool db_autosave; /* True if the database saves when changed. */ bool db_autosave; /* The database's automatic save setting. */
struct file db_file; /* The database's associated file object. */ struct file db_file; /* The database's associated file object. */
GPtrArray *db_entries; /* The database's backing array. */ GPtrArray *db_entries; /* The database's backing array. */
GHashTable *db_keys; /* The database's mapping of key -> value. */ GHashTable *db_keys; /* The database's mapping of key -> value. */

View File

@ -4,8 +4,8 @@
#ifndef OCARINA_CORE_INDEX_H #ifndef OCARINA_CORE_INDEX_H
#define OCARINA_CORE_INDEX_H #define OCARINA_CORE_INDEX_H
#include <core/database.h>
extern "C" { extern "C" {
#include <core/database.h>
#include <core/set.h> #include <core/set.h>
} }

View File

@ -4,7 +4,9 @@
#ifndef OCARINA_CORE_TAGS_ALBUM_H #ifndef OCARINA_CORE_TAGS_ALBUM_H
#define OCARINA_CORE_TAGS_ALBUM_H #define OCARINA_CORE_TAGS_ALBUM_H
extern "C" {
#include <core/database.h> #include <core/database.h>
}
#include <string> #include <string>
/** /**

View File

@ -4,7 +4,9 @@
#ifndef OCARINA_CORE_TAGS_ARTIST_H #ifndef OCARINA_CORE_TAGS_ARTIST_H
#define OCARINA_CORE_TAGS_ARTIST_H #define OCARINA_CORE_TAGS_ARTIST_H
extern "C" {
#include <core/database.h> #include <core/database.h>
}
#include <string> #include <string>
/** /**

View File

@ -4,7 +4,9 @@
#ifndef OCARINA_CORE_TAGS_GENRE_H #ifndef OCARINA_CORE_TAGS_GENRE_H
#define OCARINA_CORE_TAGS_GENRE_H #define OCARINA_CORE_TAGS_GENRE_H
extern "C" {
#include <core/database.h> #include <core/database.h>
}
#include <string> #include <string>
/** /**

View File

@ -4,7 +4,9 @@
#ifndef OCARINA_CORE_TAGS_LIBRARY_H #ifndef OCARINA_CORE_TAGS_LIBRARY_H
#define OCARINA_CORE_TAGS_LIBRARY_H #define OCARINA_CORE_TAGS_LIBRARY_H
extern "C" {
#include <core/database.h> #include <core/database.h>
}
#include <string> #include <string>
/** /**

View File

@ -5,9 +5,9 @@
#define OCARINA_CORE_TAGS_TRACK_H #define OCARINA_CORE_TAGS_TRACK_H
extern "C" { extern "C" {
#include <core/database.h>
#include <core/date.h> #include <core/date.h>
} }
#include <core/database.h>
#include <core/tags/artist.h> #include <core/tags/artist.h>
#include <core/tags/album.h> #include <core/tags/album.h>
#include <core/tags/genre.h> #include <core/tags/genre.h>

View File

@ -19,7 +19,7 @@ res += [ CoreTest("random", "random.c") ]
res += [ CoreTest("file", "file.c") ] res += [ CoreTest("file", "file.c") ]
res += [ CoreTest("date", "date.c") ] res += [ CoreTest("date", "date.c") ]
res += [ CoreTest("set", "set.c") ] res += [ CoreTest("set", "set.c") ]
res += [ CoreTest("database", "database.cpp") ] res += [ CoreTest("database", "database.c") ]
res += [ CoreTest("index", "index.cpp") ] res += [ CoreTest("index", "index.cpp") ]
res += [ CoreTest("filter", "filter.cpp") ] res += [ CoreTest("filter", "filter.cpp") ]
res += [ CoreTest("idle", "idle.cpp") ] res += [ CoreTest("idle", "idle.cpp") ]

View File

@ -4,9 +4,7 @@
*/ */
#include <core/database.h> #include <core/database.h>
#include <core/string.h> #include <core/string.h>
#include "test.h" #include <tests/test.h>
#include <vector>
/* /*
* Derive a db_entry for storing integerss * Derive a db_entry for storing integerss
@ -22,7 +20,7 @@ static unsigned int test_setup_count = 0;
static struct int_entry *__int_alloc(unsigned int val) static struct int_entry *__int_alloc(unsigned int val)
{ {
struct int_entry *ent = new int_entry; struct int_entry *ent = g_malloc(sizeof(struct int_entry));
ent->ie_val = val; ent->ie_val = val;
dbe_init(&ent->ie_dbe, ent); dbe_init(&ent->ie_dbe, ent);
return ent; return ent;
@ -38,7 +36,7 @@ static struct db_entry *int_alloc(const gchar *key)
static void int_free(struct db_entry *dbe) static void int_free(struct db_entry *dbe)
{ {
test_free_count++; test_free_count++;
delete INT_ENTRY(dbe); g_free(INT_ENTRY(dbe));
} }
static gchar *int_key(struct db_entry *dbe) static gchar *int_key(struct db_entry *dbe)
@ -64,12 +62,12 @@ static void int_write(struct file *file, struct db_entry *dbe)
} }
static const struct db_ops int_ops = { static const struct db_ops int_ops = {
int_alloc, .dbe_alloc = int_alloc,
int_free, .dbe_free = int_free,
int_key, .dbe_key = int_key,
int_read, .dbe_read = int_read,
int_setup, .dbe_setup = int_setup,
int_write, .dbe_write = int_write,
}; };
@ -80,7 +78,7 @@ static void test_db_entry()
ent = INT_ENTRY(int_ops.dbe_alloc("1")); ent = INT_ENTRY(int_ops.dbe_alloc("1"));
test_equal(ent->ie_dbe.dbe_index, 0); test_equal(ent->ie_dbe.dbe_index, 0);
test_equal(ent->ie_dbe.dbe_data, ent); test_equal((void *)ent->ie_dbe.dbe_data, (void *)ent);
test_equal(ent->ie_val, 1); test_equal(ent->ie_val, 1);
test_str_equal(int_ops.dbe_key(&ent->ie_dbe), "1"); test_str_equal(int_ops.dbe_key(&ent->ie_dbe), "1");
@ -97,7 +95,7 @@ static void test_db_entry()
file_close(&f); file_close(&f);
int_ops.dbe_setup(&ent->ie_dbe); int_ops.dbe_setup(&ent->ie_dbe);
test_equal(ent->ie_dbe.dbe_data, ent); test_equal((void *)ent->ie_dbe.dbe_data, (void *)ent);
test_equal(ent->ie_val, 1); test_equal(ent->ie_val, 1);
test_str_equal(int_ops.dbe_key(&ent->ie_dbe), "1"); test_str_equal(int_ops.dbe_key(&ent->ie_dbe), "1");
test_equal(test_setup_count, 1); test_equal(test_setup_count, 1);
@ -116,19 +114,22 @@ static void test_init()
test_equal(db.db_entries->len, 0); test_equal(db.db_entries->len, 0);
test_equal(g_hash_table_size(db.db_keys), 0); test_equal(g_hash_table_size(db.db_keys), 0);
test_equal(db.db_size, 0); test_equal(db.db_size, 0);
test_equal(db.db_autosave, false); test_equal(db.db_autosave, (bool)false);
test_equal(db.db_file.f_version, 0); test_equal(db.db_file.f_version, 0);
test_equal(db.db_file.f_name, "init.db"); test_equal(db.db_file.f_name, "init.db");
db_deinit(&db); db_deinit(&db);
} }
#define PTRS_AT(db, index) \
g_ptr_array_index(db, index)
static void test_stress(unsigned int N) static void test_stress(unsigned int N)
{ {
std::vector<struct int_entry *> ptrs;
struct db_entry *dbe, *next; struct db_entry *dbe, *next;
struct int_entry rmv; struct int_entry rmv;
struct database db; struct database db;
GPtrArray *ptrs;
unsigned int i; unsigned int i;
gchar *key; gchar *key;
@ -137,21 +138,22 @@ static void test_stress(unsigned int N)
test_free_count = 0; test_free_count = 0;
test_setup_count = 0; test_setup_count = 0;
db_init(&db, "stress.db", false, &int_ops); db_init(&db, "stress.db", false, &int_ops);
ptrs = g_ptr_array_new();
/* db_insert() */ /* db_insert() */
for (i = 0; i < N; i++) { for (i = 0; i < N; i++) {
key = g_strdup_printf("%u", i); key = g_strdup_printf("%u", i);
dbe = db_insert(&db, &__int_alloc(i)->ie_dbe); dbe = db_insert(&db, &__int_alloc(i)->ie_dbe);
test_loop_not_equal(dbe, NULL, i); test_loop_not_equal((void *)dbe, NULL, i);
test_loop_equal(dbe->dbe_index, i, i); test_loop_equal(dbe->dbe_index, i, i);
test_loop_equal(dbe->dbe_key, key, i); test_loop_equal(dbe->dbe_key, key, i);
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i); test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
ptrs.push_back(INT_ENTRY(dbe)); g_ptr_array_add(ptrs, INT_ENTRY(dbe));
g_free(key); g_free(key);
} test_loop_passed(); } test_loop_passed();
dbe = db_insert(&db, NULL); dbe = db_insert(&db, NULL);
test_equal(dbe, NULL); test_equal((void *)dbe, NULL);
test_equal(db.db_size, N); test_equal(db.db_size, N);
test_equal(db_actual_size(&db), N); test_equal(db_actual_size(&db), N);
test_equal(test_setup_count, N); test_equal(test_setup_count, N);
@ -159,35 +161,35 @@ static void test_stress(unsigned int N)
/* db_at() */ /* db_at() */
for (i = 0; i < N; i++) { for (i = 0; i < N; i++) {
dbe = db_at(&db, i); dbe = db_at(&db, i);
test_loop_not_equal(dbe, NULL, i); test_loop_not_equal((void *)dbe, NULL, i);
test_loop_equal(INT_ENTRY(dbe), ptrs.at(i), i); test_loop_equal((void *)INT_ENTRY(dbe), PTRS_AT(ptrs, i), i);
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i); test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
} test_loop_passed(); } test_loop_passed();
test_equal(db_at(&db, N), NULL); test_equal((void *)db_at(&db, N), NULL);
/* db_get() */ /* db_get() */
for (i = 0; i < N; i++) { for (i = 0; i < N; i++) {
key = g_strdup_printf("%u", i); key = g_strdup_printf("%u", i);
dbe = db_get(&db, key); dbe = db_get(&db, key);
test_loop_not_equal(dbe, NULL, i); test_loop_not_equal((void *)dbe, NULL, i);
test_loop_str_equal(int_ops.dbe_key(dbe), key, i); test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
test_loop_equal(INT_ENTRY(dbe), ptrs.at(i), i); test_loop_equal((void *)INT_ENTRY(dbe), PTRS_AT(ptrs, i), i);
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i); test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
g_free(key); g_free(key);
} test_loop_passed(); } test_loop_passed();
key = g_strdup_printf("%u", N); key = g_strdup_printf("%u", N);
test_equal(db_get(&db, key), NULL); test_equal((void *)db_get(&db, key), NULL);
g_free(key); g_free(key);
/* db_find() */ /* db_find() */
for (i = 0; i <= N; i++) { for (i = 0; i <= N; i++) {
key = g_strdup_printf("%u", i); key = g_strdup_printf("%u", i);
dbe = db_find(&db, key); dbe = db_find(&db, key);
test_loop_not_equal(dbe, NULL, i); test_loop_not_equal((void *)dbe, NULL, i);
test_loop_str_equal(int_ops.dbe_key(dbe), key, i); test_loop_str_equal(int_ops.dbe_key(dbe), key, i);
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i); test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
if (i < N) if (i < N)
test_loop_equal(INT_ENTRY(dbe), ptrs.at(i), i); test_loop_equal((void *)INT_ENTRY(dbe), PTRS_AT(ptrs, i), i);
g_free(key); g_free(key);
} test_loop_passed(); } test_loop_passed();
test_equal(db.db_size, N + 1); test_equal(db.db_size, N + 1);
@ -198,8 +200,8 @@ static void test_stress(unsigned int N)
key = g_strdup_printf("%u", i); key = g_strdup_printf("%u", i);
dbe = db_get(&db, key); dbe = db_get(&db, key);
db_remove(&db, dbe); db_remove(&db, dbe);
test_loop_equal(INT_ENTRY(db_at(&db, i)), NULL, i); test_loop_equal((void *)INT_ENTRY(db_at(&db, i)), NULL, i);
test_loop_equal(INT_ENTRY(db_get(&db, key)), NULL, i); test_loop_equal((void *)INT_ENTRY(db_get(&db, key)), NULL, i);
g_free(key); g_free(key);
} test_loop_passed(); } test_loop_passed();
db_remove(&db, &rmv.ie_dbe); db_remove(&db, &rmv.ie_dbe);
@ -210,24 +212,25 @@ static void test_stress(unsigned int N)
/* db_for_each() (db_first() / db_next()) */ /* db_for_each() (db_first() / db_next()) */
i = 1; i = 1;
db_for_each(dbe, next, &db) { db_for_each(dbe, next, &db) {
test_loop_not_equal(dbe, NULL, i); test_loop_not_equal((void *)dbe, NULL, i);
if (i == (N - 1)) { if (i == (N - 1)) {
test_loop_equal(next, NULL, i); test_loop_equal((void *)next, NULL, i);
} else { } else {
test_loop_not_equal(next, NULL, i); test_loop_not_equal((void *)next, NULL, i);
test_loop_equal(INT_ENTRY(next)->ie_val, i + 2, i); test_loop_equal(INT_ENTRY(next)->ie_val, i + 2, i);
} }
test_loop_equal(INT_ENTRY(dbe), ptrs.at(i), i); test_loop_equal((void *)INT_ENTRY(dbe), PTRS_AT(ptrs, i), i);
test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i); test_loop_equal(INT_ENTRY(dbe)->ie_val, i, i);
test_loop_equal(db_next(&db, dbe), next, i); test_loop_equal((void *)db_next(&db, dbe), (void *)next, i);
i += 2; i += 2;
} test_loop_passed(); } test_loop_passed();
test_equal(i, N + 1); test_equal(i, N + 1);
db_deinit(&db); db_deinit(&db);
g_ptr_array_free(ptrs, true);
test_equal(db.db_size, 0); test_equal(db.db_size, 0);
test_equal(db_actual_size(&db), 0); test_equal(db_actual_size(&db), 0);
test_equal(db_first(&db), NULL); test_equal((void *)db_first(&db), NULL);
test_equal(test_free_count, N + 1); test_equal(test_free_count, N + 1);
} }