WIP node.js binding

Vitaliy Filippov 2024-05-19 01:58:28 +03:00
parent 50e56b3b92
commit be7efcd64f
6 changed files with 360 additions and 0 deletions

54
node-binding/addon.cc Normal file
View File

@ -0,0 +1,54 @@
#include "addon.h"
// Initialize the node addon
NAN_MODULE_INIT(InitAddon) {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(NodeVitastor::Create);
tpl->SetClassName(Nan::New("Client").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "read", NodeVitastor::Read);
// Nan::SetPrototypeMethod(tpl, "write", NodeVitastor::Write);
// Nan::SetPrototypeMethod(tpl, "sync", NodeVitastor::Sync);
// Nan::SetPrototypeMethod(tpl, "readBitmap", NodeVitastor::ReadBitmap);
// Nan::SetPrototypeMethod(tpl, "watchInode", NodeVitastor::WatchInode);
Nan::SetPrototypeMethod(tpl, "destroy", NodeVitastor::Destroy);
Nan::Set(target, Nan::New("Client").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
/*
// VitastorKV
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(NodeVitastorKV::Create);
tpl->SetClassName(Nan::New("KV").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "open", NodeVitastorKV::open);
Nan::SetPrototypeMethod(tpl, "setConfig", NodeVitastorKV::setConfig);
Nan::SetPrototypeMethod(tpl, "close", NodeVitastorKV::close);
Nan::SetPrototypeMethod(tpl, "getSize", NodeVitastorKV::getSize);
Nan::SetPrototypeMethod(tpl, "get", NodeVitastorKV::get);
Nan::SetPrototypeMethod(tpl, "set", NodeVitastorKV::set);
Nan::SetPrototypeMethod(tpl, "del", NodeVitastorKV::del);
Nan::SetPrototypeMethod(tpl, "listStart", NodeVitastorKV::listStart);
Nan::SetPrototypeMethod(tpl, "listNext", NodeVitastorKV::listNext);
Nan::SetPrototypeMethod(tpl, "listClose", NodeVitastorKV::listClose);
Nan::SetPrototypeMethod(tpl, "destroy", NodeVitastorKV::destroy);
Nan::Set(target, Nan::New("KV").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
// Listing handle
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(NodeVitastorKV::Create);
tpl->SetClassName(Nan::New("ListHandle").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Nan::SetPrototypeMethod(tpl, "next", NodeVitastorListing::listNext);
Nan::SetPrototypeMethod(tpl, "close", NodeVitastorListing::listClose);
Nan::Set(target, Nan::New("ListHandle").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
*/
}
NODE_MODULE(addon, (void*)InitAddon)

19
node-binding/addon.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef NODE_VITASTOR_ADDON_H
#define NODE_VITASTOR_ADDON_H
#include <nan.h>
#include <vitastor_c.h>
#include "client.h"
#define ERRORF(format, ...) fprintf(stderr, format "\n", __VA_ARGS__);
#define TRACEF(format, ...) fprintf(stderr, format "\n", __VA_ARGS__);
#define TRACE(msg) fprintf(stderr, "%s\n", msg);
//#define TRACEF(format, arg) ;
//#define TRACE(msg) ;
#define NODE_THIS() Nan::ObjectWrap::Unwrap<NodeVitastor>(info.This());
#endif

20
node-binding/binding.gyp Normal file
View File

@ -0,0 +1,20 @@
{
'targets': [
{
'target_name': 'addon',
'sources': [
'client.cc',
'addon.cc'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")'
],
'cflags': [
'<!(pkg-config --cflags vitastor)'
],
'libraries': [
'<!(pkg-config --libs vitastor)'
]
}
]
}

200
node-binding/client.cc Normal file
View File

@ -0,0 +1,200 @@
#include "addon.h"
NodeVitastor::NodeVitastor(): Nan::ObjectWrap() {
TRACE("NodeVitastor: constructor");
poll_watcher.data = this;
}
NodeVitastor::~NodeVitastor() {
uv_poll_stop(&poll_watcher);
vitastor_c_destroy(c);
c = NULL;
}
NAN_METHOD(NodeVitastor::Create) {
TRACE("NodeVitastor::Create");
v8::Local<v8::Object> jsParams = info[0].As<v8::Object>();
v8::Local<v8::Array> keys = Nan::GetOwnPropertyNames(jsParams).ToLocalChecked();
std::vector<std::string> cfg;
for (uint32_t i = 0; i < keys->Length(); i++) {
auto key = keys->Get(Nan::GetCurrentContext(), i).ToLocalChecked();
cfg.push_back(std::string(*Nan::Utf8String(key)));
cfg.push_back(std::string(*Nan::Utf8String(jsParams->Get(Nan::GetCurrentContext(), key).ToLocalChecked())));
}
const char **c_cfg = new const char*[cfg.size()];
for (size_t i = 0; i < cfg.size(); i++) {
c_cfg[i] = cfg[i].c_str();
}
NodeVitastor* cli = new NodeVitastor();
cli->c = vitastor_c_create_uring_json(c_cfg, cfg.size());
delete[] c_cfg;
int res = vitastor_c_uring_register_eventfd(cli->c);
if (res >= 0) {
cli->eventfd = res;
res = uv_poll_init_socket(uv_default_loop(), &cli->poll_watcher, cli->eventfd);
if (res >= 0) {
res = uv_poll_start(&cli->poll_watcher, UV_READABLE, on_io_readable);
}
}
if (res < 0) {
ERRORF("NodeVitastor: failed to create and register io_uring eventfd in libuv: %s", strerror(-cli->eventfd));
vitastor_c_destroy(cli->c);
cli->c = NULL;
Nan::ThrowError("failed to create and register io_uring eventfd");
return;
}
cli->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
void NodeVitastor::on_io_readable(uv_poll_t* handle, int status, int revents) {
TRACEF("NodeVitastor::on_io_readable status/revents %d %d", status, revents);
if (revents & UV_READABLE) {
NodeVitastor* self = (NodeVitastor*)handle->data;
std::unique_lock<std::mutex> lock(self->mu);
vitastor_c_uring_handle_events(self->c);
}
}
class VitastorRequest: public Nan::AsyncResource {
public:
VitastorRequest(v8::Local<v8::Function> cb): Nan::AsyncResource("VitastorRequest") {
callback.Reset(cb);
}
iovec iov;
Nan::Persistent<v8::Function> callback;
};
// read(pool, inode, offset, len, callback(err, buffer, version))
NAN_METHOD(NodeVitastor::Read) {
TRACE("NodeVitastor::Read");
NodeVitastor* self = NODE_THIS();
uint64_t pool = Nan::To<int64_t>(info[0]).FromJust();
uint64_t inode = Nan::To<int64_t>(info[1]).FromJust();
uint64_t offset = Nan::To<int64_t>(info[2]).FromJust();
uint64_t len = Nan::To<int64_t>(info[3]).FromJust();
uint8_t *buf = (uint8_t*)malloc(len);
if (!buf)
{
Nan::ThrowError("failed to allocate memory");
return;
}
v8::Local<v8::Function> callback = info[4].As<v8::Function>();
auto req = new VitastorRequest(callback);
req->iov = { .iov_base = buf, .iov_len = len };
std::unique_lock<std::mutex> lock(self->mu);
vitastor_c_read(self->c, ((pool << (64-POOL_ID_BITS)) | inode), offset, len, &req->iov, 1, on_read_finish, req);
}
// write(pool, inode, offset, buffer, { version }?, callback(err))
NAN_METHOD(NodeVitastor::Write)
{
TRACE("NodeVitastor::Write");
NodeVitastor* self = NODE_THIS();
uint64_t pool = Nan::To<int64_t>(info[0]).FromJust();
uint64_t inode = Nan::To<int64_t>(info[1]).FromJust();
uint64_t offset = Nan::To<int64_t>(info[2]).FromJust();
char* buf = node::Buffer::Data(info[3]);
uint64_t len = node::Buffer::Length(info[3]);
uint64_t version = 0;
int cbpos = 4;
if (!info[4].IsEmpty() && info[4]->IsObject()) {
cbpos++;
v8::Isolate *isolate = info.GetIsolate();
auto context = isolate->GetCurrentContext();
auto key = v8::String::NewFromUtf8(isolate, "version").ToLocalChecked();
auto params = info[4].As<v8::Object>();
auto versionObj = params->Get(context, key).ToLocalChecked();
if (!versionObj.IsEmpty()) {
version = Nan::To<int64_t>(versionObj).FromJust();
}
}
v8::Local<v8::Function> callback = info[cbpos].As<v8::Function>();
auto req = new VitastorRequest(callback);
req->iov = { .iov_base = buf, .iov_len = len };
std::unique_lock<std::mutex> lock(self->mu);
vitastor_c_write(self->c, ((pool << (64-POOL_ID_BITS)) | inode), offset, len, version, &req->iov, 1, on_write_finish, req);
}
// sync(callback(err))
NAN_METHOD(NodeVitastor::Sync)
{
TRACE("NodeVitastor::Sync");
NodeVitastor* self = NODE_THIS();
v8::Local<v8::Function> callback = info[0].As<v8::Function>();
auto req = new VitastorRequest(callback);
std::unique_lock<std::mutex> lock(self->mu);
vitastor_c_sync(self->c, on_write_finish, req);
}
static void on_error(VitastorRequest *req, Nan::Callback & nanCallback, long retval)
{
// Legal errors: EINVAL, EIO, EROFS, ENOSPC, EINTR, ENOENT
v8::Local<v8::Value> args[1];
if (!retval)
args[0] = Nan::Null();
else if (retval == -ENOENT)
args[0] = Nan::Error("ENOENT");
else if (retval == -EINVAL)
args[0] = Nan::Error("EINVAL");
else if (retval == -EIO)
args[0] = Nan::Error("EIO");
else if (retval == -EIO)
args[0] = Nan::Error("EROFS");
else if (retval == -EROFS)
args[0] = Nan::Error("ENOSPC");
else if (retval == -ENOSPC)
args[0] = Nan::Error("ENOSPC");
else if (retval == -EINTR)
args[0] = Nan::Error("EINTR");
else
args[0] = Nan::Error(std::to_string(-retval).c_str());
nanCallback.Call(1, args, req);
}
void NodeVitastor::on_read_finish(void *opaque, long retval, uint64_t version)
{
Nan::HandleScope scope;
VitastorRequest *req = (VitastorRequest *)opaque;
Nan::Callback nanCallback(Nan::New(req->callback));
if (retval == -ENOENT) {
nanCallback.Call(0, NULL, req);
} else if (retval < 0) {
on_error(req, nanCallback, retval);
} else {
v8::Local<v8::Value> args[3];
args[0] = Nan::Null();
args[1] = Nan::NewBuffer((char*)req->iov.iov_base, req->iov.iov_len).ToLocalChecked();
args[2] = v8::BigInt::NewFromUnsigned(v8::Isolate::GetCurrent(), version);
nanCallback.Call(3, args, req);
}
delete req;
}
void NodeVitastor::on_write_finish(void *opaque, long retval)
{
Nan::HandleScope scope;
VitastorRequest *req = (VitastorRequest *)opaque;
Nan::Callback nanCallback(Nan::New(req->callback));
on_error(req, nanCallback, retval);
delete req;
}
NAN_METHOD(NodeVitastor::Destroy) {
TRACE("NodeVitastor::Destroy");
}

43
node-binding/client.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef NODE_VITASTOR_CLIENT_H
#define NODE_VITASTOR_CLIENT_H
#include <mutex>
#include <nan.h>
#include <vitastor_c.h>
class NodeVitastor : public Nan::ObjectWrap {
public:
// create()
static NAN_METHOD(Create);
// read(pool, inode, offset, len, callback(err, buffer, version))
static NAN_METHOD(Read);
// write(pool, inode, offset, buffer, { version }?, callback(err))
static NAN_METHOD(Write);
// sync(callback(err))
static NAN_METHOD(Sync);
/*
// read_bitmap(pool, inode, offset, len, callback(err, bitmap_buffer, version))
static NAN_METHOD(ReadBitmap);
// watch_inode(name, callback({ num, name, size, parent_id?, readonly?, meta?, mod_revision, block_size, bitmap_granularity, immediate_commit }))
static NAN_METHOD(WatchInode);
*/
// destroy()
static NAN_METHOD(Destroy);
~NodeVitastor();
private:
vitastor_c *c = NULL;
int eventfd = -1;
uv_poll_t poll_watcher;
std::mutex mu;
NodeVitastor();
static void on_io_readable(uv_poll_t* handle, int status, int revents);
static void on_read_finish(void *opaque, long retval, uint64_t version);
static void on_write_finish(void *opaque, long retval);
};
#endif

24
node-binding/package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "vitastor",
"version": "1.7.0",
"description": "Low-level native bindings to Vitastor client library",
"main": "index.js",
"keywords": [
"storage",
"sds",
"vitastor"
],
"repository": {
"type": "git",
"url": "git://git.yourcmc.ru/vitalif/vitastor.git"
},
"scripts": {
"build": "node-gyp rebuild"
},
"author": "Vitaliy Filippov",
"license": "GPL-2.0-or-later",
"dependencies": {
"bindings": "1.5.0",
"nan": "^2.19.0"
}
}