From 46cc1df385b93b2ebbeb45ea5530564a34755d23 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Tue, 15 Nov 2022 18:20:05 +0000 Subject: [PATCH] Do some Db work --- src/Db.c | 199 +++++++++++++++++++++++++++++++++++++++++++-- src/Util.c | 39 +++++++++ src/include/Db.h | 2 +- src/include/Json.h | 2 +- src/include/Util.h | 6 ++ 5 files changed, 238 insertions(+), 10 deletions(-) diff --git a/src/Db.c b/src/Db.c index 9699e23..173ed97 100644 --- a/src/Db.c +++ b/src/Db.c @@ -25,12 +25,13 @@ #include #include +#include #include struct Db { - const char *dir; + char *dir; size_t cacheSize; size_t maxCache; pthread_mutex_t lock; @@ -44,7 +45,10 @@ struct DbRef pthread_mutex_t lock; unsigned long ts; - char *file; + size_t size; + + char *prefix; + char *key; }; static ssize_t DbComputeSize(HashMap *); @@ -147,8 +151,32 @@ DbComputeSize(HashMap * json) 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 * -DbOpen(const char *dir, size_t cache) +DbOpen(char *dir, size_t cache) { Db *db; @@ -176,6 +204,10 @@ DbOpen(const char *dir, size_t cache) return NULL; } } + else + { + db->cache = NULL; + } return db; } @@ -183,13 +215,29 @@ DbOpen(const char *dir, size_t cache) void DbClose(Db * db) { + char *key; + DbRef *val; + if (!db) { return; } pthread_mutex_destroy(&db->lock); - HashMapFree(db->cache); + + 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); + } Free(db); } @@ -197,31 +245,147 @@ DbClose(Db * db) DbRef * DbCreate(Db * db, char *prefix, char *key) { + FILE *fp; + char *file; + if (!db || !prefix || !key) { return NULL; } - return NULL; + file = DbFileName(db, prefix, key); + + if (UtilLastModified(file)) + { + return NULL; + } + + return DbLock(db, prefix, key); } DbRef * DbLock(Db * db, char *prefix, char *key) { + char *file; + char *hash; + DbRef *ref; + if (!db || !prefix || !key) { return NULL; } + ref = NULL; + hash = NULL; + 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); - return NULL; + pthread_mutex_lock(&ref->lock); + +finish: + Free(file); + Free(hash); + return ref; } void DbUnlock(Db * db, DbRef * ref) { + FILE *fp; + char *file; + if (!db || !ref) { return; @@ -229,9 +393,28 @@ DbUnlock(Db * db, DbRef * ref) 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 * diff --git a/src/Util.c b/src/Util.c index 40e0128..3b10a17 100644 --- a/src/Util.c +++ b/src/Util.c @@ -34,6 +34,7 @@ #include #include +#include #include unsigned long @@ -48,6 +49,23 @@ UtilServerTs(void) 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 * UtilUtf8Encode(unsigned long utf8) { @@ -115,6 +133,27 @@ UtilStringDuplicate(char *inStr) 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 UtilSleepMillis(long ms) { diff --git a/src/include/Db.h b/src/include/Db.h index 142c2c4..97a72bf 100644 --- a/src/include/Db.h +++ b/src/include/Db.h @@ -32,7 +32,7 @@ typedef struct Db Db; typedef struct DbRef DbRef; extern Db * - DbOpen(const char *, size_t); + DbOpen(char *, size_t); extern void DbClose(Db *); diff --git a/src/include/Json.h b/src/include/Json.h index 35797ec..144eda6 100644 --- a/src/include/Json.h +++ b/src/include/Json.h @@ -146,7 +146,7 @@ extern JsonValue * JsonValueString(char *string); extern char * -JsonValueAsString(JsonValue *); + JsonValueAsString(JsonValue *); extern JsonValue * JsonValueInteger(long integer); diff --git a/src/include/Util.h b/src/include/Util.h index 61a5140..8237940 100644 --- a/src/include/Util.h +++ b/src/include/Util.h @@ -57,6 +57,9 @@ extern unsigned long UtilServerTs(void); +extern unsigned long + UtilLastModified(char *); + /* * Encode a single UTF-8 codepoint as a string buffer containing * between 1 and 4 bytes. The string buffer is allocated on the heap, @@ -88,6 +91,9 @@ extern char * extern char * UtilStringDuplicate(char *); +extern char * + UtilStringConcat(char *, char *); + /* * Sleep for the given number of milliseconds. This is a simple wrapper * for nanosleep() that makes its usage much easier.