core/database: Convert file to C
Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
parent
8dfe818a7a
commit
877d9ac1d7
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* Copyright 2013 (c) Anna Schumaker.
|
||||
*/
|
||||
#include <core/database.h>
|
|
@ -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. */
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#ifndef OCARINA_CORE_TAGS_ALBUM_H
|
||||
#define OCARINA_CORE_TAGS_ALBUM_H
|
||||
|
||||
extern "C" {
|
||||
#include <core/database.h>
|
||||
}
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#ifndef OCARINA_CORE_TAGS_ARTIST_H
|
||||
#define OCARINA_CORE_TAGS_ARTIST_H
|
||||
|
||||
extern "C" {
|
||||
#include <core/database.h>
|
||||
}
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#ifndef OCARINA_CORE_TAGS_GENRE_H
|
||||
#define OCARINA_CORE_TAGS_GENRE_H
|
||||
|
||||
extern "C" {
|
||||
#include <core/database.h>
|
||||
}
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
#ifndef OCARINA_CORE_TAGS_LIBRARY_H
|
||||
#define OCARINA_CORE_TAGS_LIBRARY_H
|
||||
|
||||
extern "C" {
|
||||
#include <core/database.h>
|
||||
}
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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") ]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue