MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 1182 lines 39 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include "ant.h" 4#include "errors.h" 5#include "internal.h" 6#include "modules/lmdb.h" 7#include "modules/buffer.h" 8#include "modules/symbol.h" 9#include "descriptors.h" 10#include "gc/modules.h" 11 12#include <lmdb.h> 13#include <stdbool.h> 14#include <stdint.h> 15#include <stdlib.h> 16#include <string.h> 17 18struct lmdb_env_handle { 19 MDB_env *env; 20 bool closed; 21 bool read_only; 22 char *path; 23 lmdb_db_handle_t *db_head; 24 lmdb_txn_handle_t *txn_head; 25 lmdb_env_handle_t *next_global; 26}; 27 28struct lmdb_db_handle { 29 MDB_dbi dbi; 30 bool closed; 31 char *name; 32 lmdb_env_handle_t *env; 33 lmdb_db_handle_t *next_in_env; 34 lmdb_db_handle_t *next_global; 35}; 36 37struct lmdb_txn_handle { 38 MDB_txn *txn; 39 bool closed; 40 bool read_only; 41 lmdb_env_handle_t *env; 42 lmdb_txn_handle_t *next_in_env; 43 lmdb_txn_handle_t *next_global; 44}; 45 46struct lmdb_env_ref { 47 ant_value_t obj; 48 lmdb_env_handle_t *env; 49 lmdb_env_ref_t *next; 50}; 51 52struct lmdb_db_ref { 53 ant_value_t obj; 54 lmdb_db_handle_t *db; 55 lmdb_db_ref_t *next; 56}; 57 58struct lmdb_txn_ref { 59 ant_value_t obj; 60 lmdb_txn_handle_t *txn; 61 lmdb_txn_ref_t *next; 62}; 63 64static lmdb_js_types_t lmdb_types = {0}; 65static lmdb_env_handle_t *env_handles = NULL; 66static lmdb_db_handle_t *db_handles = NULL; 67static lmdb_txn_handle_t *txn_handles = NULL; 68static lmdb_env_ref_t *env_refs = NULL; 69static lmdb_db_ref_t *db_refs = NULL; 70static lmdb_txn_ref_t *txn_refs = NULL; 71 72static ant_value_t make_env_obj(ant_t *js, lmdb_env_handle_t *env); 73static ant_value_t make_db_obj(ant_t *js, lmdb_db_handle_t *db); 74static ant_value_t make_txn_obj(ant_t *js, lmdb_txn_handle_t *txn); 75 76static void list_remove_db(lmdb_env_handle_t *env, lmdb_db_handle_t *target) { 77 if (!env || !target) return; 78 lmdb_db_handle_t **cur = &env->db_head; 79 while (*cur) { 80 if (*cur == target) { 81 *cur = target->next_in_env; 82 target->next_in_env = NULL; 83 return; 84 } 85 cur = &(*cur)->next_in_env; 86 } 87} 88 89static void list_remove_txn(lmdb_env_handle_t *env, lmdb_txn_handle_t *target) { 90 if (!env || !target) return; 91 lmdb_txn_handle_t **cur = &env->txn_head; 92 while (*cur) { 93 if (*cur == target) { 94 *cur = target->next_in_env; 95 target->next_in_env = NULL; 96 return; 97 } 98 cur = &(*cur)->next_in_env; 99 } 100} 101 102static void register_env_ref(ant_value_t obj, lmdb_env_handle_t *env) { 103 lmdb_env_ref_t *ref = ant_calloc(sizeof(lmdb_env_ref_t)); 104 if (!ref) return; 105 ref->obj = obj; 106 ref->env = env; 107 ref->next = env_refs; 108 env_refs = ref; 109} 110 111static void register_db_ref(ant_value_t obj, lmdb_db_handle_t *db) { 112 lmdb_db_ref_t *ref = ant_calloc(sizeof(lmdb_db_ref_t)); 113 if (!ref) return; 114 ref->obj = obj; 115 ref->db = db; 116 ref->next = db_refs; 117 db_refs = ref; 118} 119 120static void register_txn_ref(ant_value_t obj, lmdb_txn_handle_t *txn) { 121 lmdb_txn_ref_t *ref = ant_calloc(sizeof(lmdb_txn_ref_t)); 122 if (!ref) return; 123 ref->obj = obj; 124 ref->txn = txn; 125 ref->next = txn_refs; 126 txn_refs = ref; 127} 128 129static lmdb_env_handle_t *find_env_by_obj(ant_value_t obj) { 130 for (lmdb_env_ref_t *ref = env_refs; ref; ref = ref->next) 131 if (ref->obj == obj) return ref->env; 132 return NULL; 133} 134 135static lmdb_db_handle_t *find_db_by_obj(ant_value_t obj) { 136 for (lmdb_db_ref_t *ref = db_refs; ref; ref = ref->next) 137 if (ref->obj == obj) return ref->db; 138 return NULL; 139} 140 141static lmdb_txn_handle_t *find_txn_by_obj(ant_value_t obj) { 142 for (lmdb_txn_ref_t *ref = txn_refs; ref; ref = ref->next) 143 if (ref->obj == obj) return ref->txn; 144 return NULL; 145} 146 147static void unregister_env_ref_by_obj(ant_value_t obj) { 148 lmdb_env_ref_t **cur = &env_refs; 149 while (*cur) { 150 if ((*cur)->obj == obj) { 151 lmdb_env_ref_t *next = (*cur)->next; 152 free(*cur); 153 *cur = next; 154 return; 155 } 156 cur = &(*cur)->next; 157 } 158} 159 160static void unregister_db_ref_by_obj(ant_value_t obj) { 161 lmdb_db_ref_t **cur = &db_refs; 162 while (*cur) { 163 if ((*cur)->obj == obj) { 164 lmdb_db_ref_t *next = (*cur)->next; 165 free(*cur); 166 *cur = next; 167 return; 168 } 169 cur = &(*cur)->next; 170 } 171} 172 173static void unregister_txn_ref_by_obj(ant_value_t obj) { 174 lmdb_txn_ref_t **cur = &txn_refs; 175 while (*cur) { 176 if ((*cur)->obj == obj) { 177 lmdb_txn_ref_t *next = (*cur)->next; 178 free(*cur); 179 *cur = next; 180 return; 181 } 182 cur = &(*cur)->next; 183 } 184} 185 186static void unregister_db_refs_by_env(lmdb_env_handle_t *env) { 187 lmdb_db_ref_t **cur = &db_refs; 188 while (*cur) { 189 if ((*cur)->db && (*cur)->db->env == env) { 190 lmdb_db_ref_t *next = (*cur)->next; 191 free(*cur); 192 *cur = next; 193 continue; 194 } 195 cur = &(*cur)->next; 196 } 197} 198 199static void unregister_txn_refs_by_env(lmdb_env_handle_t *env) { 200 lmdb_txn_ref_t **cur = &txn_refs; 201 while (*cur) { 202 if ((*cur)->txn && (*cur)->txn->env == env) { 203 lmdb_txn_ref_t *next = (*cur)->next; 204 free(*cur); 205 *cur = next; 206 continue; 207 } 208 cur = &(*cur)->next; 209 } 210} 211 212static void env_handle_close(lmdb_env_handle_t *env) { 213 if (!env || env->closed) return; 214 215 lmdb_txn_handle_t *txn = env->txn_head; 216 while (txn) { 217 if (!txn->closed && txn->txn) { 218 mdb_txn_abort(txn->txn); 219 txn->txn = NULL; 220 txn->closed = true; 221 } 222 txn = txn->next_in_env; 223 } 224 225 lmdb_db_handle_t *db = env->db_head; 226 while (db) { 227 if (!db->closed) { 228 mdb_dbi_close(env->env, db->dbi); 229 db->closed = true; 230 } 231 db = db->next_in_env; 232 } 233 234 mdb_env_close(env->env); 235 env->env = NULL; 236 env->closed = true; 237 env->db_head = NULL; 238 env->txn_head = NULL; 239 240 unregister_db_refs_by_env(env); 241 unregister_txn_refs_by_env(env); 242} 243 244static lmdb_env_handle_t *get_env_handle(ant_t *js, ant_value_t obj, bool open_required) { 245 lmdb_env_handle_t *env = find_env_by_obj(obj); 246 if (!env) return NULL; 247 if (open_required && env->closed) return NULL; 248 return env; 249} 250 251static lmdb_db_handle_t *get_db_handle(ant_t *js, ant_value_t obj, bool open_required) { 252 lmdb_db_handle_t *db = find_db_by_obj(obj); 253 if (!db) return NULL; 254 if (open_required && (db->closed || !db->env || db->env->closed)) return NULL; 255 return db; 256} 257 258static lmdb_txn_handle_t *get_txn_handle(ant_t *js, ant_value_t obj, bool open_required) { 259 lmdb_txn_handle_t *txn = find_txn_by_obj(obj); 260 if (!txn) return NULL; 261 if (open_required && (txn->closed || !txn->txn)) return NULL; 262 return txn; 263} 264 265static bool option_bool(ant_t *js, ant_value_t options, const char *key, bool fallback) { 266 if (vtype(options) != T_OBJ) return fallback; 267 ant_value_t val = js_get(js, options, key); 268 if (vtype(val) == T_UNDEF) return fallback; 269 return js_truthy(js, val); 270} 271 272static unsigned int option_uint(ant_t *js, ant_value_t options, const char *key, unsigned int fallback) { 273 if (vtype(options) != T_OBJ) return fallback; 274 ant_value_t val = js_get(js, options, key); 275 if (vtype(val) != T_NUM) return fallback; 276 double n = js_getnum(val); 277 if (n < 0.0) return fallback; 278 return (unsigned int)n; 279} 280 281static size_t option_size(ant_t *js, ant_value_t options, const char *key, size_t fallback) { 282 if (vtype(options) != T_OBJ) return fallback; 283 ant_value_t val = js_get(js, options, key); 284 if (vtype(val) != T_NUM) return fallback; 285 double n = js_getnum(val); 286 if (n < 0.0) return fallback; 287 return (size_t)n; 288} 289 290static bool js_to_mdb_val(ant_t *js, ant_value_t input, MDB_val *out) { 291 if (vtype(input) == T_STR) { 292 size_t len = 0; 293 const char *str = js_getstr(js, input, &len); 294 if (!str) return false; 295 out->mv_data = (void *)str; 296 out->mv_size = len; 297 return true; 298 } 299 300 if (vtype(input) == T_OBJ) { 301 TypedArrayData *ta = buffer_get_typedarray_data(input); 302 if (ta) { 303 if (!ta->buffer || !ta->buffer->data) return false; 304 out->mv_data = (void *)(ta->buffer->data + ta->byte_offset); 305 out->mv_size = ta->byte_length; 306 return true; 307 } 308 309 ArrayBufferData *ab = buffer_get_arraybuffer_data(input); 310 if (ab) { 311 if (!ab->data) return false; 312 out->mv_data = (void *)ab->data; 313 out->mv_size = ab->length; 314 return true; 315 } 316 } 317 318 return false; 319} 320 321static ant_value_t mdb_val_to_js(ant_t *js, MDB_val *val, bool as_string) { 322 if (as_string) { 323 return js_mkstr(js, val->mv_data, val->mv_size); 324 } 325 326 ArrayBufferData *ab = create_array_buffer_data(val->mv_size); 327 if (!ab) return js_mkerr(js, "Failed to allocate LMDB read buffer"); 328 329 if (val->mv_size > 0 && val->mv_data) { 330 memcpy(ab->data, val->mv_data, val->mv_size); 331 } 332 333 ant_value_t out = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, ab->length, "Uint8Array"); 334 if (vtype(out) == T_ERR) free_array_buffer_data(ab); 335 return out; 336} 337 338static bool parse_get_encoding(ant_t *js, ant_value_t encoding, bool *as_string) { 339 if (vtype(encoding) == T_UNDEF) return true; 340 if (vtype(encoding) != T_STR) return false; 341 342 size_t len = 0; 343 const char *mode = js_getstr(js, encoding, &len); 344 if (!mode) return false; 345 346 if (len == 5 && memcmp(mode, "bytes", 5) == 0) { 347 *as_string = false; 348 return true; 349 } 350 if (len == 4 && memcmp(mode, "utf8", 4) == 0) { 351 *as_string = true; 352 return true; 353 } 354 355 return false; 356} 357 358static ant_value_t lmdb_open(ant_t *js, ant_value_t *args, int nargs) { 359 if (nargs < 1 || vtype(args[0]) != T_STR) { 360 return js_mkerr(js, "lmdb.open(path, options?) requires a string path"); 361 } 362 363 size_t path_len = 0; 364 const char *path = js_getstr(js, args[0], &path_len); 365 if (!path || path_len == 0) { 366 return js_mkerr(js, "lmdb.open path cannot be empty"); 367 } 368 369 ant_value_t options = nargs > 1 ? args[1] : js_mkundef(); 370 371 bool read_only = option_bool(js, options, "readOnly", false); 372 bool no_subdir = option_bool(js, options, "noSubdir", false); 373 bool no_sync = option_bool(js, options, "noSync", false); 374 bool no_meta_sync = option_bool(js, options, "noMetaSync", false); 375 bool no_read_ahead = option_bool(js, options, "noReadAhead", false); 376 bool no_lock = option_bool(js, options, "noLock", false); 377 bool write_map = option_bool(js, options, "writeMap", false); 378 bool map_async = option_bool(js, options, "mapAsync", false); 379 380 size_t map_size = option_size(js, options, "mapSize", 0); 381 unsigned int max_readers = option_uint(js, options, "maxReaders", 0); 382 unsigned int max_dbs = option_uint(js, options, "maxDbs", 0); 383 unsigned int mode = option_uint(js, options, "mode", 0644U); 384 385 unsigned int flags = 0; 386 if (read_only) flags |= MDB_RDONLY; 387 if (no_subdir) flags |= MDB_NOSUBDIR; 388 if (no_sync) flags |= MDB_NOSYNC; 389 if (no_meta_sync) flags |= MDB_NOMETASYNC; 390 if (no_read_ahead) flags |= MDB_NORDAHEAD; 391 if (no_lock) flags |= MDB_NOLOCK; 392 if (write_map) flags |= MDB_WRITEMAP; 393 if (map_async) flags |= MDB_MAPASYNC; 394 395 MDB_env *env = NULL; 396 int rc = mdb_env_create(&env); 397 if (rc != 0) return js_mkerr(js, "lmdb_env_create failed: %s", mdb_strerror(rc)); 398 399 if (map_size > 0) { 400 rc = mdb_env_set_mapsize(env, map_size); 401 if (rc != 0) { 402 mdb_env_close(env); 403 return js_mkerr(js, "lmdb_env_set_mapsize failed: %s", mdb_strerror(rc)); 404 } 405 } 406 407 if (max_readers > 0) { 408 rc = mdb_env_set_maxreaders(env, max_readers); 409 if (rc != 0) { 410 mdb_env_close(env); 411 return js_mkerr(js, "lmdb_env_set_maxreaders failed: %s", mdb_strerror(rc)); 412 } 413 } 414 415 if (max_dbs > 0) { 416 rc = mdb_env_set_maxdbs(env, max_dbs); 417 if (rc != 0) { 418 mdb_env_close(env); 419 return js_mkerr(js, "lmdb_env_set_maxdbs failed: %s", mdb_strerror(rc)); 420 } 421 } 422 423 rc = mdb_env_open(env, path, flags, (mdb_mode_t)mode); 424 if (rc != 0) { 425 mdb_env_close(env); 426 return js_mkerr(js, "lmdb_env_open('%s') failed: %s", path, mdb_strerror(rc)); 427 } 428 429 lmdb_env_handle_t *handle = ant_calloc(sizeof(lmdb_env_handle_t)); 430 if (!handle) { 431 mdb_env_close(env); 432 return js_mkerr(js, "Out of memory"); 433 } 434 435 handle->env = env; 436 handle->closed = false; 437 handle->read_only = read_only; 438 handle->path = strndup(path, path_len); 439 handle->next_global = env_handles; 440 env_handles = handle; 441 442 return make_env_obj(js, handle); 443} 444 445static ant_value_t lmdb_env_open_db(ant_t *js, ant_value_t *args, int nargs) { 446 lmdb_env_handle_t *env = get_env_handle(js, js_getthis(js), true); 447 if (!env) return js_mkerr(js, "Invalid or closed LMDB env"); 448 449 const char *name = NULL; 450 size_t name_len = 0; 451 ant_value_t options = js_mkundef(); 452 453 if (nargs > 0 && vtype(args[0]) == T_STR) { 454 name = js_getstr(js, args[0], &name_len); 455 if (nargs > 1) options = args[1]; 456 } else if (nargs > 0 && vtype(args[0]) == T_OBJ) { 457 options = args[0]; 458 } 459 460 bool create = option_bool(js, options, "create", false); 461 bool dup_sort = option_bool(js, options, "dupSort", false); 462 bool dup_fixed = option_bool(js, options, "dupFixed", false); 463 bool integer_key = option_bool(js, options, "integerKey", false); 464 bool integer_dup = option_bool(js, options, "integerDup", false); 465 466 if (env->read_only && create) { 467 return js_mkerr(js, "Cannot open DB with create=true on a read-only env"); 468 } 469 470 unsigned int dbi_flags = 0; 471 if (create) dbi_flags |= MDB_CREATE; 472 if (dup_sort) dbi_flags |= MDB_DUPSORT; 473 if (dup_fixed) dbi_flags |= MDB_DUPFIXED; 474 if (integer_key) dbi_flags |= MDB_INTEGERKEY; 475 if (integer_dup) dbi_flags |= MDB_INTEGERDUP; 476 477 unsigned int txn_flags = env->read_only ? MDB_RDONLY : 0U; 478 MDB_txn *txn = NULL; 479 int rc = mdb_txn_begin(env->env, NULL, txn_flags, &txn); 480 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 481 482 MDB_dbi dbi = 0; 483 rc = mdb_dbi_open(txn, name, dbi_flags, &dbi); 484 if (rc != 0) { 485 mdb_txn_abort(txn); 486 return js_mkerr(js, "lmdb_dbi_open failed: %s", mdb_strerror(rc)); 487 } 488 489 if (txn_flags & MDB_RDONLY) { 490 mdb_txn_abort(txn); 491 } else { 492 rc = mdb_txn_commit(txn); 493 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 494 } 495 496 lmdb_db_handle_t *db = ant_calloc(sizeof(lmdb_db_handle_t)); 497 if (!db) { 498 mdb_dbi_close(env->env, dbi); 499 return js_mkerr(js, "Out of memory"); 500 } 501 502 db->dbi = dbi; 503 db->closed = false; 504 db->env = env; 505 db->name = name ? strndup(name, name_len) : NULL; 506 507 db->next_in_env = env->db_head; 508 env->db_head = db; 509 db->next_global = db_handles; 510 db_handles = db; 511 512 return make_db_obj(js, db); 513} 514 515static ant_value_t lmdb_env_begin_txn(ant_t *js, ant_value_t *args, int nargs) { 516 lmdb_env_handle_t *env = get_env_handle(js, js_getthis(js), true); 517 if (!env) return js_mkerr(js, "Invalid or closed LMDB env"); 518 519 ant_value_t options = nargs > 0 ? args[0] : js_mkundef(); 520 bool read_only = option_bool(js, options, "readOnly", env->read_only); 521 522 if (env->read_only && !read_only) { 523 return js_mkerr(js, "Cannot create read-write transaction on read-only env"); 524 } 525 526 MDB_txn *txn = NULL; 527 int rc = mdb_txn_begin(env->env, NULL, read_only ? MDB_RDONLY : 0U, &txn); 528 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 529 530 lmdb_txn_handle_t *handle = ant_calloc(sizeof(lmdb_txn_handle_t)); 531 if (!handle) { 532 mdb_txn_abort(txn); 533 return js_mkerr(js, "Out of memory"); 534 } 535 536 handle->txn = txn; 537 handle->closed = false; 538 handle->read_only = read_only; 539 handle->env = env; 540 541 handle->next_in_env = env->txn_head; 542 env->txn_head = handle; 543 handle->next_global = txn_handles; 544 txn_handles = handle; 545 546 return make_txn_obj(js, handle); 547} 548 549static ant_value_t lmdb_env_close_method(ant_t *js, ant_value_t *args, int nargs) { 550 (void)args; 551 (void)nargs; 552 ant_value_t self = js_getthis(js); 553 lmdb_env_handle_t *env = get_env_handle(js, self, false); 554 if (!env) return js_mkerr(js, "Invalid LMDB env"); 555 556 env_handle_close(env); 557 unregister_env_ref_by_obj(self); 558 return js_mkundef(); 559} 560 561static ant_value_t lmdb_env_sync_method(ant_t *js, ant_value_t *args, int nargs) { 562 lmdb_env_handle_t *env = get_env_handle(js, js_getthis(js), true); 563 if (!env) return js_mkerr(js, "Invalid or closed LMDB env"); 564 565 bool force = true; 566 if (nargs > 0) force = js_truthy(js, args[0]); 567 568 int rc = mdb_env_sync(env->env, force ? 1 : 0); 569 if (rc != 0) return js_mkerr(js, "lmdb_env_sync failed: %s", mdb_strerror(rc)); 570 return js_mkundef(); 571} 572 573static ant_value_t lmdb_env_stat_method(ant_t *js, ant_value_t *args, int nargs) { 574 lmdb_env_handle_t *env = get_env_handle(js, js_getthis(js), true); 575 if (!env) return js_mkerr(js, "Invalid or closed LMDB env"); 576 577 MDB_txn *txn = NULL; 578 int rc = mdb_txn_begin(env->env, NULL, MDB_RDONLY, &txn); 579 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 580 581 MDB_stat stat; 582 rc = mdb_stat(txn, 0, &stat); 583 mdb_txn_abort(txn); 584 if (rc != 0) return js_mkerr(js, "lmdb_stat failed: %s", mdb_strerror(rc)); 585 586 ant_value_t out = js_mkobj(js); 587 js_set(js, out, "psize", js_mknum((double)stat.ms_psize)); 588 js_set(js, out, "depth", js_mknum((double)stat.ms_depth)); 589 js_set(js, out, "branchPages", js_mknum((double)stat.ms_branch_pages)); 590 js_set(js, out, "leafPages", js_mknum((double)stat.ms_leaf_pages)); 591 js_set(js, out, "overflowPages", js_mknum((double)stat.ms_overflow_pages)); 592 js_set(js, out, "entries", js_mknum((double)stat.ms_entries)); 593 594 return out; 595} 596 597static ant_value_t lmdb_env_info_method(ant_t *js, ant_value_t *args, int nargs) { 598 lmdb_env_handle_t *env = get_env_handle(js, js_getthis(js), true); 599 if (!env) return js_mkerr(js, "Invalid or closed LMDB env"); 600 601 MDB_envinfo info; 602 int rc = mdb_env_info(env->env, &info); 603 if (rc != 0) return js_mkerr(js, "lmdb_env_info failed: %s", mdb_strerror(rc)); 604 605 ant_value_t out = js_mkobj(js); 606 js_set(js, out, "mapSize", js_mknum((double)info.me_mapsize)); 607 js_set(js, out, "lastPgNo", js_mknum((double)info.me_last_pgno)); 608 js_set(js, out, "lastTxnId", js_mknum((double)info.me_last_txnid)); 609 js_set(js, out, "maxReaders", js_mknum((double)info.me_maxreaders)); 610 js_set(js, out, "numReaders", js_mknum((double)info.me_numreaders)); 611 612 return out; 613} 614 615static ant_value_t lmdb_txn_get_impl(ant_t *js, ant_value_t *args, int nargs, bool as_string) { 616 if (nargs < 2) return js_mkerr(js, "txn.getBytes/getString(db, key) requires db and key"); 617 618 lmdb_txn_handle_t *txn = get_txn_handle(js, js_getthis(js), true); 619 if (!txn) return js_mkerr(js, "Invalid or closed LMDB transaction"); 620 621 lmdb_db_handle_t *db = get_db_handle(js, args[0], true); 622 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 623 if (db->env != txn->env) return js_mkerr(js, "Database and transaction belong to different envs"); 624 625 MDB_val key; 626 if (!js_to_mdb_val(js, args[1], &key)) { 627 return js_mkerr(js, "LMDB key must be string, ArrayBuffer, or TypedArray"); 628 } 629 630 MDB_val value; 631 int rc = mdb_get(txn->txn, db->dbi, &key, &value); 632 if (rc == MDB_NOTFOUND) return js_mkundef(); 633 if (rc != 0) return js_mkerr(js, "lmdb_get failed: %s", mdb_strerror(rc)); 634 635 return mdb_val_to_js(js, &value, as_string); 636} 637 638static ant_value_t lmdb_txn_get(ant_t *js, ant_value_t *args, int nargs) { 639 if (nargs < 2) return js_mkerr(js, "txn.get(db, key, encoding?) requires db and key"); 640 bool as_string = false; 641 if (nargs > 2 && !parse_get_encoding(js, args[2], &as_string)) { 642 return js_mkerr(js, "txn.get encoding must be 'utf8' or 'bytes'"); 643 } 644 return lmdb_txn_get_impl(js, args, nargs, as_string); 645} 646 647static ant_value_t lmdb_txn_get_bytes(ant_t *js, ant_value_t *args, int nargs) { 648 return lmdb_txn_get_impl(js, args, nargs, false); 649} 650 651static ant_value_t lmdb_txn_get_string(ant_t *js, ant_value_t *args, int nargs) { 652 return lmdb_txn_get_impl(js, args, nargs, true); 653} 654 655static ant_value_t lmdb_txn_put(ant_t *js, ant_value_t *args, int nargs) { 656 if (nargs < 3) return js_mkerr(js, "txn.put(db, key, value, options?) requires db, key, and value"); 657 658 lmdb_txn_handle_t *txn = get_txn_handle(js, js_getthis(js), true); 659 if (!txn) return js_mkerr(js, "Invalid or closed LMDB transaction"); 660 if (txn->read_only) return js_mkerr(js, "Cannot write in read-only transaction"); 661 662 lmdb_db_handle_t *db = get_db_handle(js, args[0], true); 663 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 664 if (db->env != txn->env) return js_mkerr(js, "Database and transaction belong to different envs"); 665 666 MDB_val key; 667 MDB_val value; 668 if (!js_to_mdb_val(js, args[1], &key)) return js_mkerr(js, "LMDB key must be string, ArrayBuffer, or TypedArray"); 669 if (!js_to_mdb_val(js, args[2], &value)) return js_mkerr(js, "LMDB value must be string, ArrayBuffer, or TypedArray"); 670 671 ant_value_t options = nargs > 3 ? args[3] : js_mkundef(); 672 unsigned int flags = 0; 673 if (option_bool(js, options, "noOverwrite", false)) flags |= MDB_NOOVERWRITE; 674 if (option_bool(js, options, "noDupData", false)) flags |= MDB_NODUPDATA; 675 if (option_bool(js, options, "append", false)) flags |= MDB_APPEND; 676 if (option_bool(js, options, "appendDup", false)) flags |= MDB_APPENDDUP; 677 678 int rc = mdb_put(txn->txn, db->dbi, &key, &value, flags); 679 if (rc == MDB_KEYEXIST) return js_false; 680 if (rc != 0) return js_mkerr(js, "lmdb_put failed: %s", mdb_strerror(rc)); 681 return js_true; 682} 683 684static ant_value_t lmdb_txn_del(ant_t *js, ant_value_t *args, int nargs) { 685 if (nargs < 2) return js_mkerr(js, "txn.del(db, key, value?) requires db and key"); 686 687 lmdb_txn_handle_t *txn = get_txn_handle(js, js_getthis(js), true); 688 if (!txn) return js_mkerr(js, "Invalid or closed LMDB transaction"); 689 if (txn->read_only) return js_mkerr(js, "Cannot delete in read-only transaction"); 690 691 lmdb_db_handle_t *db = get_db_handle(js, args[0], true); 692 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 693 if (db->env != txn->env) return js_mkerr(js, "Database and transaction belong to different envs"); 694 695 MDB_val key; 696 if (!js_to_mdb_val(js, args[1], &key)) return js_mkerr(js, "LMDB key must be string, ArrayBuffer, or TypedArray"); 697 698 MDB_val value; 699 MDB_val *value_ptr = NULL; 700 if (nargs > 2 && vtype(args[2]) != T_UNDEF) { 701 if (!js_to_mdb_val(js, args[2], &value)) return js_mkerr(js, "LMDB value must be string, ArrayBuffer, or TypedArray"); 702 value_ptr = &value; 703 } 704 705 int rc = mdb_del(txn->txn, db->dbi, &key, value_ptr); 706 if (rc == MDB_NOTFOUND) return js_false; 707 if (rc != 0) return js_mkerr(js, "lmdb_del failed: %s", mdb_strerror(rc)); 708 return js_true; 709} 710 711static ant_value_t lmdb_txn_commit(ant_t *js, ant_value_t *args, int nargs) { 712 ant_value_t self = js_getthis(js); 713 lmdb_txn_handle_t *txn = get_txn_handle(js, self, true); 714 if (!txn) return js_mkerr(js, "Invalid or closed LMDB transaction"); 715 716 int rc = txn->read_only ? MDB_SUCCESS : mdb_txn_commit(txn->txn); 717 if (txn->read_only) mdb_txn_abort(txn->txn); 718 719 txn->txn = NULL; 720 txn->closed = true; 721 if (txn->env) list_remove_txn(txn->env, txn); 722 unregister_txn_ref_by_obj(self); 723 724 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 725 return js_mkundef(); 726} 727 728static ant_value_t lmdb_txn_abort(ant_t *js, ant_value_t *args, int nargs) { 729 ant_value_t self = js_getthis(js); 730 lmdb_txn_handle_t *txn = get_txn_handle(js, self, false); 731 if (!txn) return js_mkerr(js, "Invalid LMDB transaction"); 732 if (txn->closed || !txn->txn) { 733 unregister_txn_ref_by_obj(self); 734 return js_mkundef(); 735 } 736 737 mdb_txn_abort(txn->txn); 738 txn->txn = NULL; 739 txn->closed = true; 740 if (txn->env) list_remove_txn(txn->env, txn); 741 unregister_txn_ref_by_obj(self); 742 return js_mkundef(); 743} 744 745static ant_value_t lmdb_db_get_impl(ant_t *js, ant_value_t *args, int nargs, bool as_string) { 746 if (nargs < 1) return js_mkerr(js, "db.getBytes/getString(key) requires key"); 747 lmdb_db_handle_t *db = get_db_handle(js, js_getthis(js), true); 748 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 749 750 MDB_txn *txn = NULL; 751 int rc = mdb_txn_begin(db->env->env, NULL, MDB_RDONLY, &txn); 752 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 753 754 MDB_val key; 755 MDB_val value; 756 if (!js_to_mdb_val(js, args[0], &key)) { 757 mdb_txn_abort(txn); 758 return js_mkerr(js, "LMDB key must be string, ArrayBuffer, or TypedArray"); 759 } 760 761 rc = mdb_get(txn, db->dbi, &key, &value); 762 if (rc == MDB_NOTFOUND) { 763 mdb_txn_abort(txn); 764 return js_mkundef(); 765 } 766 if (rc != 0) { 767 mdb_txn_abort(txn); 768 return js_mkerr(js, "lmdb_get failed: %s", mdb_strerror(rc)); 769 } 770 771 ant_value_t out = mdb_val_to_js(js, &value, as_string); 772 mdb_txn_abort(txn); 773 return out; 774} 775 776static ant_value_t lmdb_db_get(ant_t *js, ant_value_t *args, int nargs) { 777 if (nargs < 1) return js_mkerr(js, "db.get(key, encoding?) requires key"); 778 bool as_string = false; 779 if (nargs > 1 && !parse_get_encoding(js, args[1], &as_string)) { 780 return js_mkerr(js, "db.get encoding must be 'utf8' or 'bytes'"); 781 } 782 return lmdb_db_get_impl(js, args, nargs, as_string); 783} 784 785static ant_value_t lmdb_db_get_bytes(ant_t *js, ant_value_t *args, int nargs) { 786 return lmdb_db_get_impl(js, args, nargs, false); 787} 788 789static ant_value_t lmdb_db_get_string(ant_t *js, ant_value_t *args, int nargs) { 790 return lmdb_db_get_impl(js, args, nargs, true); 791} 792 793static ant_value_t lmdb_db_put(ant_t *js, ant_value_t *args, int nargs) { 794 if (nargs < 2) return js_mkerr(js, "db.put(key, value, options?) requires key and value"); 795 lmdb_db_handle_t *db = get_db_handle(js, js_getthis(js), true); 796 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 797 if (db->env->read_only) return js_mkerr(js, "Cannot write on read-only LMDB env"); 798 799 MDB_txn *txn = NULL; 800 int rc = mdb_txn_begin(db->env->env, NULL, 0, &txn); 801 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 802 803 MDB_val key; 804 MDB_val value; 805 if (!js_to_mdb_val(js, args[0], &key) || !js_to_mdb_val(js, args[1], &value)) { 806 mdb_txn_abort(txn); 807 return js_mkerr(js, "LMDB key/value must be string, ArrayBuffer, or TypedArray"); 808 } 809 810 ant_value_t options = nargs > 2 ? args[2] : js_mkundef(); 811 unsigned int flags = 0; 812 if (option_bool(js, options, "noOverwrite", false)) flags |= MDB_NOOVERWRITE; 813 if (option_bool(js, options, "noDupData", false)) flags |= MDB_NODUPDATA; 814 if (option_bool(js, options, "append", false)) flags |= MDB_APPEND; 815 if (option_bool(js, options, "appendDup", false)) flags |= MDB_APPENDDUP; 816 817 rc = mdb_put(txn, db->dbi, &key, &value, flags); 818 if (rc == MDB_KEYEXIST) { 819 mdb_txn_abort(txn); 820 return js_false; 821 } 822 if (rc != 0) { 823 mdb_txn_abort(txn); 824 return js_mkerr(js, "lmdb_put failed: %s", mdb_strerror(rc)); 825 } 826 827 rc = mdb_txn_commit(txn); 828 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 829 return js_true; 830} 831 832static ant_value_t lmdb_db_del(ant_t *js, ant_value_t *args, int nargs) { 833 if (nargs < 1) return js_mkerr(js, "db.del(key, value?) requires key"); 834 lmdb_db_handle_t *db = get_db_handle(js, js_getthis(js), true); 835 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 836 if (db->env->read_only) return js_mkerr(js, "Cannot delete on read-only LMDB env"); 837 838 MDB_txn *txn = NULL; 839 int rc = mdb_txn_begin(db->env->env, NULL, 0, &txn); 840 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 841 842 MDB_val key; 843 if (!js_to_mdb_val(js, args[0], &key)) { 844 mdb_txn_abort(txn); 845 return js_mkerr(js, "LMDB key must be string, ArrayBuffer, or TypedArray"); 846 } 847 848 MDB_val value; 849 MDB_val *value_ptr = NULL; 850 if (nargs > 1 && vtype(args[1]) != T_UNDEF) { 851 if (!js_to_mdb_val(js, args[1], &value)) { 852 mdb_txn_abort(txn); 853 return js_mkerr(js, "LMDB value must be string, ArrayBuffer, or TypedArray"); 854 } 855 value_ptr = &value; 856 } 857 858 rc = mdb_del(txn, db->dbi, &key, value_ptr); 859 if (rc == MDB_NOTFOUND) { 860 mdb_txn_abort(txn); 861 return js_false; 862 } 863 if (rc != 0) { 864 mdb_txn_abort(txn); 865 return js_mkerr(js, "lmdb_del failed: %s", mdb_strerror(rc)); 866 } 867 868 rc = mdb_txn_commit(txn); 869 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 870 return js_true; 871} 872 873static ant_value_t lmdb_db_clear(ant_t *js, ant_value_t *args, int nargs) { 874 lmdb_db_handle_t *db = get_db_handle(js, js_getthis(js), true); 875 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 876 if (db->env->read_only) return js_mkerr(js, "Cannot clear on read-only LMDB env"); 877 878 MDB_txn *txn = NULL; 879 int rc = mdb_txn_begin(db->env->env, NULL, 0, &txn); 880 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 881 882 rc = mdb_drop(txn, db->dbi, 0); 883 if (rc != 0) { 884 mdb_txn_abort(txn); 885 return js_mkerr(js, "lmdb_drop(clear) failed: %s", mdb_strerror(rc)); 886 } 887 888 rc = mdb_txn_commit(txn); 889 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 890 return js_mkundef(); 891} 892 893static ant_value_t lmdb_db_drop(ant_t *js, ant_value_t *args, int nargs) { 894 ant_value_t self = js_getthis(js); 895 lmdb_db_handle_t *db = get_db_handle(js, self, true); 896 if (!db) return js_mkerr(js, "Invalid or closed LMDB database handle"); 897 if (db->env->read_only) return js_mkerr(js, "Cannot drop on read-only LMDB env"); 898 899 bool del_db = true; 900 if (nargs > 0 && vtype(args[0]) == T_OBJ) { 901 del_db = option_bool(js, args[0], "delete", true); 902 } else if (nargs > 0 && vtype(args[0]) != T_UNDEF) { 903 del_db = js_truthy(js, args[0]); 904 } 905 906 MDB_txn *txn = NULL; 907 int rc = mdb_txn_begin(db->env->env, NULL, 0, &txn); 908 if (rc != 0) return js_mkerr(js, "lmdb_txn_begin failed: %s", mdb_strerror(rc)); 909 910 rc = mdb_drop(txn, db->dbi, del_db ? 1 : 0); 911 if (rc != 0) { 912 mdb_txn_abort(txn); 913 return js_mkerr(js, "lmdb_drop failed: %s", mdb_strerror(rc)); 914 } 915 916 rc = mdb_txn_commit(txn); 917 if (rc != 0) return js_mkerr(js, "lmdb_txn_commit failed: %s", mdb_strerror(rc)); 918 919 if (del_db) { 920 db->closed = true; 921 if (db->env) list_remove_db(db->env, db); 922 unregister_db_ref_by_obj(self); 923 } 924 return js_mkundef(); 925} 926 927static ant_value_t lmdb_db_close(ant_t *js, ant_value_t *args, int nargs) { 928 (void)args; 929 (void)nargs; 930 ant_value_t self = js_getthis(js); 931 lmdb_db_handle_t *db = get_db_handle(js, self, false); 932 if (!db) return js_mkerr(js, "Invalid LMDB database handle"); 933 if (db->closed) { 934 unregister_db_ref_by_obj(self); 935 return js_mkundef(); 936 } 937 938 if (db->env && !db->env->closed) { 939 mdb_dbi_close(db->env->env, db->dbi); 940 list_remove_db(db->env, db); 941 } 942 943 db->closed = true; 944 unregister_db_ref_by_obj(self); 945 return js_mkundef(); 946} 947 948static ant_value_t lmdb_strerror_fn(ant_t *js, ant_value_t *args, int nargs) { 949 if (nargs < 1 || vtype(args[0]) != T_NUM) { 950 return js_mkerr(js, "lmdb.strerror(code) requires a numeric code"); 951 } 952 int code = (int)js_getnum(args[0]); 953 const char *err = mdb_strerror(code); 954 return js_mkstr(js, err, strlen(err)); 955} 956 957static ant_value_t lmdb_env_constructor(ant_t *js, ant_value_t *args, int nargs) { 958 return js_mkerr(js, "LMDBEnv cannot be constructed directly; use lmdb.open()"); 959} 960 961static ant_value_t lmdb_db_constructor(ant_t *js, ant_value_t *args, int nargs) { 962 return js_mkerr(js, "LMDBDatabase cannot be constructed directly; use env.openDB()"); 963} 964 965static ant_value_t lmdb_txn_constructor(ant_t *js, ant_value_t *args, int nargs) { 966 return js_mkerr(js, "LMDBTxn cannot be constructed directly; use env.beginTxn()"); 967} 968 969static void ensure_lmdb_prototypes(ant_t *js) { 970 if (lmdb_types.ready) return; 971 ant_value_t object_proto = js->sym.object_proto; 972 973 ant_value_t env_ctor_obj = js_mkobj(js); 974 ant_value_t env_proto = js_mkobj(js); 975 976 js_set_proto_init(env_proto, object_proto); 977 js_set(js, env_proto, "openDB", js_mkfun(lmdb_env_open_db)); 978 js_set(js, env_proto, "beginTxn", js_mkfun(lmdb_env_begin_txn)); 979 js_set(js, env_proto, "close", js_mkfun(lmdb_env_close_method)); 980 js_set(js, env_proto, "sync", js_mkfun(lmdb_env_sync_method)); 981 js_set(js, env_proto, "stat", js_mkfun(lmdb_env_stat_method)); 982 js_set(js, env_proto, "info", js_mkfun(lmdb_env_info_method)); 983 js_set_sym(js, env_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBEnv", 7)); 984 js_set_slot(env_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_env_constructor)); 985 js_mkprop_fast(js, env_ctor_obj, "prototype", 9, env_proto); 986 js_mkprop_fast(js, env_ctor_obj, "name", 4, ANT_STRING("LMDBEnv")); 987 js_set_descriptor(js, env_ctor_obj, "name", 4, 0); 988 989 ant_value_t db_ctor_obj = js_mkobj(js); 990 ant_value_t db_proto = js_mkobj(js); 991 992 js_set_proto_init(db_proto, object_proto); 993 js_set(js, db_proto, "get", js_mkfun(lmdb_db_get)); 994 js_set(js, db_proto, "getBytes", js_mkfun(lmdb_db_get_bytes)); 995 js_set(js, db_proto, "getString", js_mkfun(lmdb_db_get_string)); 996 js_set(js, db_proto, "put", js_mkfun(lmdb_db_put)); 997 js_set(js, db_proto, "del", js_mkfun(lmdb_db_del)); 998 js_set(js, db_proto, "clear", js_mkfun(lmdb_db_clear)); 999 js_set(js, db_proto, "drop", js_mkfun(lmdb_db_drop)); 1000 js_set(js, db_proto, "close", js_mkfun(lmdb_db_close)); 1001 js_set_sym(js, db_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBDatabase", 12)); 1002 js_set_slot(db_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_db_constructor)); 1003 js_mkprop_fast(js, db_ctor_obj, "prototype", 9, db_proto); 1004 js_mkprop_fast(js, db_ctor_obj, "name", 4, ANT_STRING("LMDBDatabase")); 1005 js_set_descriptor(js, db_ctor_obj, "name", 4, 0); 1006 1007 ant_value_t txn_ctor_obj = js_mkobj(js); 1008 ant_value_t txn_proto = js_mkobj(js); 1009 1010 js_set_proto_init(txn_proto, object_proto); 1011 js_set(js, txn_proto, "get", js_mkfun(lmdb_txn_get)); 1012 js_set(js, txn_proto, "getBytes", js_mkfun(lmdb_txn_get_bytes)); 1013 js_set(js, txn_proto, "getString", js_mkfun(lmdb_txn_get_string)); 1014 js_set(js, txn_proto, "put", js_mkfun(lmdb_txn_put)); 1015 js_set(js, txn_proto, "del", js_mkfun(lmdb_txn_del)); 1016 js_set(js, txn_proto, "commit", js_mkfun(lmdb_txn_commit)); 1017 js_set(js, txn_proto, "abort", js_mkfun(lmdb_txn_abort)); 1018 js_set_sym(js, txn_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBTxn", 7)); 1019 js_set_slot(txn_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_txn_constructor)); 1020 js_mkprop_fast(js, txn_ctor_obj, "prototype", 9, txn_proto); 1021 js_mkprop_fast(js, txn_ctor_obj, "name", 4, ANT_STRING("LMDBTxn")); 1022 js_set_descriptor(js, txn_ctor_obj, "name", 4, 0); 1023 1024 lmdb_types.env_ctor = js_obj_to_func(env_ctor_obj); 1025 lmdb_types.db_ctor = js_obj_to_func(db_ctor_obj); 1026 lmdb_types.txn_ctor = js_obj_to_func(txn_ctor_obj); 1027 lmdb_types.env_proto = env_proto; 1028 lmdb_types.db_proto = db_proto; 1029 lmdb_types.txn_proto = txn_proto; 1030 lmdb_types.ready = true; 1031} 1032 1033static ant_value_t make_env_obj(ant_t *js, lmdb_env_handle_t *env) { 1034 ensure_lmdb_prototypes(js); 1035 ant_value_t obj = js_mkobj(js); 1036 js_set_slot(obj, SLOT_DATA, ANT_PTR(env)); 1037 register_env_ref(obj, env); 1038 if (is_special_object(lmdb_types.env_proto)) js_set_proto_init(obj, lmdb_types.env_proto); 1039 return obj; 1040} 1041 1042static ant_value_t make_db_obj(ant_t *js, lmdb_db_handle_t *db) { 1043 ensure_lmdb_prototypes(js); 1044 ant_value_t obj = js_mkobj(js); 1045 js_set_slot(obj, SLOT_DATA, ANT_PTR(db)); 1046 register_db_ref(obj, db); 1047 if (is_special_object(lmdb_types.db_proto)) js_set_proto_init(obj, lmdb_types.db_proto); 1048 return obj; 1049} 1050 1051static ant_value_t make_txn_obj(ant_t *js, lmdb_txn_handle_t *txn) { 1052 ensure_lmdb_prototypes(js); 1053 ant_value_t obj = js_mkobj(js); 1054 js_set_slot(obj, SLOT_DATA, ANT_PTR(txn)); 1055 register_txn_ref(obj, txn); 1056 if (is_special_object(lmdb_types.txn_proto)) js_set_proto_init(obj, lmdb_types.txn_proto); 1057 return obj; 1058} 1059 1060ant_value_t lmdb_library(ant_t *js) { 1061 ensure_lmdb_prototypes(js); 1062 ant_value_t lib = js_mkobj(js); 1063 js_set(js, lib, "open", js_mkfun(lmdb_open)); 1064 js_set(js, lib, "strerror", js_mkfun(lmdb_strerror_fn)); 1065 js_set(js, lib, "Env", lmdb_types.env_ctor); 1066 js_set(js, lib, "Database", lmdb_types.db_ctor); 1067 js_set(js, lib, "Txn", lmdb_types.txn_ctor); 1068 1069 int major = 0; int minor = 0; int patch = 0; 1070 const char *version = mdb_version(&major, &minor, &patch); 1071 1072 js_set(js, lib, "version", js_mkstr(js, version, strlen(version))); 1073 js_set(js, lib, "versionMajor", js_mknum((double)major)); 1074 js_set(js, lib, "versionMinor", js_mknum((double)minor)); 1075 js_set(js, lib, "versionPatch", js_mknum((double)patch)); 1076 1077 ant_value_t constants = js_mkobj(js); 1078 js_set(js, constants, "NOOVERWRITE", js_mknum((double)MDB_NOOVERWRITE)); 1079 js_set(js, constants, "NODUPDATA", js_mknum((double)MDB_NODUPDATA)); 1080 js_set(js, constants, "APPEND", js_mknum((double)MDB_APPEND)); 1081 js_set(js, constants, "APPENDDUP", js_mknum((double)MDB_APPENDDUP)); 1082 js_set(js, constants, "NOSUBDIR", js_mknum((double)MDB_NOSUBDIR)); 1083 js_set(js, constants, "NOSYNC", js_mknum((double)MDB_NOSYNC)); 1084 js_set(js, constants, "NOMETASYNC", js_mknum((double)MDB_NOMETASYNC)); 1085 js_set(js, constants, "WRITEMAP", js_mknum((double)MDB_WRITEMAP)); 1086 js_set(js, constants, "MAPASYNC", js_mknum((double)MDB_MAPASYNC)); 1087 js_set(js, constants, "NOTLS", js_mknum((double)MDB_NOTLS)); 1088 js_set(js, constants, "NOLOCK", js_mknum((double)MDB_NOLOCK)); 1089 js_set(js, constants, "NORDAHEAD", js_mknum((double)MDB_NORDAHEAD)); 1090 js_set(js, constants, "NOMEMINIT", js_mknum((double)MDB_NOMEMINIT)); 1091 js_set(js, lib, "constants", constants); 1092 1093 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "lmdb", 4)); 1094 return lib; 1095} 1096 1097void gc_mark_lmdb(ant_t *js, gc_mark_fn mark) { 1098 if (lmdb_types.ready) { 1099 mark(js, lmdb_types.env_ctor); 1100 mark(js, lmdb_types.db_ctor); 1101 mark(js, lmdb_types.txn_ctor); 1102 mark(js, lmdb_types.env_proto); 1103 mark(js, lmdb_types.db_proto); 1104 mark(js, lmdb_types.txn_proto); 1105 } 1106 for (lmdb_env_ref_t *ref = env_refs; ref; ref = ref->next) mark(js, ref->obj); 1107 for (lmdb_db_ref_t *ref = db_refs; ref; ref = ref->next) mark(js, ref->obj); 1108 for (lmdb_txn_ref_t *ref = txn_refs; ref; ref = ref->next) mark(js, ref->obj); 1109} 1110 1111void cleanup_lmdb_module(void) { 1112 lmdb_txn_handle_t *txn = txn_handles; 1113 while (txn) { 1114 if (!txn->closed && txn->txn) { 1115 mdb_txn_abort(txn->txn); 1116 txn->txn = NULL; 1117 txn->closed = true; 1118 } 1119 txn = txn->next_global; 1120 } 1121 1122 lmdb_db_handle_t *db = db_handles; 1123 while (db) { 1124 if (!db->closed && db->env && !db->env->closed) { 1125 mdb_dbi_close(db->env->env, db->dbi); 1126 db->closed = true; 1127 } 1128 db = db->next_global; 1129 } 1130 1131 lmdb_env_handle_t *env = env_handles; 1132 while (env) { 1133 if (!env->closed) env_handle_close(env); 1134 env = env->next_global; 1135 } 1136 1137 txn = txn_handles; 1138 while (txn) { 1139 lmdb_txn_handle_t *next = txn->next_global; 1140 free(txn); 1141 txn = next; 1142 } 1143 txn_handles = NULL; 1144 1145 db = db_handles; 1146 while (db) { 1147 lmdb_db_handle_t *next = db->next_global; 1148 free(db->name); 1149 free(db); 1150 db = next; 1151 } 1152 db_handles = NULL; 1153 1154 env = env_handles; 1155 while (env) { 1156 lmdb_env_handle_t *next = env->next_global; 1157 free(env->path); 1158 free(env); 1159 env = next; 1160 } 1161 env_handles = NULL; 1162 1163 while (env_refs) { 1164 lmdb_env_ref_t *next = env_refs->next; 1165 free(env_refs); 1166 env_refs = next; 1167 } 1168 1169 while (db_refs) { 1170 lmdb_db_ref_t *next = db_refs->next; 1171 free(db_refs); 1172 db_refs = next; 1173 } 1174 1175 while (txn_refs) { 1176 lmdb_txn_ref_t *next = txn_refs->next; 1177 free(txn_refs); 1178 txn_refs = next; 1179 } 1180 1181 lmdb_types = (lmdb_js_types_t){0}; 1182}