aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/c/urweb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/c/urweb.c')
-rw-r--r--src/c/urweb.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 169152dc..30619314 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -22,6 +22,8 @@
#include "types.h"
+#include "uthash.h"
+
uw_unit uw_unit_v = 0;
@@ -70,6 +72,9 @@ void uw_buffer_free(uw_buffer *b) {
void uw_buffer_reset(uw_buffer *b) {
b->front = b->start;
+ if (b->front != b->back) {
+ *b->front = 0;
+ }
}
int uw_buffer_check(uw_buffer *b, size_t extra) {
@@ -483,6 +488,10 @@ struct uw_context {
char *output_buffer;
size_t output_buffer_size;
+ // For caching.
+ int numRecording;
+ int recordingOffset;
+
int remoteSock;
};
@@ -567,6 +576,9 @@ uw_context uw_init(int id, uw_loggers *lg) {
ctx->output_buffer = malloc(1);
ctx->output_buffer_size = 1;
+ ctx->numRecording = 0;
+ ctx->recordingOffset = 0;
+
ctx->remoteSock = -1;
return ctx;
@@ -1703,6 +1715,21 @@ void uw_write(uw_context ctx, const char* s) {
*ctx->page.front = 0;
}
+void uw_recordingStart(uw_context ctx) {
+ if (ctx->numRecording++ == 0) {
+ ctx->recordingOffset = ctx->page.front - ctx->page.start;
+ }
+}
+
+char *uw_recordingRead(uw_context ctx) {
+ // Only the outermost recorder can read unless the recording is empty.
+ char *recording = ctx->page.start + ctx->recordingOffset;
+ if (--ctx->numRecording > 0 && recording != ctx->page.front) {
+ return NULL;
+ }
+ return strdup(recording);
+}
+
char *uw_Basis_attrifyInt(uw_context ctx, uw_Basis_int n) {
char *result;
int len;
@@ -4506,3 +4533,158 @@ int uw_remoteSock(uw_context ctx) {
void uw_set_remoteSock(uw_context ctx, int sock) {
ctx->remoteSock = sock;
}
+
+
+// Sqlcache
+
+void uw_Sqlcache_freeValue(uw_Sqlcache_Value *value) {
+ if (value) {
+ free(value->result);
+ free(value->output);
+ free(value);
+ }
+}
+
+void uw_Sqlcache_freeEntry(uw_Sqlcache_Entry* entry) {
+ if (entry) {
+ free(entry->key);
+ uw_Sqlcache_freeValue(entry->value);
+ free(entry);
+ }
+}
+
+// TODO: pick a number.
+unsigned int uw_Sqlcache_maxSize = 1234567890;
+
+void uw_Sqlcache_delete(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry) {
+ HASH_DEL(cache->table, entry);
+ uw_Sqlcache_freeEntry(entry);
+}
+
+uw_Sqlcache_Entry *uw_Sqlcache_find(uw_Sqlcache_Cache *cache, char *key, size_t len, int bump) {
+ uw_Sqlcache_Entry *entry = NULL;
+ HASH_FIND(hh, cache->table, key, len, entry);
+ if (entry && bump) {
+ // Bump for LRU purposes.
+ HASH_DEL(cache->table, entry);
+ // Important that we use [entry->key], because [key] might be ephemeral.
+ HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry);
+ }
+ return entry;
+}
+
+void uw_Sqlcache_add(uw_Sqlcache_Cache *cache, uw_Sqlcache_Entry *entry, size_t len) {
+ HASH_ADD_KEYPTR(hh, cache->table, entry->key, len, entry);
+ if (HASH_COUNT(cache->table) > uw_Sqlcache_maxSize) {
+ // Deletes the first element of the cache.
+ uw_Sqlcache_delete(cache, cache->table);
+ }
+}
+
+unsigned long uw_Sqlcache_getTimeNow(uw_Sqlcache_Cache *cache) {
+ return ++cache->timeNow;
+}
+
+unsigned long uw_Sqlcache_timeMax(unsigned long x, unsigned long y) {
+ return x > y ? x : y;
+}
+
+char uw_Sqlcache_keySep = '_';
+
+char *uw_Sqlcache_allocKeyBuffer(char **keys, int numKeys) {
+ size_t len = 0;
+ while (numKeys-- > 0) {
+ char* k = keys[numKeys];
+ if (!k) {
+ // Can only happen when flushihg, in which case we don't need anything past the null key.
+ break;
+ }
+ // Leave room for separator.
+ len += 1 + strlen(k);
+ }
+ char *buf = malloc(len+1);
+ // If nothing is copied into the buffer, it should look like it has length 0.
+ buf[0] = 0;
+ return buf;
+}
+
+char *uw_Sqlcache_keyCopy(char *buf, char *key) {
+ *buf++ = uw_Sqlcache_keySep;
+ return stpcpy(buf, key);
+}
+
+// The NUL-terminated prefix of [key] below always looks something like "_k1_k2_k3..._kn".
+// TODO: strlen(key) = buf - key?
+
+uw_Sqlcache_Value *uw_Sqlcache_check(uw_Sqlcache_Cache *cache, char **keys, int numKeys) {
+ char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
+ char *buf = key;
+ time_t timeInvalid = cache->timeInvalid;
+ uw_Sqlcache_Entry *entry;
+ while (numKeys-- > 0) {
+ buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]);
+ size_t len = buf - key;
+ entry = uw_Sqlcache_find(cache, key, len, 1);
+ if (!entry) {
+ free(key);
+ return NULL;
+ }
+ timeInvalid = uw_Sqlcache_timeMax(timeInvalid, entry->timeInvalid);
+ }
+ free(key);
+ uw_Sqlcache_Value *value = entry->value;
+ return value && value->timeValid > timeInvalid ? value : NULL;
+}
+
+void uw_Sqlcache_store(uw_Sqlcache_Cache *cache, char **keys, int numKeys, uw_Sqlcache_Value *value) {
+ char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
+ char *buf = key;
+ time_t timeNow = uw_Sqlcache_getTimeNow(cache);
+ uw_Sqlcache_Entry *entry;
+ while (numKeys-- > 0) {
+ buf = uw_Sqlcache_keyCopy(buf, keys[numKeys]);
+ size_t len = buf - key;
+ entry = uw_Sqlcache_find(cache, key, len, 1);
+ if (!entry) {
+ entry = malloc(sizeof(uw_Sqlcache_Entry));
+ entry->key = strdup(key);
+ entry->value = NULL;
+ entry->timeInvalid = 0; // ASK: is this okay?
+ uw_Sqlcache_add(cache, entry, len);
+ }
+ }
+ free(key);
+ uw_Sqlcache_freeValue(entry->value);
+ entry->value = value;
+ entry->value->timeValid = timeNow;
+}
+
+void uw_Sqlcache_flush(uw_Sqlcache_Cache *cache, char **keys, int numKeys) {
+ char *key = uw_Sqlcache_allocKeyBuffer(keys, numKeys);
+ char *buf = key;
+ time_t timeNow = uw_Sqlcache_getTimeNow(cache);
+ uw_Sqlcache_Entry *entry;
+ while (numKeys-- > 0) {
+ char *k = keys[numKeys];
+ if (!k) {
+ if (entry) {
+ entry->timeInvalid = timeNow;
+ } else {
+ // Haven't found an entry yet, so the first key was null.
+ cache->timeInvalid = timeNow;
+ }
+ free(key);
+ return;
+ }
+ buf = uw_Sqlcache_keyCopy(buf, k);
+ size_t len = buf - key;
+ entry = uw_Sqlcache_find(cache, key, len, 0);
+ if (!entry) {
+ free(key);
+ return;
+ }
+ }
+ free(key);
+ // All the keys were non-null and the relevant entry is present, so we delete it.
+ uw_Sqlcache_delete(cache, entry);
+}