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.
*/
#include <core/database.h>

View File

@ -1,23 +1,36 @@
/**
/*
* 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
#define OCARINA_CORE_DATABASE_H
extern "C" {
#include <core/file.h>
}
/**
* The DatabaseEntry class is the base class for storing
* generic data inside a Database.
*/
struct db_entry {
unsigned int dbe_index; /* The db_entry's position in the database. */
gchar *dbe_key; /* The db_entry's hash key. */
void *dbe_data; /* The db_entry's private data. */
};
static inline void dbe_init(struct db_entry *dbe, void *data)
{
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 {
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. */
GPtrArray *db_entries; /* The database's backing array. */
GHashTable *db_keys; /* The database's mapping of key -> value. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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