M README.md => README.md +10 -0
@@ 189,3 189,13 @@ These methods are concerned with handling user-input.
### Key/Value Store
MoonGem implements an in-memory key/value store accessed via the `mg.store` table. This feature is useful for establishing state that's persistent across multiple requests.
+
+- `#mg.store`
+ - Returns the number of stored key/value pairs
+- `mg.store.dump([path])`
+ - Writes the contents of the data store in a tab-separated format to a file at `path`, or stdout if `path` is ommitted
+- `mg.store.get_info()`
+ - Returns a table with the following fields:
+ - `length`: The number of stored key/value pairs
+ - `capacity`: The total number of slots allocated
+ - `data_size`: The combined length of all of the stored values, in bytes
M include/store.h => include/store.h +1 -0
@@ 9,6 9,7 @@
typedef struct cell_t {
uint64_t key;
+ size_t length;
char* data;
bool deleted;
} cell_t;
M src/api.c => src/api.c +69 -0
@@ 1,5 1,6 @@
#include <lauxlib.h>
#include <lua.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
@@ 23,6 24,10 @@
#define FLD_CERT_FINGERPRINT "fingerprint"
#define FLD_CERT_EXPIRATION "not_after"
+#define FLD_STORE_LENGTH "length"
+#define FLD_STORE_CAPACITY "capacity"
+#define FLD_STORE_DATA_SIZE "data_size"
+
static void set_interrupt_response(response_t* response, int status,
const char* meta) {
response->interrupted = true;
@@ 571,3 576,67 @@ int api_get_store_value(lua_State* L) {
return 1;
}
+
+int api_dump_store(lua_State* L) {
+ lua_settop(L, 1);
+
+ const char* path = lua_tostring(L, 1);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, FLD_STORE);
+ store_t* store = lua_touserdata(L, -1);
+
+ FILE* out = path == NULL ? stdout : fopen(path, "w");
+ if (out == NULL) {
+ luaL_error(L, "Failed to open output stream");
+ } else {
+ fprintf(out, "position\tkey\tlength\tdata\n");
+ for (size_t i = 0; i < store->cell_count; i++) {
+ cell_t* cell = &store->cells[i];
+ if (cell->key == 0 || cell->deleted) {
+ continue;
+ }
+
+ fprintf(out, "%zu\t%zu\t%zu\t%s\n", i, cell->key, cell->length,
+ cell->data);
+ }
+ }
+
+ return 0;
+}
+
+int api_get_store_length(lua_State* L) {
+ lua_getfield(L, LUA_REGISTRYINDEX, FLD_STORE);
+ store_t* store = lua_touserdata(L, -1);
+
+ lua_pushinteger(L, store->stored_count);
+
+ return 1;
+}
+
+int api_get_store_info(lua_State* L) {
+ lua_newtable(L);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, FLD_STORE);
+ store_t* store = lua_touserdata(L, -1);
+
+ lua_pushinteger(L, store->stored_count);
+ lua_setfield(L, -2, FLD_STORE_LENGTH);
+
+ lua_pushinteger(L, store->cell_count);
+ lua_setfield(L, -2, FLD_STORE_CAPACITY);
+
+ size_t total_length = 0;
+ for (size_t i = 0; i < store->cell_count; i++) {
+ cell_t* cell = &store->cells[i];
+ if (cell->key == 0 || cell->deleted) {
+ continue;
+ }
+
+ total_length += cell->length;
+ }
+
+ lua_pushinteger(L, total_length);
+ lua_setfield(L, -2, FLD_STORE_DATA_SIZE);
+
+ return 1;
+}
M src/script.c => src/script.c +11 -1
@@ 59,6 59,9 @@
#define TBL_STORE "store"
+#define FUNC_DUMP "dump"
+#define FUNC_INFO "get_info"
+
#define SCRIPT_BUFFER_SIZE (1 << 16)
static char global_script_buffer[SCRIPT_BUFFER_SIZE];
@@ 119,6 122,9 @@ int api_endblock(lua_State* L);
/* Key/Value Store */
int api_set_store_value(lua_State* L);
int api_get_store_value(lua_State* L);
+int api_dump_store(lua_State* L);
+int api_get_store_length(lua_State* L);
+int api_get_store_info(lua_State* L);
static void set_api_methods(lua_State* L) {
luaL_Reg methods[] = {{FUNC_SET_PATH, api_set_path},
@@ 167,9 173,13 @@ static void set_api_methods(lua_State* L) {
luaL_newlib(L, methods);
// set up a table for the key/value store
- lua_newtable(L);
+ luaL_Reg store_methods[] = {{FUNC_DUMP, api_dump_store},
+ {FUNC_INFO, api_get_store_info},
+ {NULL, NULL}};
+ luaL_newlib(L, store_methods);
luaL_Reg store_metamethods[] = {{"__index", api_get_store_value},
{"__newindex", api_set_store_value},
+ {"__len", api_get_store_length},
{NULL, NULL}};
luaL_newlib(L, store_metamethods);
lua_setmetatable(L, -2);
M src/store.c => src/store.c +4 -1
@@ 66,6 66,7 @@ static void insert_by_keyhash(store_t* store, uint64_t key, char* data) {
cell->key = key;
cell->data = data;
+ cell->length = strlen(data);
cell->deleted = false;
}
@@ 133,12 134,14 @@ bool delete_from_store(store_t* store, char* key) {
return false;
}
- target->deleted = true;
if (target->data != NULL) {
free(target->data);
target->data = NULL;
}
+ target->deleted = true;
+ target->length = 0;
+
--(store->stored_count);
return true;