Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316#include <stdlib.h>#include <string.h>#include <stdio.h>#include <uthash.h>#include <yyjson.h>
#include "ant.h"#include "cfunc.h"#include "sync.h"#include "errors.h"#include "internal.h"#include "descriptors.h"#include "modules/localstorage.h"
typedef struct storage_entry { char *key; char *value; UT_hash_handle hh;} storage_entry_t;
static storage_entry_t *local_storage = NULL;static char *storage_file_path = NULL;static ant_mutex_t local_storage_lock = ANT_MUTEX_INIT;
static void storage_save(void) { if (!storage_file_path) return; yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); if (!doc) return; yyjson_mut_val *root = yyjson_mut_obj(doc); yyjson_mut_doc_set_root(doc, root); storage_entry_t *entry, *tmp; HASH_ITER(hh, local_storage, entry, tmp) { yyjson_mut_obj_add_str(doc, root, entry->key, entry->value); } yyjson_write_err err; yyjson_mut_write_file(storage_file_path, doc, YYJSON_WRITE_PRETTY, NULL, &err); yyjson_mut_doc_free(doc);}
static void storage_load(void) { if (!storage_file_path) return; yyjson_read_err err; yyjson_doc *doc = yyjson_read_file(storage_file_path, 0, NULL, &err); if (!doc) return; yyjson_val *root = yyjson_doc_get_root(doc); if (!yyjson_is_obj(root)) { yyjson_doc_free(doc); return; } yyjson_obj_iter iter; yyjson_obj_iter_init(root, &iter); yyjson_val *key, *val; while ((key = yyjson_obj_iter_next(&iter))) { val = yyjson_obj_iter_get_val(key); if (yyjson_is_str(key) && yyjson_is_str(val)) { const char *k = yyjson_get_str(key); const char *v = yyjson_get_str(val); size_t klen = yyjson_get_len(key); size_t vlen = yyjson_get_len(val); storage_entry_t *entry = calloc(1, sizeof(storage_entry_t) + klen + 1 + vlen + 1); if (!entry) continue; entry->key = (char *)(entry + 1); entry->value = entry->key + klen + 1; memcpy(entry->key, k, klen); entry->key[klen] = '\0'; memcpy(entry->value, v, vlen); entry->value[vlen] = '\0'; HASH_ADD_KEYPTR(hh, local_storage, entry->key, klen, entry); } } yyjson_doc_free(doc);}
static void storage_clear(void) { storage_entry_t *entry, *tmp; HASH_ITER(hh, local_storage, entry, tmp) { HASH_DEL(local_storage, entry); free(entry); }}
static void storage_set_item(const char *key, size_t key_len, const char *value, size_t value_len) { storage_entry_t *entry = NULL; HASH_FIND(hh, local_storage, key, key_len, entry); if (entry) { HASH_DEL(local_storage, entry); free(entry); } entry = calloc(1, sizeof(storage_entry_t) + key_len + 1 + value_len + 1); if (!entry) return; entry->key = (char *)(entry + 1); entry->value = entry->key + key_len + 1; memcpy(entry->key, key, key_len); entry->key[key_len] = '\0'; memcpy(entry->value, value, value_len); entry->value[value_len] = '\0'; HASH_ADD_KEYPTR(hh, local_storage, entry->key, key_len, entry); storage_save();}
static char *storage_get_item(const char *key, size_t key_len) { storage_entry_t *entry = NULL; HASH_FIND(hh, local_storage, key, key_len, entry); return entry ? entry->value : NULL;}
static void storage_remove_item(const char *key, size_t key_len) { storage_entry_t *entry = NULL; HASH_FIND(hh, local_storage, key, key_len, entry); if (entry) { HASH_DEL(local_storage, entry); free(entry); storage_save(); }}
static size_t storage_length(void) { return HASH_COUNT(local_storage);}
static char *storage_key(size_t index) { storage_entry_t *entry; size_t i = 0; for (entry = local_storage; entry != NULL; entry = entry->hh.next) { if (i == index) return entry->key; i++; } return NULL;}
static bool storage_file_set(void) { ant_mutex_lock(&local_storage_lock); bool set = storage_file_path != NULL; ant_mutex_unlock(&local_storage_lock); return set;}
static ant_value_t storage_file_unset_error(ant_t *js) { return js_mkerr(js, "localStorage requires a file path: pass --localstorage-file or call localStorage.setFile(path)");}
// localStorage.setItem(key, value)static ant_value_t js_localstorage_setItem(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); if (nargs < 2) { return js_mkerr(js, "Failed to execute 'setItem' on 'Storage': 2 arguments required"); } size_t key_len, value_len; char *key = js_getstr(js, args[0], &key_len); char *value = js_getstr(js, js_tostring_val(js, args[1]), &value_len); ant_mutex_lock(&local_storage_lock); storage_set_item(key, key_len, value, value_len); ant_mutex_unlock(&local_storage_lock); return js_mkundef();}
// localStorage.getItem(key)static ant_value_t js_localstorage_getItem(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); if (nargs < 1) { return js_mkerr(js, "Failed to execute 'getItem' on 'Storage': 1 argument required"); } size_t key_len; char *key = js_getstr(js, args[0], &key_len); ant_mutex_lock(&local_storage_lock); char *value = storage_get_item(key, key_len); ant_value_t result = value ? js_mkstr(js, value, strlen(value)) : js_mknull(); ant_mutex_unlock(&local_storage_lock); return result;}
// localStorage.removeItem(key)static ant_value_t js_localstorage_removeItem(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); if (nargs < 1) { return js_mkerr(js, "Failed to execute 'removeItem' on 'Storage': 1 argument required"); } size_t key_len; char *key = js_getstr(js, args[0], &key_len); ant_mutex_lock(&local_storage_lock); storage_remove_item(key, key_len); ant_mutex_unlock(&local_storage_lock); return js_mkundef();}
// localStorage.clear()static ant_value_t js_localstorage_clear(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); (void)args; (void)nargs; ant_mutex_lock(&local_storage_lock); storage_clear(); storage_save(); ant_mutex_unlock(&local_storage_lock); return js_mkundef();}
// localStorage.key(index)static ant_value_t js_localstorage_key(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); if (nargs < 1) { return js_mkerr(js, "Failed to execute 'key' on 'Storage': 1 argument required"); } if (vtype(args[0]) != kTypeNumber) { return js_mknull(); } double idx = js_getnum(args[0]); if (idx < 0) return js_mknull(); size_t index = (size_t)idx; ant_mutex_lock(&local_storage_lock); char *key = storage_key(index); ant_value_t result = key ? js_mkstr(js, key, strlen(key)) : js_mknull(); ant_mutex_unlock(&local_storage_lock); return result;}
// localStorage.lengthstatic ant_value_t js_localstorage_length(ant_params_t) { if (!storage_file_set()) return storage_file_unset_error(js); ant_mutex_lock(&local_storage_lock); size_t length = storage_length(); ant_mutex_unlock(&local_storage_lock); return js_mknum((double)length);}
// localStorage.setFile(path)static ant_value_t js_localstorage_setFile(ant_params_t) { if (nargs < 1) { return js_mkerr(js, "Failed to execute 'setFile' on 'Storage': 1 argument required"); } size_t path_len; char *path = js_getstr(js, args[0], &path_len); char *next = malloc(path_len + 1); if (!next) return js_mkerr(js, "Failed to allocate memory for file path"); memcpy(next, path, path_len); next[path_len] = '\0'; ant_mutex_lock(&local_storage_lock); free(storage_file_path); storage_file_path = next; storage_load(); ant_mutex_unlock(&local_storage_lock); return js_mkundef();}
void init_localstorage_module(ant_t *js) { ant_value_t glob = js_glob(js); const char *file_path = js->runtime.ls_fp; ant_mutex_lock(&local_storage_lock); if (file_path && !storage_file_path) { storage_file_path = strdup(file_path); storage_load(); } ant_mutex_unlock(&local_storage_lock); ant_value_t storage_obj = Ant_Object_Create(js); js_set(js, storage_obj, "setItem", Ant_Function_Create(js_localstorage_setItem)); js_set(js, storage_obj, "getItem", Ant_Function_Create(js_localstorage_getItem)); js_set(js, storage_obj, "removeItem", Ant_Function_Create(js_localstorage_removeItem)); js_set(js, storage_obj, "clear", Ant_Function_Create(js_localstorage_clear)); js_set(js, storage_obj, "key", Ant_Function_Create(js_localstorage_key)); js_set(js, storage_obj, "setFile", Ant_Function_Create(js_localstorage_setFile)); ant_value_t length_getter = Ant_Function_Create(js_localstorage_length); js_set_getter_desc(js, storage_obj, "length", 6, length_getter, 0); js_set_tostringtag(js, storage_obj, js_mkstr(js, "Storage", 7)); js_set(js, glob, "localStorage", storage_obj);}