forked from lda/telodendria
Make Db interfaces take varargs.
This makes it much more flexible, at the expense of making it a little more fragile. I can think of a number of scenarios where we'll have paths that have variables in multiple spots, and I don't want to do sprintf() magic every time I need to access an object at one of those paths.
This commit is contained in:
parent
a0304f4062
commit
789baf33a1
5 changed files with 244 additions and 67 deletions
64
src/Array.c
64
src/Array.c
|
@ -247,3 +247,67 @@ ArraySort(Array * array, int (*compare) (void *, void *))
|
||||||
}
|
}
|
||||||
ArrayQuickSort(array, 0, array->size, compare);
|
ArrayQuickSort(array, 0, array->size, compare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Even though the following operations could be done using only the
|
||||||
|
* public Array API defined above, I opted for low-level struct
|
||||||
|
* manipulation because it allows much more efficient copying; we only
|
||||||
|
* allocate what we for sure need upfront, and don't have to
|
||||||
|
* re-allocate during the operation. */
|
||||||
|
|
||||||
|
Array *
|
||||||
|
ArrayFromVarArgs(size_t n, va_list ap)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
Array *arr = Malloc(sizeof(Array));
|
||||||
|
|
||||||
|
if (!arr)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr->size = n;
|
||||||
|
arr->allocated = n;
|
||||||
|
arr->entries = Malloc(sizeof(void *) * arr->allocated);
|
||||||
|
|
||||||
|
if (!arr->entries)
|
||||||
|
{
|
||||||
|
Free(arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
arr->entries[i] = va_arg(ap, void *);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array *
|
||||||
|
ArrayDuplicate(Array * arr)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
Array *arr2 = Malloc(sizeof(Array));
|
||||||
|
|
||||||
|
if (!arr2)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2->size = arr->size;
|
||||||
|
arr2->allocated = arr->size;
|
||||||
|
arr2->entries = Malloc(sizeof(void *) * arr->allocated);
|
||||||
|
|
||||||
|
if (!arr2->entries)
|
||||||
|
{
|
||||||
|
Free(arr2);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < arr2->size; i++)
|
||||||
|
{
|
||||||
|
arr2->entries[i] = arr->entries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr2;
|
||||||
|
}
|
||||||
|
|
209
src/Db.c
209
src/Db.c
|
@ -51,8 +51,7 @@ struct DbRef
|
||||||
unsigned long ts;
|
unsigned long ts;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
char *prefix;
|
Array *name;
|
||||||
char *key;
|
|
||||||
|
|
||||||
DbRef *prev;
|
DbRef *prev;
|
||||||
DbRef *next;
|
DbRef *next;
|
||||||
|
@ -161,27 +160,64 @@ DbComputeSize(HashMap * json)
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
DbHashKey(char *prefix, char *key)
|
DbHashKey(Array * args)
|
||||||
{
|
{
|
||||||
return UtilStringConcat(prefix, key);
|
size_t i;
|
||||||
|
char *str = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(args); i++)
|
||||||
|
{
|
||||||
|
char *tmp = UtilStringConcat(str, ArrayGet(args, i));
|
||||||
|
|
||||||
|
Free(str);
|
||||||
|
str = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
DbFileName(Db * db, char *prefix, char *key)
|
DbDirName(Db * db, Array * args)
|
||||||
{
|
{
|
||||||
char *tmp = UtilStringConcat(prefix, "/");
|
size_t i;
|
||||||
char *tmp2 = UtilStringConcat(tmp, key);
|
char *str = UtilStringConcat(db->dir, "/");
|
||||||
char *tmp3 = UtilStringConcat(tmp2, ".json");
|
|
||||||
|
|
||||||
char *tmp4 = UtilStringConcat(db->dir, "/");
|
for (i = 0; i < ArraySize(args) - 1; i++)
|
||||||
char *tmp5 = UtilStringConcat(tmp4, tmp3);
|
{
|
||||||
|
char *tmp, *tmp2;
|
||||||
|
|
||||||
Free(tmp);
|
tmp = UtilStringConcat(str, ArrayGet(args, i));
|
||||||
Free(tmp2);
|
tmp2 = UtilStringConcat(tmp, "/");
|
||||||
Free(tmp3);
|
|
||||||
Free(tmp4);
|
|
||||||
|
|
||||||
return tmp5;
|
Free(str);
|
||||||
|
Free(tmp);
|
||||||
|
|
||||||
|
str = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
DbFileName(Db * db, Array * args)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
char *str = UtilStringConcat(db->dir, "/");
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(args); i++)
|
||||||
|
{
|
||||||
|
char *tmp, *tmp2;
|
||||||
|
|
||||||
|
tmp = UtilStringConcat(str, ArrayGet(args, i));
|
||||||
|
tmp2 = UtilStringConcat(tmp, (i < ArraySize(args) - 1) ? "/" : ".json");
|
||||||
|
|
||||||
|
Free(str);
|
||||||
|
Free(tmp);
|
||||||
|
|
||||||
|
str = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -192,7 +228,7 @@ DbCacheEvict(Db * db)
|
||||||
|
|
||||||
while (ref && db->cacheSize > db->maxCache)
|
while (ref && db->cacheSize > db->maxCache)
|
||||||
{
|
{
|
||||||
char *hash = DbHashKey(ref->prefix, ref->key);
|
char *hash = DbHashKey(ref->name);
|
||||||
|
|
||||||
if (pthread_mutex_trylock(&ref->lock) != 0)
|
if (pthread_mutex_trylock(&ref->lock) != 0)
|
||||||
{
|
{
|
||||||
|
@ -205,12 +241,11 @@ DbCacheEvict(Db * db)
|
||||||
pthread_mutex_unlock(&ref->lock);
|
pthread_mutex_unlock(&ref->lock);
|
||||||
pthread_mutex_destroy(&ref->lock);
|
pthread_mutex_destroy(&ref->lock);
|
||||||
|
|
||||||
hash = DbHashKey(ref->prefix, ref->key);
|
hash = DbHashKey(ref->name);
|
||||||
HashMapDelete(db->cache, hash);
|
HashMapDelete(db->cache, hash);
|
||||||
Free(hash);
|
Free(hash);
|
||||||
|
|
||||||
Free(ref->prefix);
|
ArrayFree(ref->name);
|
||||||
Free(ref->key);
|
|
||||||
|
|
||||||
db->cacheSize -= ref->size;
|
db->cacheSize -= ref->size;
|
||||||
|
|
||||||
|
@ -281,8 +316,7 @@ DbClose(Db * db)
|
||||||
{
|
{
|
||||||
Free(key);
|
Free(key);
|
||||||
JsonFree(val->json);
|
JsonFree(val->json);
|
||||||
Free(val->prefix);
|
ArrayFree(val->name);
|
||||||
Free(val->key);
|
|
||||||
pthread_mutex_destroy(&val->lock);
|
pthread_mutex_destroy(&val->lock);
|
||||||
Free(val);
|
Free(val);
|
||||||
}
|
}
|
||||||
|
@ -291,41 +325,8 @@ DbClose(Db * db)
|
||||||
Free(db);
|
Free(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
DbRef *
|
static DbRef *
|
||||||
DbCreate(Db * db, char *prefix, char *key)
|
DbLockFromArr(Db * db, Array * args)
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
char *file;
|
|
||||||
|
|
||||||
if (!db || !prefix || !key)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
file = DbFileName(db, prefix, key);
|
|
||||||
|
|
||||||
if (UtilLastModified(file) || UtilMkdir(prefix, 0750) < 0)
|
|
||||||
{
|
|
||||||
Free(file);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fp = fopen(file, "w");
|
|
||||||
Free(file);
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(fp, "{}");
|
|
||||||
fflush(fp);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
return DbLock(db, prefix, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
DbRef *
|
|
||||||
DbLock(Db * db, char *prefix, char *key)
|
|
||||||
{
|
{
|
||||||
char *file;
|
char *file;
|
||||||
char *hash;
|
char *hash;
|
||||||
|
@ -333,7 +334,7 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
struct flock lock;
|
struct flock lock;
|
||||||
|
|
||||||
if (!db || !prefix || !key)
|
if (!db || !args)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -344,9 +345,12 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
pthread_mutex_lock(&db->lock);
|
pthread_mutex_lock(&db->lock);
|
||||||
|
|
||||||
/* Check if the item is in the cache */
|
/* Check if the item is in the cache */
|
||||||
hash = DbHashKey(prefix, key);
|
hash = DbHashKey(args);
|
||||||
ref = HashMapGet(db->cache, hash);
|
ref = HashMapGet(db->cache, hash);
|
||||||
file = DbFileName(db, prefix, key);
|
file = DbFileName(db, args);
|
||||||
|
|
||||||
|
printf("Object hash: %s\n", hash);
|
||||||
|
printf("File name: %s\n", file);
|
||||||
|
|
||||||
/* Open the file for reading and writing so we can lock it */
|
/* Open the file for reading and writing so we can lock it */
|
||||||
fp = fopen(file, "r+");
|
fp = fopen(file, "r+");
|
||||||
|
@ -443,8 +447,7 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
|
|
||||||
ref->ts = UtilServerTs();
|
ref->ts = UtilServerTs();
|
||||||
ref->size = DbComputeSize(ref->json);
|
ref->size = DbComputeSize(ref->json);
|
||||||
ref->prefix = UtilStringDuplicate(prefix);
|
ref->name = ArrayDuplicate(args);
|
||||||
ref->key = UtilStringDuplicate(key);
|
|
||||||
|
|
||||||
HashMapSet(db->cache, hash, ref);
|
HashMapSet(db->cache, hash, ref);
|
||||||
db->cacheSize += ref->size;
|
db->cacheSize += ref->size;
|
||||||
|
@ -467,6 +470,90 @@ finish:
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DbRef *
|
||||||
|
DbCreate(Db * db, size_t nArgs,...)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *file;
|
||||||
|
char *dir;
|
||||||
|
va_list ap;
|
||||||
|
Array *args;
|
||||||
|
|
||||||
|
if (!db)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_start(ap, nArgs);
|
||||||
|
args = ArrayFromVarArgs(nArgs, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (!args)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = DbFileName(db, args);
|
||||||
|
|
||||||
|
if (UtilLastModified(file) || UtilMkdir(dir, 0750) < 0)
|
||||||
|
{
|
||||||
|
Free(file);
|
||||||
|
ArrayFree(args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = DbDirName(db, args);
|
||||||
|
if (UtilMkdir(dir, 0750) < 0)
|
||||||
|
{
|
||||||
|
Free(file);
|
||||||
|
ArrayFree(args);
|
||||||
|
Free(dir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = fopen(file, "w");
|
||||||
|
Free(file);
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
ArrayFree(args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fp, "{}");
|
||||||
|
fflush(fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return DbLockFromArr(db, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
DbRef *
|
||||||
|
DbLock(Db * db, size_t nArgs,...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
Array *args;
|
||||||
|
DbRef *ret;
|
||||||
|
|
||||||
|
va_start(ap, nArgs);
|
||||||
|
args = ArrayFromVarArgs(nArgs, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
printf("Enter DbLock()\n");
|
||||||
|
|
||||||
|
if (!args)
|
||||||
|
{
|
||||||
|
printf("Failed to parse varargs\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = DbLockFromArr(db, args);
|
||||||
|
|
||||||
|
ArrayFree(args);
|
||||||
|
|
||||||
|
printf("Exit DbLock()\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
DbUnlock(Db * db, DbRef * ref)
|
DbUnlock(Db * db, DbRef * ref)
|
||||||
{
|
{
|
||||||
|
|
27
src/Util.c
27
src/Util.c
|
@ -208,9 +208,10 @@ char *
|
||||||
UtilStringConcat(char *str1, char *str2)
|
UtilStringConcat(char *str1, char *str2)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
|
size_t str1Len, str2Len;
|
||||||
|
|
||||||
size_t str1Len = strlen(str1);
|
str1Len = str1 ? strlen(str1) : 0;
|
||||||
size_t str2Len = strlen(str2);
|
str2Len = str2 ? strlen(str2) : 0;
|
||||||
|
|
||||||
ret = Malloc(str1Len + str2Len + 1);
|
ret = Malloc(str1Len + str2Len + 1);
|
||||||
|
|
||||||
|
@ -219,8 +220,26 @@ UtilStringConcat(char *str1, char *str2)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(ret, str1);
|
if (str1 && str2)
|
||||||
strcpy(ret + str1Len, str2);
|
{
|
||||||
|
strcpy(ret, str1);
|
||||||
|
strcpy(ret + str1Len, str2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (str1)
|
||||||
|
{
|
||||||
|
strcpy(ret, str1);
|
||||||
|
}
|
||||||
|
else if (str2)
|
||||||
|
{
|
||||||
|
strcpy(ret, str2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
strcpy(ret, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define TELODENDRIA_ARRAY_H
|
#define TELODENDRIA_ARRAY_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
typedef struct Array Array;
|
typedef struct Array Array;
|
||||||
|
|
||||||
|
@ -56,4 +57,10 @@ extern void
|
||||||
extern int
|
extern int
|
||||||
ArrayTrim(Array *);
|
ArrayTrim(Array *);
|
||||||
|
|
||||||
|
extern Array *
|
||||||
|
ArrayFromVarArgs(size_t, va_list);
|
||||||
|
|
||||||
|
extern Array *
|
||||||
|
ArrayDuplicate(Array *);
|
||||||
|
|
||||||
#endif /* TELODENDRIA_ARRAY_H */
|
#endif /* TELODENDRIA_ARRAY_H */
|
||||||
|
|
|
@ -42,10 +42,10 @@ extern void
|
||||||
DbClose(Db *);
|
DbClose(Db *);
|
||||||
|
|
||||||
extern DbRef *
|
extern DbRef *
|
||||||
DbCreate(Db *, char *, char *);
|
DbCreate(Db *, size_t,...);
|
||||||
|
|
||||||
extern DbRef *
|
extern DbRef *
|
||||||
DbLock(Db *, char *, char *);
|
DbLock(Db *, size_t,...);
|
||||||
|
|
||||||
extern int
|
extern int
|
||||||
DbUnlock(Db *, DbRef *);
|
DbUnlock(Db *, DbRef *);
|
||||||
|
|
Loading…
Reference in a new issue