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:
Jordan Bancino 2022-11-21 20:31:37 +00:00
parent a0304f4062
commit 789baf33a1
5 changed files with 244 additions and 67 deletions

View file

@ -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
View file

@ -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)
{ {

View file

@ -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;
} }

View file

@ -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 */

View file

@ -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 *);