WIP node.js binding
parent
50e56b3b92
commit
be7efcd64f
|
@ -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)
|
|
@ -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
|
|
@ -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)'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue