forked from lda/telodendria
Do some Db work
This commit is contained in:
parent
9494016d2d
commit
46cc1df385
5 changed files with 238 additions and 10 deletions
195
src/Db.c
195
src/Db.c
|
@ -25,12 +25,13 @@
|
||||||
|
|
||||||
#include <Memory.h>
|
#include <Memory.h>
|
||||||
#include <Json.h>
|
#include <Json.h>
|
||||||
|
#include <Util.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
struct Db
|
struct Db
|
||||||
{
|
{
|
||||||
const char *dir;
|
char *dir;
|
||||||
size_t cacheSize;
|
size_t cacheSize;
|
||||||
size_t maxCache;
|
size_t maxCache;
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
|
@ -44,7 +45,10 @@ struct DbRef
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
|
|
||||||
unsigned long ts;
|
unsigned long ts;
|
||||||
char *file;
|
size_t size;
|
||||||
|
|
||||||
|
char *prefix;
|
||||||
|
char *key;
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t DbComputeSize(HashMap *);
|
static ssize_t DbComputeSize(HashMap *);
|
||||||
|
@ -147,8 +151,32 @@ DbComputeSize(HashMap * json)
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
DbHashKey(char *prefix, char *key)
|
||||||
|
{
|
||||||
|
return UtilStringConcat(prefix, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
DbFileName(Db * db, char *prefix, char *key)
|
||||||
|
{
|
||||||
|
char *tmp = UtilStringConcat(prefix, "/");
|
||||||
|
char *tmp2 = UtilStringConcat(tmp, key);
|
||||||
|
char *tmp3 = UtilStringConcat(tmp2, ".json");
|
||||||
|
|
||||||
|
char *tmp4 = UtilStringConcat(db->dir, "/");
|
||||||
|
char *tmp5 = UtilStringConcat(tmp4, tmp3);
|
||||||
|
|
||||||
|
Free(tmp);
|
||||||
|
Free(tmp2);
|
||||||
|
Free(tmp3);
|
||||||
|
Free(tmp4);
|
||||||
|
|
||||||
|
return tmp5;
|
||||||
|
}
|
||||||
|
|
||||||
Db *
|
Db *
|
||||||
DbOpen(const char *dir, size_t cache)
|
DbOpen(char *dir, size_t cache)
|
||||||
{
|
{
|
||||||
Db *db;
|
Db *db;
|
||||||
|
|
||||||
|
@ -176,6 +204,10 @@ DbOpen(const char *dir, size_t cache)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db->cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
@ -183,13 +215,29 @@ DbOpen(const char *dir, size_t cache)
|
||||||
void
|
void
|
||||||
DbClose(Db * db)
|
DbClose(Db * db)
|
||||||
{
|
{
|
||||||
|
char *key;
|
||||||
|
DbRef *val;
|
||||||
|
|
||||||
if (!db)
|
if (!db)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_destroy(&db->lock);
|
pthread_mutex_destroy(&db->lock);
|
||||||
|
|
||||||
|
if (db->cache)
|
||||||
|
{
|
||||||
|
while (HashMapIterate(db->cache, &key, (void **) &val))
|
||||||
|
{
|
||||||
|
Free(key);
|
||||||
|
JsonFree(val->json);
|
||||||
|
Free(val->prefix);
|
||||||
|
Free(val->key);
|
||||||
|
pthread_mutex_destroy(&val->lock);
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
HashMapFree(db->cache);
|
HashMapFree(db->cache);
|
||||||
|
}
|
||||||
|
|
||||||
Free(db);
|
Free(db);
|
||||||
}
|
}
|
||||||
|
@ -197,31 +245,147 @@ DbClose(Db * db)
|
||||||
DbRef *
|
DbRef *
|
||||||
DbCreate(Db * db, char *prefix, char *key)
|
DbCreate(Db * db, char *prefix, char *key)
|
||||||
{
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *file;
|
||||||
|
|
||||||
if (!db || !prefix || !key)
|
if (!db || !prefix || !key)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file = DbFileName(db, prefix, key);
|
||||||
|
|
||||||
|
if (UtilLastModified(file))
|
||||||
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DbLock(db, prefix, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
DbRef *
|
DbRef *
|
||||||
DbLock(Db * db, char *prefix, char *key)
|
DbLock(Db * db, char *prefix, char *key)
|
||||||
{
|
{
|
||||||
|
char *file;
|
||||||
|
char *hash;
|
||||||
|
DbRef *ref;
|
||||||
|
|
||||||
if (!db || !prefix || !key)
|
if (!db || !prefix || !key)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref = NULL;
|
||||||
|
hash = NULL;
|
||||||
|
|
||||||
pthread_mutex_lock(&db->lock);
|
pthread_mutex_lock(&db->lock);
|
||||||
|
|
||||||
|
/* Check if the item is in the cache */
|
||||||
|
if (db->cache)
|
||||||
|
{
|
||||||
|
hash = DbHashKey(prefix, key);
|
||||||
|
ref = HashMapGet(db->cache, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
file = DbFileName(db, prefix, key);
|
||||||
|
|
||||||
|
if (ref) /* In cache */
|
||||||
|
{
|
||||||
|
unsigned long diskTs = UtilLastModified(file);
|
||||||
|
|
||||||
|
if (diskTs > ref->ts)
|
||||||
|
{
|
||||||
|
/* File was modified on disk since it was cached */
|
||||||
|
FILE *fp = fopen(file, "r");
|
||||||
|
HashMap *json;
|
||||||
|
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
ref = NULL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
json = JsonDecode(fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!json)
|
||||||
|
{
|
||||||
|
ref = NULL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonFree(ref->json);
|
||||||
|
ref->json = json;
|
||||||
|
ref->ts = diskTs;
|
||||||
|
ref->size = DbComputeSize(ref->json);
|
||||||
|
ref->prefix = UtilStringDuplicate(prefix);
|
||||||
|
ref->key = UtilStringDuplicate(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Not in cache; load from disk */
|
||||||
|
FILE *fp = fopen(file, "r");
|
||||||
|
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
ref = NULL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = Malloc(sizeof(DbRef));
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
fclose(fp);
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref->json = JsonDecode(fp);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!ref->json)
|
||||||
|
{
|
||||||
|
Free(ref);
|
||||||
|
ref = NULL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_init(&ref->lock, NULL);
|
||||||
|
ref->ts = UtilServerTs();
|
||||||
|
ref->size = DbComputeSize(ref->json);
|
||||||
|
ref->prefix = prefix;
|
||||||
|
ref->key = key;
|
||||||
|
|
||||||
|
/* If cache is enabled, cache this ref */
|
||||||
|
if (db->cache)
|
||||||
|
{
|
||||||
|
HashMapSet(db->cache, hash, ref);
|
||||||
|
db->cacheSize += ref->size;
|
||||||
|
|
||||||
|
/* Cache is full; evict old items */
|
||||||
|
if (db->cacheSize > db->maxCache)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&db->lock);
|
pthread_mutex_unlock(&db->lock);
|
||||||
return NULL;
|
pthread_mutex_lock(&ref->lock);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
Free(file);
|
||||||
|
Free(hash);
|
||||||
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
DbUnlock(Db * db, DbRef * ref)
|
DbUnlock(Db * db, DbRef * ref)
|
||||||
{
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *file;
|
||||||
|
|
||||||
if (!db || !ref)
|
if (!db || !ref)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -229,9 +393,28 @@ DbUnlock(Db * db, DbRef * ref)
|
||||||
|
|
||||||
pthread_mutex_lock(&db->lock);
|
pthread_mutex_lock(&db->lock);
|
||||||
|
|
||||||
pthread_mutex_unlock(&db->lock);
|
file = DbFileName(db, ref->prefix, ref->key);
|
||||||
|
fp = fopen(file, "w");
|
||||||
|
if (!fp)
|
||||||
|
{ /* TODO: This seems dangerous */
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonEncode(ref->json, fp);
|
||||||
|
fflush(fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&ref->lock);
|
||||||
|
|
||||||
|
/* No cache, free it now */
|
||||||
|
if (!db->cache)
|
||||||
|
{
|
||||||
|
JsonFree(ref->json);
|
||||||
|
Free(ref->prefix);
|
||||||
|
Free(ref->key);
|
||||||
|
pthread_mutex_destroy(&ref->lock);
|
||||||
|
Free(ref);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap *
|
HashMap *
|
||||||
|
|
39
src/Util.c
39
src/Util.c
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
unsigned long
|
unsigned long
|
||||||
|
@ -48,6 +49,23 @@ UtilServerTs(void)
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
UtilLastModified(char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
unsigned long ts;
|
||||||
|
|
||||||
|
if (stat(path, &st) == 0)
|
||||||
|
{
|
||||||
|
ts = (st.st_mtim.tv_sec * 1000) + (st.st_mtim.tv_nsec / 1000000);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
UtilUtf8Encode(unsigned long utf8)
|
UtilUtf8Encode(unsigned long utf8)
|
||||||
{
|
{
|
||||||
|
@ -115,6 +133,27 @@ UtilStringDuplicate(char *inStr)
|
||||||
return outStr;
|
return outStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
UtilStringConcat(char *str1, char *str2)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
size_t str1Len = strlen(str1);
|
||||||
|
size_t str2Len = strlen(str2);
|
||||||
|
|
||||||
|
ret = Malloc(str1Len + str2Len + 1);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(ret, str1);
|
||||||
|
strcpy(ret + str1Len, str2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
UtilSleepMillis(long ms)
|
UtilSleepMillis(long ms)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,7 +32,7 @@ typedef struct Db Db;
|
||||||
typedef struct DbRef DbRef;
|
typedef struct DbRef DbRef;
|
||||||
|
|
||||||
extern Db *
|
extern Db *
|
||||||
DbOpen(const char *, size_t);
|
DbOpen(char *, size_t);
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
DbClose(Db *);
|
DbClose(Db *);
|
||||||
|
|
|
@ -146,7 +146,7 @@ extern JsonValue *
|
||||||
JsonValueString(char *string);
|
JsonValueString(char *string);
|
||||||
|
|
||||||
extern char *
|
extern char *
|
||||||
JsonValueAsString(JsonValue *);
|
JsonValueAsString(JsonValue *);
|
||||||
|
|
||||||
extern JsonValue *
|
extern JsonValue *
|
||||||
JsonValueInteger(long integer);
|
JsonValueInteger(long integer);
|
||||||
|
|
|
@ -57,6 +57,9 @@
|
||||||
extern unsigned long
|
extern unsigned long
|
||||||
UtilServerTs(void);
|
UtilServerTs(void);
|
||||||
|
|
||||||
|
extern unsigned long
|
||||||
|
UtilLastModified(char *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encode a single UTF-8 codepoint as a string buffer containing
|
* Encode a single UTF-8 codepoint as a string buffer containing
|
||||||
* between 1 and 4 bytes. The string buffer is allocated on the heap,
|
* between 1 and 4 bytes. The string buffer is allocated on the heap,
|
||||||
|
@ -88,6 +91,9 @@ extern char *
|
||||||
extern char *
|
extern char *
|
||||||
UtilStringDuplicate(char *);
|
UtilStringDuplicate(char *);
|
||||||
|
|
||||||
|
extern char *
|
||||||
|
UtilStringConcat(char *, char *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sleep for the given number of milliseconds. This is a simple wrapper
|
* Sleep for the given number of milliseconds. This is a simple wrapper
|
||||||
* for nanosleep() that makes its usage much easier.
|
* for nanosleep() that makes its usage much easier.
|
||||||
|
|
Loading…
Reference in a new issue