Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2916132
lmdb.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
37 KB
Referenced Files
None
Subscribers
None
lmdb.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
"ant.h"
#include
"arena.h"
#include
"errors.h"
#include
"internal.h"
#include
"modules/lmdb.h"
#include
"modules/buffer.h"
#include
"modules/symbol.h"
#include
"descriptors.h"
#include
<lmdb.h>
#include
<stdbool.h>
#include
<stdint.h>
#include
<stdlib.h>
#include
<string.h>
struct
lmdb_env_handle
{
MDB_env
*
env
;
bool
closed
;
bool
read_only
;
char
*
path
;
lmdb_db_handle_t
*
db_head
;
lmdb_txn_handle_t
*
txn_head
;
lmdb_env_handle_t
*
next_global
;
};
struct
lmdb_db_handle
{
MDB_dbi
dbi
;
bool
closed
;
char
*
name
;
lmdb_env_handle_t
*
env
;
lmdb_db_handle_t
*
next_in_env
;
lmdb_db_handle_t
*
next_global
;
};
struct
lmdb_txn_handle
{
MDB_txn
*
txn
;
bool
closed
;
bool
read_only
;
lmdb_env_handle_t
*
env
;
lmdb_txn_handle_t
*
next_in_env
;
lmdb_txn_handle_t
*
next_global
;
};
struct
lmdb_env_ref
{
jsval_t
obj
;
lmdb_env_handle_t
*
env
;
lmdb_env_ref_t
*
next
;
};
struct
lmdb_db_ref
{
jsval_t
obj
;
lmdb_db_handle_t
*
db
;
lmdb_db_ref_t
*
next
;
};
struct
lmdb_txn_ref
{
jsval_t
obj
;
lmdb_txn_handle_t
*
txn
;
lmdb_txn_ref_t
*
next
;
};
static
lmdb_js_types_t
lmdb_types
=
{
0
};
static
lmdb_env_handle_t
*
env_handles
=
NULL
;
static
lmdb_db_handle_t
*
db_handles
=
NULL
;
static
lmdb_txn_handle_t
*
txn_handles
=
NULL
;
static
lmdb_env_ref_t
*
env_refs
=
NULL
;
static
lmdb_db_ref_t
*
db_refs
=
NULL
;
static
lmdb_txn_ref_t
*
txn_refs
=
NULL
;
static
jsval_t
make_env_obj
(
ant_t
*
js
,
lmdb_env_handle_t
*
env
);
static
jsval_t
make_db_obj
(
ant_t
*
js
,
lmdb_db_handle_t
*
db
);
static
jsval_t
make_txn_obj
(
ant_t
*
js
,
lmdb_txn_handle_t
*
txn
);
static
void
list_remove_db
(
lmdb_env_handle_t
*
env
,
lmdb_db_handle_t
*
target
)
{
if
(
!
env
||
!
target
)
return
;
lmdb_db_handle_t
**
cur
=
&
env
->
db_head
;
while
(
*
cur
)
{
if
(
*
cur
==
target
)
{
*
cur
=
target
->
next_in_env
;
target
->
next_in_env
=
NULL
;
return
;
}
cur
=
&
(
*
cur
)
->
next_in_env
;
}
}
static
void
list_remove_txn
(
lmdb_env_handle_t
*
env
,
lmdb_txn_handle_t
*
target
)
{
if
(
!
env
||
!
target
)
return
;
lmdb_txn_handle_t
**
cur
=
&
env
->
txn_head
;
while
(
*
cur
)
{
if
(
*
cur
==
target
)
{
*
cur
=
target
->
next_in_env
;
target
->
next_in_env
=
NULL
;
return
;
}
cur
=
&
(
*
cur
)
->
next_in_env
;
}
}
static
void
register_env_ref
(
jsval_t
obj
,
lmdb_env_handle_t
*
env
)
{
lmdb_env_ref_t
*
ref
=
ant_calloc
(
sizeof
(
lmdb_env_ref_t
));
if
(
!
ref
)
return
;
ref
->
obj
=
obj
;
ref
->
env
=
env
;
ref
->
next
=
env_refs
;
env_refs
=
ref
;
}
static
void
register_db_ref
(
jsval_t
obj
,
lmdb_db_handle_t
*
db
)
{
lmdb_db_ref_t
*
ref
=
ant_calloc
(
sizeof
(
lmdb_db_ref_t
));
if
(
!
ref
)
return
;
ref
->
obj
=
obj
;
ref
->
db
=
db
;
ref
->
next
=
db_refs
;
db_refs
=
ref
;
}
static
void
register_txn_ref
(
jsval_t
obj
,
lmdb_txn_handle_t
*
txn
)
{
lmdb_txn_ref_t
*
ref
=
ant_calloc
(
sizeof
(
lmdb_txn_ref_t
));
if
(
!
ref
)
return
;
ref
->
obj
=
obj
;
ref
->
txn
=
txn
;
ref
->
next
=
txn_refs
;
txn_refs
=
ref
;
}
static
lmdb_env_handle_t
*
find_env_by_obj
(
jsval_t
obj
)
{
for
(
lmdb_env_ref_t
*
ref
=
env_refs
;
ref
;
ref
=
ref
->
next
)
if
(
ref
->
obj
==
obj
)
return
ref
->
env
;
return
NULL
;
}
static
lmdb_db_handle_t
*
find_db_by_obj
(
jsval_t
obj
)
{
for
(
lmdb_db_ref_t
*
ref
=
db_refs
;
ref
;
ref
=
ref
->
next
)
if
(
ref
->
obj
==
obj
)
return
ref
->
db
;
return
NULL
;
}
static
lmdb_txn_handle_t
*
find_txn_by_obj
(
jsval_t
obj
)
{
for
(
lmdb_txn_ref_t
*
ref
=
txn_refs
;
ref
;
ref
=
ref
->
next
)
if
(
ref
->
obj
==
obj
)
return
ref
->
txn
;
return
NULL
;
}
static
void
unregister_env_ref_by_obj
(
jsval_t
obj
)
{
lmdb_env_ref_t
**
cur
=
&
env_refs
;
while
(
*
cur
)
{
if
((
*
cur
)
->
obj
==
obj
)
{
lmdb_env_ref_t
*
next
=
(
*
cur
)
->
next
;
free
(
*
cur
);
*
cur
=
next
;
return
;
}
cur
=
&
(
*
cur
)
->
next
;
}
}
static
void
unregister_db_ref_by_obj
(
jsval_t
obj
)
{
lmdb_db_ref_t
**
cur
=
&
db_refs
;
while
(
*
cur
)
{
if
((
*
cur
)
->
obj
==
obj
)
{
lmdb_db_ref_t
*
next
=
(
*
cur
)
->
next
;
free
(
*
cur
);
*
cur
=
next
;
return
;
}
cur
=
&
(
*
cur
)
->
next
;
}
}
static
void
unregister_txn_ref_by_obj
(
jsval_t
obj
)
{
lmdb_txn_ref_t
**
cur
=
&
txn_refs
;
while
(
*
cur
)
{
if
((
*
cur
)
->
obj
==
obj
)
{
lmdb_txn_ref_t
*
next
=
(
*
cur
)
->
next
;
free
(
*
cur
);
*
cur
=
next
;
return
;
}
cur
=
&
(
*
cur
)
->
next
;
}
}
static
void
unregister_db_refs_by_env
(
lmdb_env_handle_t
*
env
)
{
lmdb_db_ref_t
**
cur
=
&
db_refs
;
while
(
*
cur
)
{
if
((
*
cur
)
->
db
&&
(
*
cur
)
->
db
->
env
==
env
)
{
lmdb_db_ref_t
*
next
=
(
*
cur
)
->
next
;
free
(
*
cur
);
*
cur
=
next
;
continue
;
}
cur
=
&
(
*
cur
)
->
next
;
}
}
static
void
unregister_txn_refs_by_env
(
lmdb_env_handle_t
*
env
)
{
lmdb_txn_ref_t
**
cur
=
&
txn_refs
;
while
(
*
cur
)
{
if
((
*
cur
)
->
txn
&&
(
*
cur
)
->
txn
->
env
==
env
)
{
lmdb_txn_ref_t
*
next
=
(
*
cur
)
->
next
;
free
(
*
cur
);
*
cur
=
next
;
continue
;
}
cur
=
&
(
*
cur
)
->
next
;
}
}
static
void
env_handle_close
(
lmdb_env_handle_t
*
env
)
{
if
(
!
env
||
env
->
closed
)
return
;
lmdb_txn_handle_t
*
txn
=
env
->
txn_head
;
while
(
txn
)
{
if
(
!
txn
->
closed
&&
txn
->
txn
)
{
mdb_txn_abort
(
txn
->
txn
);
txn
->
txn
=
NULL
;
txn
->
closed
=
true
;
}
txn
=
txn
->
next_in_env
;
}
lmdb_db_handle_t
*
db
=
env
->
db_head
;
while
(
db
)
{
if
(
!
db
->
closed
)
{
mdb_dbi_close
(
env
->
env
,
db
->
dbi
);
db
->
closed
=
true
;
}
db
=
db
->
next_in_env
;
}
mdb_env_close
(
env
->
env
);
env
->
env
=
NULL
;
env
->
closed
=
true
;
env
->
db_head
=
NULL
;
env
->
txn_head
=
NULL
;
unregister_db_refs_by_env
(
env
);
unregister_txn_refs_by_env
(
env
);
}
static
lmdb_env_handle_t
*
get_env_handle
(
ant_t
*
js
,
jsval_t
obj
,
bool
open_required
)
{
lmdb_env_handle_t
*
env
=
find_env_by_obj
(
obj
);
if
(
!
env
)
return
NULL
;
if
(
open_required
&&
env
->
closed
)
return
NULL
;
return
env
;
}
static
lmdb_db_handle_t
*
get_db_handle
(
ant_t
*
js
,
jsval_t
obj
,
bool
open_required
)
{
lmdb_db_handle_t
*
db
=
find_db_by_obj
(
obj
);
if
(
!
db
)
return
NULL
;
if
(
open_required
&&
(
db
->
closed
||
!
db
->
env
||
db
->
env
->
closed
))
return
NULL
;
return
db
;
}
static
lmdb_txn_handle_t
*
get_txn_handle
(
ant_t
*
js
,
jsval_t
obj
,
bool
open_required
)
{
lmdb_txn_handle_t
*
txn
=
find_txn_by_obj
(
obj
);
if
(
!
txn
)
return
NULL
;
if
(
open_required
&&
(
txn
->
closed
||
!
txn
->
txn
))
return
NULL
;
return
txn
;
}
static
bool
option_bool
(
ant_t
*
js
,
jsval_t
options
,
const
char
*
key
,
bool
fallback
)
{
if
(
vtype
(
options
)
!=
T_OBJ
)
return
fallback
;
jsval_t
val
=
js_get
(
js
,
options
,
key
);
if
(
vtype
(
val
)
==
T_UNDEF
)
return
fallback
;
return
js_truthy
(
js
,
val
);
}
static
unsigned
int
option_uint
(
ant_t
*
js
,
jsval_t
options
,
const
char
*
key
,
unsigned
int
fallback
)
{
if
(
vtype
(
options
)
!=
T_OBJ
)
return
fallback
;
jsval_t
val
=
js_get
(
js
,
options
,
key
);
if
(
vtype
(
val
)
!=
T_NUM
)
return
fallback
;
double
n
=
js_getnum
(
val
);
if
(
n
<
0.0
)
return
fallback
;
return
(
unsigned
int
)
n
;
}
static
size_t
option_size
(
ant_t
*
js
,
jsval_t
options
,
const
char
*
key
,
size_t
fallback
)
{
if
(
vtype
(
options
)
!=
T_OBJ
)
return
fallback
;
jsval_t
val
=
js_get
(
js
,
options
,
key
);
if
(
vtype
(
val
)
!=
T_NUM
)
return
fallback
;
double
n
=
js_getnum
(
val
);
if
(
n
<
0.0
)
return
fallback
;
return
(
size_t
)
n
;
}
static
bool
js_to_mdb_val
(
ant_t
*
js
,
jsval_t
input
,
MDB_val
*
out
)
{
if
(
vtype
(
input
)
==
T_STR
)
{
size_t
len
=
0
;
const
char
*
str
=
js_getstr
(
js
,
input
,
&
len
);
if
(
!
str
)
return
false
;
out
->
mv_data
=
(
void
*
)
str
;
out
->
mv_size
=
len
;
return
true
;
}
if
(
vtype
(
input
)
==
T_OBJ
)
{
jsval_t
slot
=
js_get_slot
(
js
,
input
,
SLOT_BUFFER
);
if
(
vtype
(
slot
)
==
T_TYPEDARRAY
)
{
TypedArrayData
*
ta
=
(
TypedArrayData
*
)
js_gettypedarray
(
slot
);
if
(
!
ta
||
!
ta
->
buffer
||
!
ta
->
buffer
->
data
)
return
false
;
out
->
mv_data
=
(
void
*
)(
ta
->
buffer
->
data
+
ta
->
byte_offset
);
out
->
mv_size
=
ta
->
byte_length
;
return
true
;
}
if
(
vtype
(
slot
)
==
T_NUM
)
{
ArrayBufferData
*
ab
=
(
ArrayBufferData
*
)(
uintptr_t
)
js_getnum
(
slot
);
if
(
!
ab
||
!
ab
->
data
)
return
false
;
out
->
mv_data
=
(
void
*
)
ab
->
data
;
out
->
mv_size
=
ab
->
length
;
return
true
;
}
}
return
false
;
}
static
jsval_t
mdb_val_to_js
(
ant_t
*
js
,
MDB_val
*
val
,
bool
as_string
)
{
if
(
as_string
)
{
return
js_mkstr
(
js
,
val
->
mv_data
,
val
->
mv_size
);
}
ArrayBufferData
*
ab
=
create_array_buffer_data
(
val
->
mv_size
);
if
(
!
ab
)
return
js_mkerr
(
js
,
"Failed to allocate LMDB read buffer"
);
if
(
val
->
mv_size
>
0
&&
val
->
mv_data
)
{
memcpy
(
ab
->
data
,
val
->
mv_data
,
val
->
mv_size
);
}
jsval_t
out
=
create_typed_array
(
js
,
TYPED_ARRAY_UINT8
,
ab
,
0
,
ab
->
length
,
"Uint8Array"
);
if
(
vtype
(
out
)
==
T_ERR
)
free_array_buffer_data
(
ab
);
return
out
;
}
static
bool
parse_get_encoding
(
ant_t
*
js
,
jsval_t
encoding
,
bool
*
as_string
)
{
if
(
vtype
(
encoding
)
==
T_UNDEF
)
return
true
;
if
(
vtype
(
encoding
)
!=
T_STR
)
return
false
;
size_t
len
=
0
;
const
char
*
mode
=
js_getstr
(
js
,
encoding
,
&
len
);
if
(
!
mode
)
return
false
;
if
(
len
==
5
&&
memcmp
(
mode
,
"bytes"
,
5
)
==
0
)
{
*
as_string
=
false
;
return
true
;
}
if
(
len
==
4
&&
memcmp
(
mode
,
"utf8"
,
4
)
==
0
)
{
*
as_string
=
true
;
return
true
;
}
return
false
;
}
static
jsval_t
lmdb_open
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_STR
)
{
return
js_mkerr
(
js
,
"lmdb.open(path, options?) requires a string path"
);
}
size_t
path_len
=
0
;
const
char
*
path
=
js_getstr
(
js
,
args
[
0
],
&
path_len
);
if
(
!
path
||
path_len
==
0
)
{
return
js_mkerr
(
js
,
"lmdb.open path cannot be empty"
);
}
jsval_t
options
=
nargs
>
1
?
args
[
1
]
:
js_mkundef
();
bool
read_only
=
option_bool
(
js
,
options
,
"readOnly"
,
false
);
bool
no_subdir
=
option_bool
(
js
,
options
,
"noSubdir"
,
false
);
bool
no_sync
=
option_bool
(
js
,
options
,
"noSync"
,
false
);
bool
no_meta_sync
=
option_bool
(
js
,
options
,
"noMetaSync"
,
false
);
bool
no_read_ahead
=
option_bool
(
js
,
options
,
"noReadAhead"
,
false
);
bool
no_lock
=
option_bool
(
js
,
options
,
"noLock"
,
false
);
bool
write_map
=
option_bool
(
js
,
options
,
"writeMap"
,
false
);
bool
map_async
=
option_bool
(
js
,
options
,
"mapAsync"
,
false
);
size_t
map_size
=
option_size
(
js
,
options
,
"mapSize"
,
0
);
unsigned
int
max_readers
=
option_uint
(
js
,
options
,
"maxReaders"
,
0
);
unsigned
int
max_dbs
=
option_uint
(
js
,
options
,
"maxDbs"
,
0
);
unsigned
int
mode
=
option_uint
(
js
,
options
,
"mode"
,
0644U
);
unsigned
int
flags
=
0
;
if
(
read_only
)
flags
|=
MDB_RDONLY
;
if
(
no_subdir
)
flags
|=
MDB_NOSUBDIR
;
if
(
no_sync
)
flags
|=
MDB_NOSYNC
;
if
(
no_meta_sync
)
flags
|=
MDB_NOMETASYNC
;
if
(
no_read_ahead
)
flags
|=
MDB_NORDAHEAD
;
if
(
no_lock
)
flags
|=
MDB_NOLOCK
;
if
(
write_map
)
flags
|=
MDB_WRITEMAP
;
if
(
map_async
)
flags
|=
MDB_MAPASYNC
;
MDB_env
*
env
=
NULL
;
int
rc
=
mdb_env_create
(
&
env
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_env_create failed: %s"
,
mdb_strerror
(
rc
));
if
(
map_size
>
0
)
{
rc
=
mdb_env_set_mapsize
(
env
,
map_size
);
if
(
rc
!=
0
)
{
mdb_env_close
(
env
);
return
js_mkerr
(
js
,
"lmdb_env_set_mapsize failed: %s"
,
mdb_strerror
(
rc
));
}
}
if
(
max_readers
>
0
)
{
rc
=
mdb_env_set_maxreaders
(
env
,
max_readers
);
if
(
rc
!=
0
)
{
mdb_env_close
(
env
);
return
js_mkerr
(
js
,
"lmdb_env_set_maxreaders failed: %s"
,
mdb_strerror
(
rc
));
}
}
if
(
max_dbs
>
0
)
{
rc
=
mdb_env_set_maxdbs
(
env
,
max_dbs
);
if
(
rc
!=
0
)
{
mdb_env_close
(
env
);
return
js_mkerr
(
js
,
"lmdb_env_set_maxdbs failed: %s"
,
mdb_strerror
(
rc
));
}
}
rc
=
mdb_env_open
(
env
,
path
,
flags
,
(
mdb_mode_t
)
mode
);
if
(
rc
!=
0
)
{
mdb_env_close
(
env
);
return
js_mkerr
(
js
,
"lmdb_env_open('%s') failed: %s"
,
path
,
mdb_strerror
(
rc
));
}
lmdb_env_handle_t
*
handle
=
ant_calloc
(
sizeof
(
lmdb_env_handle_t
));
if
(
!
handle
)
{
mdb_env_close
(
env
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
handle
->
env
=
env
;
handle
->
closed
=
false
;
handle
->
read_only
=
read_only
;
handle
->
path
=
strndup
(
path
,
path_len
);
handle
->
next_global
=
env_handles
;
env_handles
=
handle
;
return
make_env_obj
(
js
,
handle
);
}
static
jsval_t
lmdb_env_open_db
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB env"
);
const
char
*
name
=
NULL
;
size_t
name_len
=
0
;
jsval_t
options
=
js_mkundef
();
if
(
nargs
>
0
&&
vtype
(
args
[
0
])
==
T_STR
)
{
name
=
js_getstr
(
js
,
args
[
0
],
&
name_len
);
if
(
nargs
>
1
)
options
=
args
[
1
];
}
else
if
(
nargs
>
0
&&
vtype
(
args
[
0
])
==
T_OBJ
)
{
options
=
args
[
0
];
}
bool
create
=
option_bool
(
js
,
options
,
"create"
,
false
);
bool
dup_sort
=
option_bool
(
js
,
options
,
"dupSort"
,
false
);
bool
dup_fixed
=
option_bool
(
js
,
options
,
"dupFixed"
,
false
);
bool
integer_key
=
option_bool
(
js
,
options
,
"integerKey"
,
false
);
bool
integer_dup
=
option_bool
(
js
,
options
,
"integerDup"
,
false
);
if
(
env
->
read_only
&&
create
)
{
return
js_mkerr
(
js
,
"Cannot open DB with create=true on a read-only env"
);
}
unsigned
int
dbi_flags
=
0
;
if
(
create
)
dbi_flags
|=
MDB_CREATE
;
if
(
dup_sort
)
dbi_flags
|=
MDB_DUPSORT
;
if
(
dup_fixed
)
dbi_flags
|=
MDB_DUPFIXED
;
if
(
integer_key
)
dbi_flags
|=
MDB_INTEGERKEY
;
if
(
integer_dup
)
dbi_flags
|=
MDB_INTEGERDUP
;
unsigned
int
txn_flags
=
env
->
read_only
?
MDB_RDONLY
:
0U
;
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
env
->
env
,
NULL
,
txn_flags
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
MDB_dbi
dbi
=
0
;
rc
=
mdb_dbi_open
(
txn
,
name
,
dbi_flags
,
&
dbi
);
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_dbi_open failed: %s"
,
mdb_strerror
(
rc
));
}
if
(
txn_flags
&
MDB_RDONLY
)
{
mdb_txn_abort
(
txn
);
}
else
{
rc
=
mdb_txn_commit
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
}
lmdb_db_handle_t
*
db
=
ant_calloc
(
sizeof
(
lmdb_db_handle_t
));
if
(
!
db
)
{
mdb_dbi_close
(
env
->
env
,
dbi
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
db
->
dbi
=
dbi
;
db
->
closed
=
false
;
db
->
env
=
env
;
db
->
name
=
name
?
strndup
(
name
,
name_len
)
:
NULL
;
db
->
next_in_env
=
env
->
db_head
;
env
->
db_head
=
db
;
db
->
next_global
=
db_handles
;
db_handles
=
db
;
return
make_db_obj
(
js
,
db
);
}
static
jsval_t
lmdb_env_begin_txn
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB env"
);
jsval_t
options
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
bool
read_only
=
option_bool
(
js
,
options
,
"readOnly"
,
env
->
read_only
);
if
(
env
->
read_only
&&
!
read_only
)
{
return
js_mkerr
(
js
,
"Cannot create read-write transaction on read-only env"
);
}
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
env
->
env
,
NULL
,
read_only
?
MDB_RDONLY
:
0U
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
lmdb_txn_handle_t
*
handle
=
ant_calloc
(
sizeof
(
lmdb_txn_handle_t
));
if
(
!
handle
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"Out of memory"
);
}
handle
->
txn
=
txn
;
handle
->
closed
=
false
;
handle
->
read_only
=
read_only
;
handle
->
env
=
env
;
handle
->
next_in_env
=
env
->
txn_head
;
env
->
txn_head
=
handle
;
handle
->
next_global
=
txn_handles
;
txn_handles
=
handle
;
return
make_txn_obj
(
js
,
handle
);
}
static
jsval_t
lmdb_env_close_method
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
jsval_t
self
=
js_getthis
(
js
);
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
self
,
false
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid LMDB env"
);
env_handle_close
(
env
);
unregister_env_ref_by_obj
(
self
);
return
js_mkundef
();
}
static
jsval_t
lmdb_env_sync_method
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB env"
);
bool
force
=
true
;
if
(
nargs
>
0
)
force
=
js_truthy
(
js
,
args
[
0
]);
int
rc
=
mdb_env_sync
(
env
->
env
,
force
?
1
:
0
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_env_sync failed: %s"
,
mdb_strerror
(
rc
));
return
js_mkundef
();
}
static
jsval_t
lmdb_env_stat_method
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB env"
);
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
env
->
env
,
NULL
,
MDB_RDONLY
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
MDB_stat
stat
;
rc
=
mdb_stat
(
txn
,
0
,
&
stat
);
mdb_txn_abort
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_stat failed: %s"
,
mdb_strerror
(
rc
));
jsval_t
out
=
js_mkobj
(
js
);
js_set
(
js
,
out
,
"psize"
,
js_mknum
((
double
)
stat
.
ms_psize
));
js_set
(
js
,
out
,
"depth"
,
js_mknum
((
double
)
stat
.
ms_depth
));
js_set
(
js
,
out
,
"branchPages"
,
js_mknum
((
double
)
stat
.
ms_branch_pages
));
js_set
(
js
,
out
,
"leafPages"
,
js_mknum
((
double
)
stat
.
ms_leaf_pages
));
js_set
(
js
,
out
,
"overflowPages"
,
js_mknum
((
double
)
stat
.
ms_overflow_pages
));
js_set
(
js
,
out
,
"entries"
,
js_mknum
((
double
)
stat
.
ms_entries
));
return
out
;
}
static
jsval_t
lmdb_env_info_method
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_env_handle_t
*
env
=
get_env_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
env
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB env"
);
MDB_envinfo
info
;
int
rc
=
mdb_env_info
(
env
->
env
,
&
info
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_env_info failed: %s"
,
mdb_strerror
(
rc
));
jsval_t
out
=
js_mkobj
(
js
);
js_set
(
js
,
out
,
"mapSize"
,
js_mknum
((
double
)
info
.
me_mapsize
));
js_set
(
js
,
out
,
"lastPgNo"
,
js_mknum
((
double
)
info
.
me_last_pgno
));
js_set
(
js
,
out
,
"lastTxnId"
,
js_mknum
((
double
)
info
.
me_last_txnid
));
js_set
(
js
,
out
,
"maxReaders"
,
js_mknum
((
double
)
info
.
me_maxreaders
));
js_set
(
js
,
out
,
"numReaders"
,
js_mknum
((
double
)
info
.
me_numreaders
));
return
out
;
}
static
jsval_t
lmdb_txn_get_impl
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
,
bool
as_string
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"txn.getBytes/getString(db, key) requires db and key"
);
lmdb_txn_handle_t
*
txn
=
get_txn_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
txn
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB transaction"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
args
[
0
],
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
!=
txn
->
env
)
return
js_mkerr
(
js
,
"Database and transaction belong to different envs"
);
MDB_val
key
;
if
(
!
js_to_mdb_val
(
js
,
args
[
1
],
&
key
))
{
return
js_mkerr
(
js
,
"LMDB key must be string, ArrayBuffer, or TypedArray"
);
}
MDB_val
value
;
int
rc
=
mdb_get
(
txn
->
txn
,
db
->
dbi
,
&
key
,
&
value
);
if
(
rc
==
MDB_NOTFOUND
)
return
js_mkundef
();
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_get failed: %s"
,
mdb_strerror
(
rc
));
return
mdb_val_to_js
(
js
,
&
value
,
as_string
);
}
static
jsval_t
lmdb_txn_get
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"txn.get(db, key, encoding?) requires db and key"
);
bool
as_string
=
false
;
if
(
nargs
>
2
&&
!
parse_get_encoding
(
js
,
args
[
2
],
&
as_string
))
{
return
js_mkerr
(
js
,
"txn.get encoding must be 'utf8' or 'bytes'"
);
}
return
lmdb_txn_get_impl
(
js
,
args
,
nargs
,
as_string
);
}
static
jsval_t
lmdb_txn_get_bytes
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
lmdb_txn_get_impl
(
js
,
args
,
nargs
,
false
);
}
static
jsval_t
lmdb_txn_get_string
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
lmdb_txn_get_impl
(
js
,
args
,
nargs
,
true
);
}
static
jsval_t
lmdb_txn_put
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
3
)
return
js_mkerr
(
js
,
"txn.put(db, key, value, options?) requires db, key, and value"
);
lmdb_txn_handle_t
*
txn
=
get_txn_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
txn
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB transaction"
);
if
(
txn
->
read_only
)
return
js_mkerr
(
js
,
"Cannot write in read-only transaction"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
args
[
0
],
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
!=
txn
->
env
)
return
js_mkerr
(
js
,
"Database and transaction belong to different envs"
);
MDB_val
key
;
MDB_val
value
;
if
(
!
js_to_mdb_val
(
js
,
args
[
1
],
&
key
))
return
js_mkerr
(
js
,
"LMDB key must be string, ArrayBuffer, or TypedArray"
);
if
(
!
js_to_mdb_val
(
js
,
args
[
2
],
&
value
))
return
js_mkerr
(
js
,
"LMDB value must be string, ArrayBuffer, or TypedArray"
);
jsval_t
options
=
nargs
>
3
?
args
[
3
]
:
js_mkundef
();
unsigned
int
flags
=
0
;
if
(
option_bool
(
js
,
options
,
"noOverwrite"
,
false
))
flags
|=
MDB_NOOVERWRITE
;
if
(
option_bool
(
js
,
options
,
"noDupData"
,
false
))
flags
|=
MDB_NODUPDATA
;
if
(
option_bool
(
js
,
options
,
"append"
,
false
))
flags
|=
MDB_APPEND
;
if
(
option_bool
(
js
,
options
,
"appendDup"
,
false
))
flags
|=
MDB_APPENDDUP
;
int
rc
=
mdb_put
(
txn
->
txn
,
db
->
dbi
,
&
key
,
&
value
,
flags
);
if
(
rc
==
MDB_KEYEXIST
)
return
js_false
;
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_put failed: %s"
,
mdb_strerror
(
rc
));
return
js_true
;
}
static
jsval_t
lmdb_txn_del
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"txn.del(db, key, value?) requires db and key"
);
lmdb_txn_handle_t
*
txn
=
get_txn_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
txn
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB transaction"
);
if
(
txn
->
read_only
)
return
js_mkerr
(
js
,
"Cannot delete in read-only transaction"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
args
[
0
],
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
!=
txn
->
env
)
return
js_mkerr
(
js
,
"Database and transaction belong to different envs"
);
MDB_val
key
;
if
(
!
js_to_mdb_val
(
js
,
args
[
1
],
&
key
))
return
js_mkerr
(
js
,
"LMDB key must be string, ArrayBuffer, or TypedArray"
);
MDB_val
value
;
MDB_val
*
value_ptr
=
NULL
;
if
(
nargs
>
2
&&
vtype
(
args
[
2
])
!=
T_UNDEF
)
{
if
(
!
js_to_mdb_val
(
js
,
args
[
2
],
&
value
))
return
js_mkerr
(
js
,
"LMDB value must be string, ArrayBuffer, or TypedArray"
);
value_ptr
=
&
value
;
}
int
rc
=
mdb_del
(
txn
->
txn
,
db
->
dbi
,
&
key
,
value_ptr
);
if
(
rc
==
MDB_NOTFOUND
)
return
js_false
;
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_del failed: %s"
,
mdb_strerror
(
rc
));
return
js_true
;
}
static
jsval_t
lmdb_txn_commit
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
jsval_t
self
=
js_getthis
(
js
);
lmdb_txn_handle_t
*
txn
=
get_txn_handle
(
js
,
self
,
true
);
if
(
!
txn
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB transaction"
);
int
rc
=
txn
->
read_only
?
MDB_SUCCESS
:
mdb_txn_commit
(
txn
->
txn
);
if
(
txn
->
read_only
)
mdb_txn_abort
(
txn
->
txn
);
txn
->
txn
=
NULL
;
txn
->
closed
=
true
;
if
(
txn
->
env
)
list_remove_txn
(
txn
->
env
,
txn
);
unregister_txn_ref_by_obj
(
self
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
return
js_mkundef
();
}
static
jsval_t
lmdb_txn_abort
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
jsval_t
self
=
js_getthis
(
js
);
lmdb_txn_handle_t
*
txn
=
get_txn_handle
(
js
,
self
,
false
);
if
(
!
txn
)
return
js_mkerr
(
js
,
"Invalid LMDB transaction"
);
if
(
txn
->
closed
||
!
txn
->
txn
)
{
unregister_txn_ref_by_obj
(
self
);
return
js_mkundef
();
}
mdb_txn_abort
(
txn
->
txn
);
txn
->
txn
=
NULL
;
txn
->
closed
=
true
;
if
(
txn
->
env
)
list_remove_txn
(
txn
->
env
,
txn
);
unregister_txn_ref_by_obj
(
self
);
return
js_mkundef
();
}
static
jsval_t
lmdb_db_get_impl
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
,
bool
as_string
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"db.getBytes/getString(key) requires key"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
db
->
env
->
env
,
NULL
,
MDB_RDONLY
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
MDB_val
key
;
MDB_val
value
;
if
(
!
js_to_mdb_val
(
js
,
args
[
0
],
&
key
))
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"LMDB key must be string, ArrayBuffer, or TypedArray"
);
}
rc
=
mdb_get
(
txn
,
db
->
dbi
,
&
key
,
&
value
);
if
(
rc
==
MDB_NOTFOUND
)
{
mdb_txn_abort
(
txn
);
return
js_mkundef
();
}
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_get failed: %s"
,
mdb_strerror
(
rc
));
}
jsval_t
out
=
mdb_val_to_js
(
js
,
&
value
,
as_string
);
mdb_txn_abort
(
txn
);
return
out
;
}
static
jsval_t
lmdb_db_get
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"db.get(key, encoding?) requires key"
);
bool
as_string
=
false
;
if
(
nargs
>
1
&&
!
parse_get_encoding
(
js
,
args
[
1
],
&
as_string
))
{
return
js_mkerr
(
js
,
"db.get encoding must be 'utf8' or 'bytes'"
);
}
return
lmdb_db_get_impl
(
js
,
args
,
nargs
,
as_string
);
}
static
jsval_t
lmdb_db_get_bytes
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
lmdb_db_get_impl
(
js
,
args
,
nargs
,
false
);
}
static
jsval_t
lmdb_db_get_string
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
lmdb_db_get_impl
(
js
,
args
,
nargs
,
true
);
}
static
jsval_t
lmdb_db_put
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"db.put(key, value, options?) requires key and value"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
->
read_only
)
return
js_mkerr
(
js
,
"Cannot write on read-only LMDB env"
);
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
db
->
env
->
env
,
NULL
,
0
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
MDB_val
key
;
MDB_val
value
;
if
(
!
js_to_mdb_val
(
js
,
args
[
0
],
&
key
)
||
!
js_to_mdb_val
(
js
,
args
[
1
],
&
value
))
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"LMDB key/value must be string, ArrayBuffer, or TypedArray"
);
}
jsval_t
options
=
nargs
>
2
?
args
[
2
]
:
js_mkundef
();
unsigned
int
flags
=
0
;
if
(
option_bool
(
js
,
options
,
"noOverwrite"
,
false
))
flags
|=
MDB_NOOVERWRITE
;
if
(
option_bool
(
js
,
options
,
"noDupData"
,
false
))
flags
|=
MDB_NODUPDATA
;
if
(
option_bool
(
js
,
options
,
"append"
,
false
))
flags
|=
MDB_APPEND
;
if
(
option_bool
(
js
,
options
,
"appendDup"
,
false
))
flags
|=
MDB_APPENDDUP
;
rc
=
mdb_put
(
txn
,
db
->
dbi
,
&
key
,
&
value
,
flags
);
if
(
rc
==
MDB_KEYEXIST
)
{
mdb_txn_abort
(
txn
);
return
js_false
;
}
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_put failed: %s"
,
mdb_strerror
(
rc
));
}
rc
=
mdb_txn_commit
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
return
js_true
;
}
static
jsval_t
lmdb_db_del
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"db.del(key, value?) requires key"
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
->
read_only
)
return
js_mkerr
(
js
,
"Cannot delete on read-only LMDB env"
);
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
db
->
env
->
env
,
NULL
,
0
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
MDB_val
key
;
if
(
!
js_to_mdb_val
(
js
,
args
[
0
],
&
key
))
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"LMDB key must be string, ArrayBuffer, or TypedArray"
);
}
MDB_val
value
;
MDB_val
*
value_ptr
=
NULL
;
if
(
nargs
>
1
&&
vtype
(
args
[
1
])
!=
T_UNDEF
)
{
if
(
!
js_to_mdb_val
(
js
,
args
[
1
],
&
value
))
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"LMDB value must be string, ArrayBuffer, or TypedArray"
);
}
value_ptr
=
&
value
;
}
rc
=
mdb_del
(
txn
,
db
->
dbi
,
&
key
,
value_ptr
);
if
(
rc
==
MDB_NOTFOUND
)
{
mdb_txn_abort
(
txn
);
return
js_false
;
}
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_del failed: %s"
,
mdb_strerror
(
rc
));
}
rc
=
mdb_txn_commit
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
return
js_true
;
}
static
jsval_t
lmdb_db_clear
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
js_getthis
(
js
),
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
->
read_only
)
return
js_mkerr
(
js
,
"Cannot clear on read-only LMDB env"
);
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
db
->
env
->
env
,
NULL
,
0
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
rc
=
mdb_drop
(
txn
,
db
->
dbi
,
0
);
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_drop(clear) failed: %s"
,
mdb_strerror
(
rc
));
}
rc
=
mdb_txn_commit
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
return
js_mkundef
();
}
static
jsval_t
lmdb_db_drop
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
jsval_t
self
=
js_getthis
(
js
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
self
,
true
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid or closed LMDB database handle"
);
if
(
db
->
env
->
read_only
)
return
js_mkerr
(
js
,
"Cannot drop on read-only LMDB env"
);
bool
del_db
=
true
;
if
(
nargs
>
0
&&
vtype
(
args
[
0
])
==
T_OBJ
)
{
del_db
=
option_bool
(
js
,
args
[
0
],
"delete"
,
true
);
}
else
if
(
nargs
>
0
&&
vtype
(
args
[
0
])
!=
T_UNDEF
)
{
del_db
=
js_truthy
(
js
,
args
[
0
]);
}
MDB_txn
*
txn
=
NULL
;
int
rc
=
mdb_txn_begin
(
db
->
env
->
env
,
NULL
,
0
,
&
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_begin failed: %s"
,
mdb_strerror
(
rc
));
rc
=
mdb_drop
(
txn
,
db
->
dbi
,
del_db
?
1
:
0
);
if
(
rc
!=
0
)
{
mdb_txn_abort
(
txn
);
return
js_mkerr
(
js
,
"lmdb_drop failed: %s"
,
mdb_strerror
(
rc
));
}
rc
=
mdb_txn_commit
(
txn
);
if
(
rc
!=
0
)
return
js_mkerr
(
js
,
"lmdb_txn_commit failed: %s"
,
mdb_strerror
(
rc
));
if
(
del_db
)
{
db
->
closed
=
true
;
if
(
db
->
env
)
list_remove_db
(
db
->
env
,
db
);
unregister_db_ref_by_obj
(
self
);
}
return
js_mkundef
();
}
static
jsval_t
lmdb_db_close
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
jsval_t
self
=
js_getthis
(
js
);
lmdb_db_handle_t
*
db
=
get_db_handle
(
js
,
self
,
false
);
if
(
!
db
)
return
js_mkerr
(
js
,
"Invalid LMDB database handle"
);
if
(
db
->
closed
)
{
unregister_db_ref_by_obj
(
self
);
return
js_mkundef
();
}
if
(
db
->
env
&&
!
db
->
env
->
closed
)
{
mdb_dbi_close
(
db
->
env
->
env
,
db
->
dbi
);
list_remove_db
(
db
->
env
,
db
);
}
db
->
closed
=
true
;
unregister_db_ref_by_obj
(
self
);
return
js_mkundef
();
}
static
jsval_t
lmdb_strerror_fn
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_NUM
)
{
return
js_mkerr
(
js
,
"lmdb.strerror(code) requires a numeric code"
);
}
int
code
=
(
int
)
js_getnum
(
args
[
0
]);
const
char
*
err
=
mdb_strerror
(
code
);
return
js_mkstr
(
js
,
err
,
strlen
(
err
));
}
static
jsval_t
lmdb_env_constructor
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
js_mkerr
(
js
,
"LMDBEnv cannot be constructed directly; use lmdb.open()"
);
}
static
jsval_t
lmdb_db_constructor
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
js_mkerr
(
js
,
"LMDBDatabase cannot be constructed directly; use env.openDB()"
);
}
static
jsval_t
lmdb_txn_constructor
(
ant_t
*
js
,
jsval_t
*
args
,
int
nargs
)
{
return
js_mkerr
(
js
,
"LMDBTxn cannot be constructed directly; use env.beginTxn()"
);
}
static
void
ensure_lmdb_prototypes
(
ant_t
*
js
)
{
if
(
lmdb_types
.
ready
)
return
;
jsval_t
object_proto
=
js
->
object
;
jsval_t
env_ctor_obj
=
js_mkobj
(
js
);
jsval_t
env_proto
=
js_mkobj
(
js
);
js_set_proto
(
js
,
env_proto
,
object_proto
);
js_set
(
js
,
env_proto
,
"openDB"
,
js_mkfun
(
lmdb_env_open_db
));
js_set
(
js
,
env_proto
,
"beginTxn"
,
js_mkfun
(
lmdb_env_begin_txn
));
js_set
(
js
,
env_proto
,
"close"
,
js_mkfun
(
lmdb_env_close_method
));
js_set
(
js
,
env_proto
,
"sync"
,
js_mkfun
(
lmdb_env_sync_method
));
js_set
(
js
,
env_proto
,
"stat"
,
js_mkfun
(
lmdb_env_stat_method
));
js_set
(
js
,
env_proto
,
"info"
,
js_mkfun
(
lmdb_env_info_method
));
js_set_sym
(
js
,
env_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"LMDBEnv"
,
7
));
js_set_slot
(
js
,
env_ctor_obj
,
SLOT_CFUNC
,
js_mkfun
(
lmdb_env_constructor
));
js_mkprop_fast
(
js
,
env_ctor_obj
,
"prototype"
,
9
,
env_proto
);
js_mkprop_fast
(
js
,
env_ctor_obj
,
"name"
,
4
,
ANT_STRING
(
"LMDBEnv"
));
js_set_descriptor
(
js
,
env_ctor_obj
,
"name"
,
4
,
0
);
jsval_t
db_ctor_obj
=
js_mkobj
(
js
);
jsval_t
db_proto
=
js_mkobj
(
js
);
js_set_proto
(
js
,
db_proto
,
object_proto
);
js_set
(
js
,
db_proto
,
"get"
,
js_mkfun
(
lmdb_db_get
));
js_set
(
js
,
db_proto
,
"getBytes"
,
js_mkfun
(
lmdb_db_get_bytes
));
js_set
(
js
,
db_proto
,
"getString"
,
js_mkfun
(
lmdb_db_get_string
));
js_set
(
js
,
db_proto
,
"put"
,
js_mkfun
(
lmdb_db_put
));
js_set
(
js
,
db_proto
,
"del"
,
js_mkfun
(
lmdb_db_del
));
js_set
(
js
,
db_proto
,
"clear"
,
js_mkfun
(
lmdb_db_clear
));
js_set
(
js
,
db_proto
,
"drop"
,
js_mkfun
(
lmdb_db_drop
));
js_set
(
js
,
db_proto
,
"close"
,
js_mkfun
(
lmdb_db_close
));
js_set_sym
(
js
,
db_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"LMDBDatabase"
,
12
));
js_set_slot
(
js
,
db_ctor_obj
,
SLOT_CFUNC
,
js_mkfun
(
lmdb_db_constructor
));
js_mkprop_fast
(
js
,
db_ctor_obj
,
"prototype"
,
9
,
db_proto
);
js_mkprop_fast
(
js
,
db_ctor_obj
,
"name"
,
4
,
ANT_STRING
(
"LMDBDatabase"
));
js_set_descriptor
(
js
,
db_ctor_obj
,
"name"
,
4
,
0
);
jsval_t
txn_ctor_obj
=
js_mkobj
(
js
);
jsval_t
txn_proto
=
js_mkobj
(
js
);
js_set_proto
(
js
,
txn_proto
,
object_proto
);
js_set
(
js
,
txn_proto
,
"get"
,
js_mkfun
(
lmdb_txn_get
));
js_set
(
js
,
txn_proto
,
"getBytes"
,
js_mkfun
(
lmdb_txn_get_bytes
));
js_set
(
js
,
txn_proto
,
"getString"
,
js_mkfun
(
lmdb_txn_get_string
));
js_set
(
js
,
txn_proto
,
"put"
,
js_mkfun
(
lmdb_txn_put
));
js_set
(
js
,
txn_proto
,
"del"
,
js_mkfun
(
lmdb_txn_del
));
js_set
(
js
,
txn_proto
,
"commit"
,
js_mkfun
(
lmdb_txn_commit
));
js_set
(
js
,
txn_proto
,
"abort"
,
js_mkfun
(
lmdb_txn_abort
));
js_set_sym
(
js
,
txn_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"LMDBTxn"
,
7
));
js_set_slot
(
js
,
txn_ctor_obj
,
SLOT_CFUNC
,
js_mkfun
(
lmdb_txn_constructor
));
js_mkprop_fast
(
js
,
txn_ctor_obj
,
"prototype"
,
9
,
txn_proto
);
js_mkprop_fast
(
js
,
txn_ctor_obj
,
"name"
,
4
,
ANT_STRING
(
"LMDBTxn"
));
js_set_descriptor
(
js
,
txn_ctor_obj
,
"name"
,
4
,
0
);
lmdb_types
.
env_ctor
=
js_obj_to_func
(
env_ctor_obj
);
lmdb_types
.
db_ctor
=
js_obj_to_func
(
db_ctor_obj
);
lmdb_types
.
txn_ctor
=
js_obj_to_func
(
txn_ctor_obj
);
lmdb_types
.
env_proto
=
env_proto
;
lmdb_types
.
db_proto
=
db_proto
;
lmdb_types
.
txn_proto
=
txn_proto
;
lmdb_types
.
ready
=
true
;
}
static
jsval_t
make_env_obj
(
ant_t
*
js
,
lmdb_env_handle_t
*
env
)
{
ensure_lmdb_prototypes
(
js
);
jsval_t
obj
=
js_mkobj
(
js
);
js_set_slot
(
js
,
obj
,
SLOT_DATA
,
ANT_PTR
(
env
));
register_env_ref
(
obj
,
env
);
if
(
is_special_object
(
lmdb_types
.
env_proto
))
js_set_proto
(
js
,
obj
,
lmdb_types
.
env_proto
);
return
obj
;
}
static
jsval_t
make_db_obj
(
ant_t
*
js
,
lmdb_db_handle_t
*
db
)
{
ensure_lmdb_prototypes
(
js
);
jsval_t
obj
=
js_mkobj
(
js
);
js_set_slot
(
js
,
obj
,
SLOT_DATA
,
ANT_PTR
(
db
));
register_db_ref
(
obj
,
db
);
if
(
is_special_object
(
lmdb_types
.
db_proto
))
js_set_proto
(
js
,
obj
,
lmdb_types
.
db_proto
);
return
obj
;
}
static
jsval_t
make_txn_obj
(
ant_t
*
js
,
lmdb_txn_handle_t
*
txn
)
{
ensure_lmdb_prototypes
(
js
);
jsval_t
obj
=
js_mkobj
(
js
);
js_set_slot
(
js
,
obj
,
SLOT_DATA
,
ANT_PTR
(
txn
));
register_txn_ref
(
obj
,
txn
);
if
(
is_special_object
(
lmdb_types
.
txn_proto
))
js_set_proto
(
js
,
obj
,
lmdb_types
.
txn_proto
);
return
obj
;
}
jsval_t
lmdb_library
(
ant_t
*
js
)
{
ensure_lmdb_prototypes
(
js
);
jsval_t
lib
=
js_mkobj
(
js
);
js_set
(
js
,
lib
,
"open"
,
js_mkfun
(
lmdb_open
));
js_set
(
js
,
lib
,
"strerror"
,
js_mkfun
(
lmdb_strerror_fn
));
js_set
(
js
,
lib
,
"Env"
,
lmdb_types
.
env_ctor
);
js_set
(
js
,
lib
,
"Database"
,
lmdb_types
.
db_ctor
);
js_set
(
js
,
lib
,
"Txn"
,
lmdb_types
.
txn_ctor
);
int
major
=
0
;
int
minor
=
0
;
int
patch
=
0
;
const
char
*
version
=
mdb_version
(
&
major
,
&
minor
,
&
patch
);
js_set
(
js
,
lib
,
"version"
,
js_mkstr
(
js
,
version
,
strlen
(
version
)));
js_set
(
js
,
lib
,
"versionMajor"
,
js_mknum
((
double
)
major
));
js_set
(
js
,
lib
,
"versionMinor"
,
js_mknum
((
double
)
minor
));
js_set
(
js
,
lib
,
"versionPatch"
,
js_mknum
((
double
)
patch
));
jsval_t
constants
=
js_mkobj
(
js
);
js_set
(
js
,
constants
,
"NOOVERWRITE"
,
js_mknum
((
double
)
MDB_NOOVERWRITE
));
js_set
(
js
,
constants
,
"NODUPDATA"
,
js_mknum
((
double
)
MDB_NODUPDATA
));
js_set
(
js
,
constants
,
"APPEND"
,
js_mknum
((
double
)
MDB_APPEND
));
js_set
(
js
,
constants
,
"APPENDDUP"
,
js_mknum
((
double
)
MDB_APPENDDUP
));
js_set
(
js
,
constants
,
"NOSUBDIR"
,
js_mknum
((
double
)
MDB_NOSUBDIR
));
js_set
(
js
,
constants
,
"NOSYNC"
,
js_mknum
((
double
)
MDB_NOSYNC
));
js_set
(
js
,
constants
,
"NOMETASYNC"
,
js_mknum
((
double
)
MDB_NOMETASYNC
));
js_set
(
js
,
constants
,
"WRITEMAP"
,
js_mknum
((
double
)
MDB_WRITEMAP
));
js_set
(
js
,
constants
,
"MAPASYNC"
,
js_mknum
((
double
)
MDB_MAPASYNC
));
js_set
(
js
,
constants
,
"NOTLS"
,
js_mknum
((
double
)
MDB_NOTLS
));
js_set
(
js
,
constants
,
"NOLOCK"
,
js_mknum
((
double
)
MDB_NOLOCK
));
js_set
(
js
,
constants
,
"NORDAHEAD"
,
js_mknum
((
double
)
MDB_NORDAHEAD
));
js_set
(
js
,
constants
,
"NOMEMINIT"
,
js_mknum
((
double
)
MDB_NOMEMINIT
));
js_set
(
js
,
lib
,
"constants"
,
constants
);
js_set_sym
(
js
,
lib
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"lmdb"
,
4
));
return
lib
;
}
void
lmdb_gc_update_roots
(
GC_OP_VAL_ARGS
)
{
if
(
lmdb_types
.
ready
)
{
op_val
(
ctx
,
&
lmdb_types
.
env_ctor
);
op_val
(
ctx
,
&
lmdb_types
.
db_ctor
);
op_val
(
ctx
,
&
lmdb_types
.
txn_ctor
);
op_val
(
ctx
,
&
lmdb_types
.
env_proto
);
op_val
(
ctx
,
&
lmdb_types
.
db_proto
);
op_val
(
ctx
,
&
lmdb_types
.
txn_proto
);
}
for
(
lmdb_env_ref_t
*
ref
=
env_refs
;
ref
;
ref
=
ref
->
next
)
op_val
(
ctx
,
&
ref
->
obj
);
for
(
lmdb_db_ref_t
*
ref
=
db_refs
;
ref
;
ref
=
ref
->
next
)
op_val
(
ctx
,
&
ref
->
obj
);
for
(
lmdb_txn_ref_t
*
ref
=
txn_refs
;
ref
;
ref
=
ref
->
next
)
op_val
(
ctx
,
&
ref
->
obj
);
}
void
cleanup_lmdb_module
(
void
)
{
lmdb_txn_handle_t
*
txn
=
txn_handles
;
while
(
txn
)
{
if
(
!
txn
->
closed
&&
txn
->
txn
)
{
mdb_txn_abort
(
txn
->
txn
);
txn
->
txn
=
NULL
;
txn
->
closed
=
true
;
}
txn
=
txn
->
next_global
;
}
lmdb_db_handle_t
*
db
=
db_handles
;
while
(
db
)
{
if
(
!
db
->
closed
&&
db
->
env
&&
!
db
->
env
->
closed
)
{
mdb_dbi_close
(
db
->
env
->
env
,
db
->
dbi
);
db
->
closed
=
true
;
}
db
=
db
->
next_global
;
}
lmdb_env_handle_t
*
env
=
env_handles
;
while
(
env
)
{
if
(
!
env
->
closed
)
env_handle_close
(
env
);
env
=
env
->
next_global
;
}
txn
=
txn_handles
;
while
(
txn
)
{
lmdb_txn_handle_t
*
next
=
txn
->
next_global
;
free
(
txn
);
txn
=
next
;
}
txn_handles
=
NULL
;
db
=
db_handles
;
while
(
db
)
{
lmdb_db_handle_t
*
next
=
db
->
next_global
;
free
(
db
->
name
);
free
(
db
);
db
=
next
;
}
db_handles
=
NULL
;
env
=
env_handles
;
while
(
env
)
{
lmdb_env_handle_t
*
next
=
env
->
next_global
;
free
(
env
->
path
);
free
(
env
);
env
=
next
;
}
env_handles
=
NULL
;
while
(
env_refs
)
{
lmdb_env_ref_t
*
next
=
env_refs
->
next
;
free
(
env_refs
);
env_refs
=
next
;
}
while
(
db_refs
)
{
lmdb_db_ref_t
*
next
=
db_refs
->
next
;
free
(
db_refs
);
db_refs
=
next
;
}
while
(
txn_refs
)
{
lmdb_txn_ref_t
*
next
=
txn_refs
->
next
;
free
(
txn_refs
);
txn_refs
=
next
;
}
lmdb_types
=
(
lmdb_js_types_t
){
0
};
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Mar 26, 4:41 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511809
Default Alt Text
lmdb.c (37 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment