#include <set>
#include <assert.h>
#include <bare.h>
#include <js.h>
#include <jstl.h>
#include <rocksdb.h>
#include <stdlib.h>
#include <string.h>
#include <utf.h>
#include <uv.h>
using rocksdb_native_on_open_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_close_t = js_function_t<void, js_receiver_t>;
using rocksdb_native_on_suspend_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_resume_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_flush_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_write_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_read_t = js_function_t<void, js_receiver_t, js_array_t, js_array_t>;
using rocksdb_native_on_iterator_open_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_iterator_close_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_iterator_read_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, js_array_t, js_array_t>;
using rocksdb_native_on_compact_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_compact_range_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using rocksdb_native_on_approximate_size_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, uint64_t>;
using rocksdb_native_on_current_wal_file_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, char *, uint64_t, uint32_t, uint64_t, uint64_t>;
struct rocksdb_native_t;
struct rocksdb_native_column_family_t {
rocksdb_column_family_t *handle;
rocksdb_column_family_descriptor_t descriptor;
std::string name;
rocksdb_native_t *db;
js_persistent_t<js_arraybuffer_t> ctx;
};
struct rocksdb_native_snapshot_t {
rocksdb_snapshot_t handle;
rocksdb_native_t *db;
js_persistent_t<js_arraybuffer_t> ctx;
};
struct rocksdb_native_t {
rocksdb_t handle;
rocksdb_options_t options;
rocksdb_slice_t *wal_filter_prefixes;
size_t wal_filter_prefixes_len;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
bool closing;
bool exiting;
std::set<rocksdb_native_column_family_t *> column_families;
std::set<rocksdb_native_snapshot_t *> snapshots;
js_deferred_teardown_t *teardown;
};
struct rocksdb_native_open_t {
rocksdb_open_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_open_t> on_open;
js_persistent_t<js_array_t> column_families;
};
struct rocksdb_native_close_t {
rocksdb_close_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_close_t> on_close;
};
struct rocksdb_native_suspend_t {
rocksdb_suspend_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_suspend_t> on_suspend;
};
struct rocksdb_native_resume_t {
rocksdb_resume_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_resume_t> on_resume;
};
struct rocksdb_native_iterator_t {
rocksdb_iterator_t handle;
rocksdb_slice_t *keys;
rocksdb_slice_t *values;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_iterator_open_t> on_open;
js_persistent_t<rocksdb_native_on_iterator_close_t> on_close;
js_persistent_t<rocksdb_native_on_iterator_read_t> on_read;
bool closing;
bool exiting;
js_deferred_teardown_t *teardown;
};
struct rocksdb_native_read_batch_t {
rocksdb_read_batch_t handle;
rocksdb_read_t *reads;
size_t capacity;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_read_t> on_read;
};
struct rocksdb_native_write_batch_t {
rocksdb_write_batch_t handle;
rocksdb_write_t *writes;
size_t capacity;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_write_t> on_write;
};
struct rocksdb_native_flush_t {
rocksdb_flush_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_flush_t> on_flush;
js_persistent_t<rocksdb_native_column_family_t> column_family;
};
struct rocksdb_native_compact_t {
rocksdb_compact_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_compact_t> on_compact;
};
struct rocksdb_native_compact_range_t {
rocksdb_compact_range_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_compact_range_t> on_compact_range;
};
struct rocksdb_native_current_wal_file_t {
rocksdb_current_wal_file_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_current_wal_file_t> on_current_wal_file;
};
struct rocksdb_native_approximate_size_t {
rocksdb_approximate_size_t handle;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<rocksdb_native_on_approximate_size_t> on_approximate_size;
};
static void
rocksdb_native__on_free(js_env_t *env, char *data) {
free(data);
}
static int
rocksdb_native__try_create_external_arraybuffer(js_env_t *env, char *data, size_t len, js_arraybuffer_t &result) {
int err;
err = js_create_external_arraybuffer<rocksdb_native__on_free>(env, data, len, result);
if (err == 0) return 0;
err = js_create_arraybuffer(env, data, len, result);
assert(err == 0);
free(data);
return 0;
}
static void
rocksdb_native__on_open(rocksdb_open_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_open_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
auto descriptors = handle->column_families;
auto handles = handle->handles;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_open_t cb;
err = js_get_reference_value(env, req->on_open, cb);
assert(err == 0);
js_array_t column_families;
err = js_get_reference_value(env, req->column_families, column_families);
assert(err == 0);
req->on_open.reset();
req->column_families.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
} else {
std::vector<js_arraybuffer_t> elements;
err = js_get_array_elements(env, column_families, elements);
assert(err == 0);
const auto len = elements.size();
for (uint32_t i = 0; i < len; i++) {
if (db->exiting) rocksdb_column_family_destroy(&db->handle, handles[i]);
else {
js_arraybuffer_t handle = elements[i];
rocksdb_native_column_family_t *column_family;
err = js_get_arraybuffer_info(env, handle, column_family);
assert(err == 0);
column_family->handle = handles[i];
err = js_create_reference(env, handle, column_family->ctx);
assert(err == 0);
db->column_families.insert(column_family);
}
}
}
rocksdb_open_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
delete[] descriptors;
delete[] handles;
}
static void
rocksdb_native__on_idle(rocksdb_t *handle) {
int err;
auto db = reinterpret_cast<rocksdb_native_t *>(handle);
for (auto &column_family : db->column_families) {
err = rocksdb_column_family_destroy(&column_family->db->handle, column_family->handle);
assert(err == 0);
column_family->handle = nullptr;
column_family->name.~basic_string();
column_family->ctx.reset();
}
for (auto &snapshot : db->snapshots) {
rocksdb_snapshot_destroy(&snapshot->handle);
snapshot->ctx.reset();
}
db->column_families.~set();
db->snapshots.~set();
if (db->wal_filter_prefixes) {
for (size_t i = 0; i < db->wal_filter_prefixes_len; i++) {
free(const_cast<char *>(db->wal_filter_prefixes[i].data));
}
free(db->wal_filter_prefixes);
db->wal_filter_prefixes = nullptr;
db->wal_filter_prefixes_len = 0;
}
}
static void
rocksdb_native__on_close(rocksdb_close_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_close_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
auto teardown = db->teardown;
rocksdb_close_cleanup(&req->handle);
if (db->closing) {
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_close_t cb;
err = js_get_reference_value(env, req->on_close, cb);
assert(err == 0);
req->on_close.reset();
req->ctx.reset();
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
} else {
delete req;
}
db->ctx.reset();
err = js_finish_deferred_teardown_callback(teardown);
assert(err == 0);
}
static void
rocksdb_native__on_teardown(js_deferred_teardown_t *teardown, void *data) {
int err;
auto db = reinterpret_cast<rocksdb_native_t *>(data);
db->exiting = true;
if (db->closing) return;
auto env = db->env;
auto req = new rocksdb_native_close_t();
req->env = env;
req->handle.data = req;
err = rocksdb_close(&db->handle, &req->handle, rocksdb_native__on_idle, rocksdb_native__on_close);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_init(
js_env_t *env,
bool read_only,
bool create_if_missing,
bool create_missing_column_families,
int32_t max_background_jobs,
uint64_t bytes_per_sync,
int32_t max_open_files,
bool use_direct_reads,
bool avoid_unnecessary_blocking_io,
bool skip_stats_update_on_db_open,
bool use_direct_io_for_flush_and_compaction,
int32_t max_file_opening_threads,
uint32_t wal_recovery_mode,
bool best_efforts_recovery,
bool enable_statistics,
uint32_t stats_level,
uint64_t wal_ttl_seconds,
uint64_t wal_size_limit_mb,
bool avoid_flush_during_shutdown,
js_array_t wal_filter_prefixes_array
) {
int err;
std::vector<js_typedarray_t<>> prefix_elements;
err = js_get_array_elements(env, wal_filter_prefixes_array, prefix_elements);
assert(err == 0);
const auto wal_filter_prefixes_len = prefix_elements.size();
rocksdb_slice_t *wal_filter_prefixes = nullptr;
if (wal_filter_prefixes_len > 0) {
wal_filter_prefixes = reinterpret_cast<rocksdb_slice_t *>(malloc(wal_filter_prefixes_len * sizeof(rocksdb_slice_t)));
for (size_t i = 0; i < wal_filter_prefixes_len; i++) {
rocksdb_slice_t prefix;
err = js_get_typedarray_info(env, prefix_elements[i], prefix.data, prefix.len);
assert(err == 0);
auto data = reinterpret_cast<char *>(malloc(prefix.len));
memcpy(data, prefix.data, prefix.len);
wal_filter_prefixes[i].data = data;
wal_filter_prefixes[i].len = prefix.len;
}
}
js_arraybuffer_t handle;
rocksdb_native_t *db;
err = js_create_arraybuffer(env, db, handle);
assert(err == 0);
db->env = env;
db->closing = false;
db->exiting = false;
db->wal_filter_prefixes = wal_filter_prefixes;
db->wal_filter_prefixes_len = wal_filter_prefixes_len;
new (&db->column_families) std::set<rocksdb_native_column_family_t *>();
new (&db->snapshots) std::set<rocksdb_native_snapshot_t *>();
rocksdb_options_init(&db->options, 8);
db->options.read_only = read_only;
db->options.create_if_missing = create_if_missing;
db->options.create_missing_column_families = create_missing_column_families;
db->options.max_background_jobs = max_background_jobs;
db->options.bytes_per_sync = bytes_per_sync;
db->options.max_open_files = max_open_files;
db->options.use_direct_reads = use_direct_reads;
db->options.avoid_unnecessary_blocking_io = avoid_unnecessary_blocking_io;
db->options.skip_stats_update_on_db_open = skip_stats_update_on_db_open;
db->options.use_direct_io_for_flush_and_compaction = use_direct_io_for_flush_and_compaction;
db->options.max_file_opening_threads = max_file_opening_threads;
db->options.lock = -1;
db->options.wal_recovery_mode = rocksdb_wal_recovery_mode_t(wal_recovery_mode);
db->options.best_efforts_recovery = best_efforts_recovery;
db->options.enable_statistics = enable_statistics;
db->options.stats_level = rocksdb_stats_level_t(stats_level);
db->options.wal_ttl_seconds = wal_ttl_seconds;
db->options.wal_size_limit_mb = wal_size_limit_mb;
db->options.avoid_flush_during_shutdown = avoid_flush_during_shutdown;
db->options.wal_filter_prefixes = db->wal_filter_prefixes;
db->options.wal_filter_prefixes_len = db->wal_filter_prefixes_len;
return handle;
}
static js_arraybuffer_t
rocksdb_native_open(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_receiver_t self,
std::string path,
js_array_t column_families_array,
int lock,
js_receiver_t ctx,
rocksdb_native_on_open_t on_open
) {
int err;
std::vector<js_arraybuffer_t> elements;
err = js_get_array_elements(env, column_families_array, elements);
assert(err == 0);
const auto len = elements.size();
auto column_families = new rocksdb_column_family_descriptor_t[len];
for (uint32_t i = 0; i < len; i++) {
js_arraybuffer_t handle = elements[i];
rocksdb_native_column_family_t *column_family;
err = js_get_arraybuffer_info(env, handle, column_family);
assert(err == 0);
memcpy(&column_families[i], &column_family->descriptor, sizeof(rocksdb_column_family_descriptor_t));
column_family->db = db;
}
auto handles = new rocksdb_column_family_t *[len];
uv_loop_t *loop;
err = js_get_env_loop(env, &loop);
assert(err == 0);
js_arraybuffer_t handle;
rocksdb_native_open_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
db->options.lock = lock;
err = rocksdb_open(loop, &db->handle, &req->handle, path.c_str(), &db->options, column_families, handles, len, nullptr, rocksdb_native__on_open);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
if (lock >= 0) {
uv_fs_t fs;
err = uv_fs_close(NULL, &fs, lock, NULL);
assert(err == 0);
}
throw js_pending_exception;
}
err = js_create_reference(env, self, db->ctx);
assert(err == 0);
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_open, req->on_open);
assert(err == 0);
err = js_create_reference(env, column_families_array, req->column_families);
assert(err == 0);
err = js_add_deferred_teardown_callback(env, rocksdb_native__on_teardown, db, &db->teardown);
assert(err == 0);
return handle;
}
static js_arraybuffer_t
rocksdb_native_close(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_receiver_t ctx,
rocksdb_native_on_close_t on_close
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_close_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
err = rocksdb_close(&db->handle, &req->handle, rocksdb_native__on_idle, rocksdb_native__on_close);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_close, req->on_close);
assert(err == 0);
db->closing = true;
return handle;
}
static void
rocksdb_native__on_suspend(rocksdb_suspend_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_suspend_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_suspend_t cb;
err = js_get_reference_value(env, req->on_suspend, cb);
assert(err == 0);
req->on_suspend.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_suspend_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_suspend(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_receiver_t ctx,
rocksdb_native_on_suspend_t on_suspend
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_suspend_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
err = rocksdb_suspend(&db->handle, &req->handle, rocksdb_native__on_suspend);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_suspend, req->on_suspend);
assert(err == 0);
return handle;
}
static void
rocksdb_native__on_resume(rocksdb_resume_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_resume_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_resume_t cb;
err = js_get_reference_value(env, req->on_resume, cb);
assert(err == 0);
req->on_resume.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_resume_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_resume(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_receiver_t ctx,
rocksdb_native_on_resume_t on_resume
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_resume_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
err = rocksdb_resume(&db->handle, &req->handle, rocksdb_native__on_resume);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_resume, req->on_resume);
assert(err == 0);
return handle;
}
static js_arraybuffer_t
rocksdb_native_column_family_init(
js_env_t *env,
std::string name,
bool enable_blob_files,
uint64_t min_blob_size,
uint64_t blob_file_size,
bool enable_blob_garbage_collection,
uint64_t block_size,
bool cache_index_and_filter_blocks,
uint32_t format_version,
bool optimize_filters_for_memory,
bool no_block_cache,
uint32_t filter_policy_type,
double bits_per_key,
int32_t bloom_before_level,
uint32_t top_level_index_pinning_tier,
uint32_t partition_pinning_tier,
uint32_t unpartitioned_pinning_tier,
bool optimize_filters_for_hits,
int32_t num_levels,
int32_t max_write_buffer_number,
double blob_garbage_collection_age_cutoff,
double blob_garbage_collection_force_threshold
) {
int err;
rocksdb_filter_policy_t filter_policy = {rocksdb_filter_policy_type_t(filter_policy_type)};
switch (filter_policy_type) {
case rocksdb_bloom_filter_policy:
filter_policy.bloom = (rocksdb_bloom_filter_options_t) {
0,
bits_per_key
};
break;
case rocksdb_ribbon_filter_policy:
filter_policy.ribbon = (rocksdb_ribbon_filter_options_t) {
0,
bits_per_key,
bloom_before_level,
};
break;
}
uv_loop_t *loop;
err = js_get_env_loop(env, &loop);
assert(err == 0);
js_arraybuffer_t handle;
rocksdb_native_column_family_t *column_family;
err = js_create_arraybuffer(env, column_family, handle);
assert(err == 0);
column_family->db = nullptr;
column_family->handle = nullptr;
new (&column_family->name) std::string(std::move(name));
column_family->descriptor = (rocksdb_column_family_descriptor_t) {
column_family->name.c_str(),
{
5,
rocksdb_level_compaction,
enable_blob_files,
min_blob_size,
blob_file_size,
enable_blob_garbage_collection,
block_size,
cache_index_and_filter_blocks,
format_version,
optimize_filters_for_memory,
no_block_cache,
filter_policy,
rocksdb_pinning_tier_t(top_level_index_pinning_tier),
rocksdb_pinning_tier_t(partition_pinning_tier),
rocksdb_pinning_tier_t(unpartitioned_pinning_tier),
optimize_filters_for_hits,
num_levels,
max_write_buffer_number,
blob_garbage_collection_age_cutoff,
blob_garbage_collection_force_threshold,
}
};
return handle;
}
static void
rocksdb_native_column_family_destroy(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family
) {
int err;
if (column_family->handle == nullptr) return;
err = rocksdb_column_family_destroy(&column_family->db->handle, column_family->handle);
assert(err == 0);
column_family->db->column_families.erase(column_family);
column_family->handle = nullptr;
column_family->name.~basic_string();
column_family->ctx.reset();
}
static js_arraybuffer_t
rocksdb_native_iterator_init(js_env_t *env) {
int err;
js_arraybuffer_t handle;
rocksdb_native_iterator_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->closing = false;
req->exiting = false;
req->handle.data = req;
return handle;
}
static js_arraybuffer_t
rocksdb_native_iterator_buffer(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req,
uint32_t capacity
) {
int err;
js_arraybuffer_t handle;
uint8_t *data;
err = js_create_arraybuffer(env, 2 * capacity * sizeof(rocksdb_slice_t), data, handle);
assert(err == 0);
size_t offset = 0;
req->keys = reinterpret_cast<rocksdb_slice_t *>(&data[offset]);
offset += capacity * sizeof(rocksdb_slice_t);
req->values = reinterpret_cast<rocksdb_slice_t *>(&data[offset]);
return handle;
}
static void
rocksdb_native__on_iterator_close(rocksdb_iterator_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data);
auto env = req->env;
auto teardown = req->teardown;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_iterator_close_t cb;
err = js_get_reference_value(env, req->on_close, cb);
assert(err == 0);
req->on_open.reset();
req->on_close.reset();
req->on_read.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_iterator_cleanup(&req->handle);
if (!req->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
err = js_finish_deferred_teardown_callback(teardown);
assert(err == 0);
}
static void
rocksdb_native__on_iterator_open(rocksdb_iterator_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_iterator_open_t cb;
err = js_get_reference_value(env, req->on_open, cb);
assert(err == 0);
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_iterator_cleanup(&req->handle);
if (!req->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
rocksdb_native__on_iterator_teardown(js_deferred_teardown_t *teardown, void *data) {
int err;
auto req = reinterpret_cast<rocksdb_native_iterator_t *>(data);
req->exiting = true;
if (req->closing) return;
err = rocksdb_iterator_close(&req->handle, rocksdb_native__on_iterator_close);
assert(err == 0);
}
static void
rocksdb_native_iterator_open(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family,
js_typedarray_t<> gt,
js_typedarray_t<> gte,
js_typedarray_t<> lt,
js_typedarray_t<> lte,
bool reverse,
bool keys_only,
std::optional<js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1>> snapshot,
js_receiver_t ctx,
rocksdb_native_on_iterator_open_t on_open,
rocksdb_native_on_iterator_close_t on_close,
rocksdb_native_on_iterator_read_t on_read
) {
int err;
rocksdb_range_t range;
err = js_get_typedarray_info(env, gt, range.gt.data, range.gt.len);
assert(err == 0);
err = js_get_typedarray_info(env, gte, range.gte.data, range.gte.len);
assert(err == 0);
err = js_get_typedarray_info(env, lt, range.lt.data, range.lt.len);
assert(err == 0);
err = js_get_typedarray_info(env, lte, range.lte.data, range.lte.len);
assert(err == 0);
rocksdb_iterator_options_t options = {
.version = 0,
.reverse = reverse,
.keys_only = keys_only
};
if (snapshot) options.snapshot = &snapshot.value()->handle;
err = rocksdb_iterator_open(&db->handle, &req->handle, column_family->handle, range, &options, rocksdb_native__on_iterator_open);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_open, req->on_open);
assert(err == 0);
err = js_create_reference(env, on_close, req->on_close);
assert(err == 0);
err = js_create_reference(env, on_read, req->on_read);
assert(err == 0);
err = js_add_deferred_teardown_callback(env, rocksdb_native__on_iterator_teardown, req, &req->teardown);
assert(err == 0);
}
static void
rocksdb_native_iterator_close(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req) {
int err;
err = rocksdb_iterator_close(&req->handle, rocksdb_native__on_iterator_close);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
req->closing = true;
}
static void
rocksdb_native__on_iterator_read(rocksdb_iterator_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto len = req->handle.len;
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_iterator_read_t cb;
err = js_get_reference_value(env, req->on_read, cb);
assert(err == 0);
std::optional<js_object_t> error;
js_array_t keys;
err = js_create_array(env, len, keys);
assert(err == 0);
js_array_t values;
err = js_create_array(env, len, values);
assert(err == 0);
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
} else {
for (size_t i = 0; i < len; i++) {
rocksdb_slice_t *key = &req->keys[i];
rocksdb_slice_t *value = &req->values[i];
if (req->exiting) {
rocksdb_slice_destroy(key);
rocksdb_slice_destroy(value);
} else {
js_arraybuffer_t result;
err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(key->data), key->len, result);
assert(err == 0);
err = js_set_element(env, keys, i, result);
assert(err == 0);
err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(value->data), value->len, result);
assert(err == 0);
err = js_set_element(env, values, i, result);
assert(err == 0);
}
}
}
rocksdb_iterator_cleanup(&req->handle);
if (!req->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error, keys, values);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
rocksdb_native_iterator_read(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req,
uint32_t capacity
) {
int err;
err = rocksdb_iterator_read(&req->handle, req->keys, req->values, capacity, rocksdb_native__on_iterator_read);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
}
static js_arraybuffer_t
rocksdb_native_read_init(js_env_t *env) {
int err;
js_arraybuffer_t handle;
rocksdb_native_read_batch_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
return handle;
}
static js_arraybuffer_t
rocksdb_native_read_buffer(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_read_batch_t, 1> req,
uint32_t capacity
) {
int err;
js_arraybuffer_t handle;
rocksdb_read_t *reads;
err = js_create_arraybuffer(env, capacity, reads, handle);
assert(err == 0);
req->capacity = capacity;
req->reads = reads;
return handle;
}
static void
rocksdb_native__on_read(rocksdb_read_batch_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_read_batch_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto len = req->handle.len;
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_read_t cb;
err = js_get_reference_value(env, req->on_read, cb);
assert(err == 0);
req->on_read.reset();
req->ctx.reset();
js_array_t errors;
err = js_create_array(env, len, errors);
assert(err == 0);
js_array_t values;
err = js_create_array(env, len, values);
assert(err == 0);
for (size_t i = 0; i < len; i++) {
char *error = req->handle.errors[i];
int status = req->handle.statuses[i];
if (error) {
js_object_t result;
err = js_create_error(env, uv_err_name(status), error, result);
assert(err == 0);
err = js_set_element(env, errors, i, result);
assert(err == 0);
} else {
rocksdb_slice_t *value = &req->reads[i].value;
if (db->exiting) rocksdb_slice_destroy(value);
else {
js_arraybuffer_t result;
if (value->data == nullptr && value->len == size_t(-1)) {
err = js_get_null(env, static_cast<js_value_t **>(result));
assert(err == 0);
} else {
err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(value->data), value->len, result);
assert(err == 0);
}
err = js_set_element(env, values, i, result);
assert(err == 0);
}
}
}
rocksdb_read_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, errors, values);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
rocksdb_native_read(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_read_batch_t, 1> req,
js_array_t operations,
std::optional<js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1>> snapshot,
bool async_io,
bool fill_cache,
js_receiver_t ctx,
rocksdb_native_on_read_t on_read
) {
int err;
std::vector<js_object_t> elements;
err = js_get_array_elements(env, operations, elements);
assert(err == 0);
const auto len = elements.size();
for (uint32_t i = 0; i < len; i++) {
js_object_t read = elements[i];
rocksdb_read_type_t type;
err = js_get_property(env, read, "type", reinterpret_cast<uint32_t &>(type));
assert(err == 0);
req->reads[i].type = type;
js_arraybuffer_t column_family_property;
err = js_get_property(env, read, "columnFamily", column_family_property);
assert(err == 0);
rocksdb_native_column_family_t *column_family;
err = js_get_arraybuffer_info(env, column_family_property, column_family);
assert(err == 0);
req->reads[i].column_family = column_family->handle;
switch (type) {
case rocksdb_get: {
js_typedarray_t property;
rocksdb_slice_t *key = &req->reads[i].key;
err = js_get_property(env, read, "key", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, key->data, key->len);
assert(err == 0);
break;
}
}
}
rocksdb_read_options_t options = {
.version = 1,
.async_io = async_io,
.fill_cache = fill_cache
};
if (snapshot) options.snapshot = &snapshot.value()->handle;
err = rocksdb_read(&db->handle, &req->handle, req->reads, len, &options, rocksdb_native__on_read);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_read, req->on_read);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_write_init(js_env_t *env) {
int err;
js_arraybuffer_t handle;
rocksdb_native_write_batch_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
return handle;
}
static js_arraybuffer_t
rocksdb_native_write_buffer(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_write_batch_t, 1> req,
uint32_t capacity
) {
int err;
js_arraybuffer_t handle;
rocksdb_write_t *writes;
err = js_create_arraybuffer(env, capacity, writes, handle);
assert(err == 0);
req->capacity = capacity;
req->writes = writes;
return handle;
}
static void
rocksdb_native__on_write(rocksdb_write_batch_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_write_batch_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_write_t cb;
err = js_get_reference_value(env, req->on_write, cb);
assert(err == 0);
req->on_write.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_write_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
rocksdb_native_write(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_write_batch_t, 1> req,
js_array_t operations,
js_receiver_t ctx,
rocksdb_native_on_write_t on_write
) {
int err;
std::vector<js_object_t> elements;
err = js_get_array_elements(env, operations, elements);
assert(err == 0);
const auto len = elements.size();
for (uint32_t i = 0; i < len; i++) {
js_object_t write = elements[i];
rocksdb_write_type_t type;
err = js_get_property(env, write, "type", reinterpret_cast<uint32_t &>(type));
assert(err == 0);
req->writes[i].type = type;
js_arraybuffer_t column_family_property;
err = js_get_property(env, write, "columnFamily", column_family_property);
assert(err == 0);
rocksdb_native_column_family_t *column_family;
err = js_get_arraybuffer_info(env, column_family_property, column_family);
assert(err == 0);
req->writes[i].column_family = column_family->handle;
js_typedarray_t property;
switch (type) {
case rocksdb_put: {
rocksdb_slice_t *key = &req->writes[i].key;
err = js_get_property(env, write, "key", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, key->data, key->len);
assert(err == 0);
rocksdb_slice_t *value = &req->writes[i].value;
err = js_get_property(env, write, "value", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, value->data, value->len);
assert(err == 0);
break;
}
case rocksdb_delete: {
rocksdb_slice_t *key = &req->writes[i].key;
err = js_get_property(env, write, "key", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, key->data, key->len);
assert(err == 0);
break;
}
case rocksdb_delete_range: {
rocksdb_slice_t *start = &req->writes[i].start;
err = js_get_property(env, write, "start", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, start->data, start->len);
assert(err == 0);
rocksdb_slice_t *end = &req->writes[i].end;
err = js_get_property(env, write, "end", property);
assert(err == 0);
err = js_get_typedarray_info(env, property, end->data, end->len);
assert(err == 0);
break;
}
}
}
err = rocksdb_write(&db->handle, &req->handle, req->writes, len, nullptr, rocksdb_native__on_write);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_write, req->on_write);
assert(err == 0);
}
static void
rocksdb_native__on_flush(rocksdb_flush_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_flush_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_flush_t cb;
err = js_get_reference_value(env, req->on_flush, cb);
assert(err == 0);
req->on_flush.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_flush_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_flush(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family,
js_receiver_t ctx,
rocksdb_native_on_flush_t on_flush
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_flush_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
err = rocksdb_flush(&db->handle, &req->handle, column_family->handle, nullptr, rocksdb_native__on_flush);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_flush, req->on_flush);
assert(err == 0);
return handle;
}
static void
rocksdb_native__on_compact_range(rocksdb_compact_range_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_compact_range_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_compact_range_t cb;
err = js_get_reference_value(env, req->on_compact_range, cb);
assert(err == 0);
req->on_compact_range.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_compact_range_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_compact_range(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family,
js_typedarray_t<> start,
js_typedarray_t<> end,
bool exclusive,
uint32_t blob_garbage_collection_policy,
double blob_garbage_collection_age_cutoff,
uint32_t bottommost_level_compaction,
js_receiver_t ctx,
rocksdb_native_on_compact_range_t on_compact_range
) {
int err;
rocksdb_slice_t start_slice;
err = js_get_typedarray_info(env, start, start_slice.data, start_slice.len);
assert(err == 0);
rocksdb_slice_t end_slice;
err = js_get_typedarray_info(env, end, end_slice.data, end_slice.len);
assert(err == 0);
js_arraybuffer_t handle;
rocksdb_native_compact_range_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
rocksdb_compact_range_options_t options;
rocksdb_compact_range_options_init(&options, 1);
options.exclusive_manual_compaction = exclusive;
options.blob_garbage_collection_policy = rocksdb_blob_garbage_collection_policy_t(blob_garbage_collection_policy);
options.blob_garbage_collection_age_cutoff = blob_garbage_collection_age_cutoff;
options.bottommost_level_compaction = rocksdb_bottommost_level_compaction_t(bottommost_level_compaction);
err = rocksdb_compact_range(&db->handle, &req->handle, column_family->handle, start_slice, end_slice, &options, rocksdb_native__on_compact_range);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_compact_range, req->on_compact_range);
assert(err == 0);
return handle;
}
static void
rocksdb_native__on_compact(rocksdb_compact_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_compact_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_compact_t cb;
err = js_get_reference_value(env, req->on_compact, cb);
assert(err == 0);
req->on_compact.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_compact_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_compact(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family,
bool exclusive,
uint32_t blob_garbage_collection_policy,
double blob_garbage_collection_age_cutoff,
uint32_t bottommost_level_compaction,
js_receiver_t ctx,
rocksdb_native_on_compact_t on_compact
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_compact_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
rocksdb_compact_range_options_t options;
rocksdb_compact_range_options_init(&options, 1);
options.exclusive_manual_compaction = exclusive;
options.blob_garbage_collection_policy = rocksdb_blob_garbage_collection_policy_t(blob_garbage_collection_policy);
options.blob_garbage_collection_age_cutoff = blob_garbage_collection_age_cutoff;
options.bottommost_level_compaction = rocksdb_bottommost_level_compaction_t(bottommost_level_compaction);
err = rocksdb_compact(&db->handle, &req->handle, column_family->handle, &options, rocksdb_native__on_compact);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_compact, req->on_compact);
assert(err == 0);
return handle;
}
static void
rocksdb_native__on_approximate_size(rocksdb_approximate_size_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_approximate_size_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_approximate_size_t cb;
err = js_get_reference_value(env, req->on_approximate_size, cb);
assert(err == 0);
req->on_approximate_size.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
rocksdb_approximate_size_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error, req->handle.result);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_approximate_size(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family,
js_typedarray_t<> start,
js_typedarray_t<> end,
bool include_memtables,
bool include_files,
double error_margin,
js_receiver_t ctx,
rocksdb_native_on_approximate_size_t on_approximate_size
) {
int err;
rocksdb_slice_t start_slice;
err = js_get_typedarray_info(env, start, start_slice.data, start_slice.len);
assert(err == 0);
rocksdb_slice_t end_slice;
err = js_get_typedarray_info(env, end, end_slice.data, end_slice.len);
assert(err == 0);
js_arraybuffer_t handle;
rocksdb_native_approximate_size_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
rocksdb_approximate_size_options_t options;
rocksdb_approximate_size_options_init(&options, 0);
options.include_memtables = include_memtables;
options.include_files = include_files;
options.files_size_error_margin = error_margin;
err = rocksdb_approximate_size(&db->handle, &req->handle, column_family->handle, start_slice, end_slice, &options, rocksdb_native__on_approximate_size);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_approximate_size, req->on_approximate_size);
assert(err == 0);
return handle;
}
static void
rocksdb_native__on_current_wal_file(rocksdb_current_wal_file_t *handle, int status) {
int err;
assert(status == 0);
auto req = reinterpret_cast<rocksdb_native_current_wal_file_t *>(handle->data);
auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db);
auto env = req->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, req->ctx, ctx);
assert(err == 0);
rocksdb_native_on_current_wal_file_t cb;
err = js_get_reference_value(env, req->on_current_wal_file, cb);
assert(err == 0);
req->on_current_wal_file.reset();
req->ctx.reset();
std::optional<js_object_t> error;
if (req->handle.error) {
err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace());
assert(err == 0);
}
auto &file = req->handle.result;
rocksdb_current_wal_file_cleanup(&req->handle);
if (!db->exiting) {
err = js_call_function_with_checkpoint(env, cb, ctx, error, file.path, file.number, uint32_t(file.type), file.start_sequence, file.size);
(void) err;
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static js_arraybuffer_t
rocksdb_native_current_wal_file(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
js_receiver_t ctx,
rocksdb_native_on_current_wal_file_t on_current_wal_file
) {
int err;
js_arraybuffer_t handle;
rocksdb_native_current_wal_file_t *req;
err = js_create_arraybuffer(env, req, handle);
assert(err == 0);
req->env = env;
req->handle.data = req;
err = rocksdb_current_wal_file(&db->handle, &req->handle, rocksdb_native__on_current_wal_file);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, ctx, req->ctx);
assert(err == 0);
err = js_create_reference(env, on_current_wal_file, req->on_current_wal_file);
assert(err == 0);
return handle;
}
static uint32_t
rocksdb_native_stats_level_get(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_t, 1> db) {
int err;
rocksdb_stats_level_t level;
err = rocksdb_stats_level_get(&db->handle, &level);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
return uint32_t(level);
}
static void
rocksdb_native_stats_level_set(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, uint32_t level) {
int err;
err = rocksdb_stats_level_set(&db->handle, rocksdb_stats_level_t(level));
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
}
static std::optional<std::string>
rocksdb_native_property_get(
js_env_t *env,
js_arraybuffer_span_of_t<rocksdb_native_t, 1> db,
std::string name
) {
rocksdb_slice_t value;
int err = rocksdb_property_get(&db->handle, name.c_str(), &value);
if (err == UV_ENOENT) return std::nullopt;
assert(err == 0);
std::string result(value.data, value.len);
rocksdb_slice_destroy(&value);
return result;
}
static js_arraybuffer_t
rocksdb_native_snapshot_create(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_t, 1> db) {
int err;
js_arraybuffer_t handle;
rocksdb_native_snapshot_t *snapshot;
err = js_create_arraybuffer(env, snapshot, handle);
assert(err == 0);
snapshot->db = db;
err = rocksdb_snapshot_create(&db->handle, &snapshot->handle);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
throw js_pending_exception;
}
err = js_create_reference(env, handle, snapshot->ctx);
assert(err == 0);
db->snapshots.insert(snapshot);
return handle;
}
static void
rocksdb_native_snapshot_destroy(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1> snapshot) {
int err;
rocksdb_snapshot_destroy(&snapshot->handle);
snapshot->db->snapshots.erase(snapshot);
snapshot->ctx.reset();
}
static js_value_t *
rocksdb_native_exports(js_env_t *env, js_value_t *exports) {
int err;
#define V(name, fn) \
err = js_set_property<fn>(env, exports, name); \
assert(err == 0);
V("init", rocksdb_native_init)
V("open", rocksdb_native_open)
V("close", rocksdb_native_close)
V("suspend", rocksdb_native_suspend)
V("resume", rocksdb_native_resume)
V("columnFamilyInit", rocksdb_native_column_family_init)
V("columnFamilyDestroy", rocksdb_native_column_family_destroy)
V("readInit", rocksdb_native_read_init)
V("readBuffer", rocksdb_native_read_buffer)
V("read", rocksdb_native_read)
V("writeInit", rocksdb_native_write_init)
V("writeBuffer", rocksdb_native_write_buffer)
V("write", rocksdb_native_write)
V("iteratorInit", rocksdb_native_iterator_init)
V("iteratorBuffer", rocksdb_native_iterator_buffer)
V("iteratorOpen", rocksdb_native_iterator_open)
V("iteratorClose", rocksdb_native_iterator_close)
V("iteratorRead", rocksdb_native_iterator_read)
V("statsLevelGet", rocksdb_native_stats_level_get)
V("statsLevelSet", rocksdb_native_stats_level_set)
V("flush", rocksdb_native_flush)
V("compact", rocksdb_native_compact)
V("compactRange", rocksdb_native_compact_range)
V("approximateSize", rocksdb_native_approximate_size)
V("currentWalFile", rocksdb_native_current_wal_file)
V("propertyGet", rocksdb_native_property_get)
V("snapshotCreate", rocksdb_native_snapshot_create)
V("snapshotDestroy", rocksdb_native_snapshot_destroy)
#undef V
#define V(name, n) \
{ \
js_value_t *val; \
err = js_create_uint32(env, n, &val); \
assert(err == 0); \
err = js_set_named_property(env, exports, name, val); \
assert(err == 0); \
}
V("GET", rocksdb_get)
V("PUT", rocksdb_put)
V("DELETE", rocksdb_delete)
V("DELETE_RANGE", rocksdb_delete_range)
#undef V
return exports;
}
BARE_MODULE(rocksdb_native, rocksdb_native_exports)