commit 68a5fecb11d7bf62870f44dff187495beef98a28 Author: brettlangdon Date: Sun Oct 27 17:58:18 2013 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85fc3d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +node_modules +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..9611c86 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +node-wgdb +========= + +Bindings for [WhiteDB](http://whitedb.org/) for [Node.JS](http://nodejs.org/). + +This is currently a work in progress and is not yet published to [npm](http://npmjs.org/). + +## TODO +* Need to add in locking +* Need to finish flushing out the API +* The C++ code really needs a decent refactor +* Test might be a good idea + +## License +``` +The MIT License (MIT) + +Copyright (c) 2013 Brett Langdon + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..922c957 --- /dev/null +++ b/binding.gyp @@ -0,0 +1,18 @@ +{ + "targets": [ + { + "include_dirs": [ + ], + "libraries": [ + "-L/usr/local/lib", + "-lwgdb" + ], + "sources": [ + "src/utils.cc", + "src/wgdb.cc", + "src/main.cc" + ], + "target_name": "wgdb" + } + ] +} diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..2ed9b82 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,5 @@ +try{ + return module.exports = require("../build/Release/wgdb").wgdb; +}catch(e){ + return module.exports = require("../build/Debug/wgdb").wgdb; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2eacef2 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "author": "Brett Langdon (http://brett.is/)", + "bugs": { + "url": "https://github.com/brettlangdon/node-wgdb/issues" + }, + "description": "Bindings for WhiteDB (wgdb)", + "homepage": "https://github.com/brettlangdon/node-wgdb", + "keywords": [ + "white", + "db", + "database", + "wgdb", + "whitedb" + ], + "license": "MIT", + "main": "lib/index.js", + "name": "wgdb", + "repository": { + "type": "git", + "url": "git://github.com/brettlangdon/node-wgdb.git" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "version": "0.1.0" +} diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..ffded85 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,14 @@ +#include +#include + +#include "wgdb.h" + +using namespace node; +using namespace v8; + +void init(Handle target){ + WgDB::Init(target); + Record::Init(); +} + +NODE_MODULE(wgdb, init); diff --git a/src/utils.cc b/src/utils.cc new file mode 100644 index 0000000..337d683 --- /dev/null +++ b/src/utils.cc @@ -0,0 +1,31 @@ +#include "utils.h" + + +char* get_str(Local val){ + if(!val->IsString()){ + ThrowException(Exception::TypeError(String::New("Argument Must Be A String"))); + return NULL; + } + + String::Utf8Value val_string(val); + char * val_char_ptr = (char *) malloc(val_string.length() + 1); + strcpy(val_char_ptr, *val_string); + return val_char_ptr; +} + + +Local encoded_to_v8(void* db_ptr, wg_int enc){ + wg_int type = wg_get_encoded_type(db_ptr, enc); + switch(type){ + case WG_INTTYPE: + return Int32::New(wg_decode_int(db_ptr, enc)); + case WG_NULLTYPE: + return Local::New(Null()); + case WG_DOUBLETYPE: + return Number::New(wg_decode_double(db_ptr, enc)); + case WG_STRTYPE: + return String::New(wg_decode_str(db_ptr, enc)); + default: + return Local::New(Undefined()); + } +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..6d54a9d --- /dev/null +++ b/src/utils.h @@ -0,0 +1,15 @@ +#ifndef WGDB_UTILS_H +#define WGDB_UTILS_H + +#include +#include +#include + +using namespace node; +using namespace v8; + +char* get_str(Local val); + +Local encoded_to_v8(void* db_ptr, wg_int enc); + +#endif diff --git a/src/wgdb.cc b/src/wgdb.cc new file mode 100644 index 0000000..f10c1ab --- /dev/null +++ b/src/wgdb.cc @@ -0,0 +1,434 @@ +#include "wgdb.h" + +Persistent Record::constructor; + +/* + * + * Structs + * +*/ +struct Baton { + bool has_cb; + Persistent callback; + WgDB* wgdb; + const char* error; + void* data; +}; + +struct Fields { + int length; + wg_int* encs; +}; + +struct RecordData { + int length; + void* record; +}; + + +/* + * + * Async Functions + * +*/ +void end_call(bool has_cb, Persistent callback, const char* error, Local result){ + HandleScope scope; + TryCatch try_catch; + + if(has_cb){ + Local argv[2]; + if(error){ + argv[0] = Exception::Error(String::New(error)); + } else{ + argv[0] = Local::New(Undefined()); + } + argv[1] = result; + callback->Call(Context::GetCurrent()->Global(), 2, argv); + } + + if(try_catch.HasCaught()){ + FatalException(try_catch); + } +} + +void do_attach(uv_work_t* req){ + Baton* baton = static_cast(req->data); + if(baton->wgdb && baton->wgdb->db_ptr){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s is already attached", baton->wgdb->db_name); + baton->error = buffer; + return; + } + + baton->wgdb->db_ptr = wg_attach_database(baton->wgdb->db_name, baton->wgdb->size); + if(!baton->wgdb->db_ptr){ + char buffer[1024]; + sprintf(buffer, "wgdb could not attach database %s (%d)", baton->wgdb->db_name, baton->wgdb->size); + baton->error = buffer; + return; + } +} + +void do_detach(uv_work_t* req){ + Baton* baton = static_cast(req->data); + if(!baton->wgdb || !baton->wgdb->db_ptr){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s is not attached", baton->wgdb->db_name); + baton->error = buffer; + return; + } + if(wg_detach_database(baton->wgdb->db_ptr) != 0){ + char buffer[1024]; + sprintf(buffer, "wgdb could not detach database %s", baton->wgdb->db_name); + baton->error = buffer; + return; + } +} + +void do_delete(uv_work_t* req){ + Baton* baton = static_cast(req->data); + if(!baton->wgdb || !baton->wgdb->db_ptr){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s is not attached", baton->wgdb->db_name); + baton->error = buffer; + return; + } + if(wg_delete_database(baton->wgdb->db_name) != 0){ + char buffer[1024]; + sprintf(buffer, "wgdb could not delete database %s", baton->wgdb->db_name); + baton->error = buffer; + return; + } +} + +void do_create_record(uv_work_t* req){ + Baton* baton = static_cast(req->data); + + RecordData* data = static_cast(baton->data); + data->record = wg_create_record(baton->wgdb->db_ptr, data->length); + if(data->record == NULL){ + char buffer[1024]; + sprintf(buffer, "wgdb database %s could not create record of length %d", baton->wgdb->db_name, data->length); + baton->error = buffer; + delete data; + return; + } + + baton->data = data; +} + +void do_record_next(uv_work_t* req){ + Baton* baton = static_cast(req->data); + + Record* record = static_cast(baton->data); + + RecordData* new_data = new RecordData(); + new_data->record = wg_get_next_record(record->wgdb->db_ptr, record->rec_ptr); + + baton->data = new_data; +} + +void do_record_fields(uv_work_t* req){ + Baton* baton = static_cast(req->data); + + Record* record = static_cast(baton->data); + + wg_int length = wg_get_record_len(record->wgdb->db_ptr, record->rec_ptr); + if(length < 0){ + char buffer[1024]; + sprintf(buffer, "could not get record length from database %s", record->wgdb->db_name); + baton->error = buffer; + return; + } + + wg_int enc; + int i; + Fields* fields = new Fields(); + fields->length = (int)length; + fields->encs = new wg_int[length]; + for(i = 0; i < length; ++i){ + enc = wg_get_field(record->wgdb->db_ptr, record->rec_ptr, i); + fields->encs[i] = enc; + } + + baton->data = fields; +} + +void do_after_no_result(uv_work_t* req, int status){ + Baton* baton = static_cast(req->data); + Local result = Local::New(Undefined()); + end_call(baton->has_cb, baton->callback, baton->error, result); + baton->callback.Dispose(); + delete baton; + delete req; +} + +void do_after_fields(uv_work_t* req, int status){ + Baton* baton = static_cast(req->data); + Fields* fields = static_cast(baton->data); + Local result = Array::New(fields->length); + for(int i = 0; i < fields->length; ++i){ + result->Set(i, encoded_to_v8(baton->wgdb->db_ptr, fields->encs[i])); + } + end_call(baton->has_cb, baton->callback, baton->error, result); + delete baton; + delete req; +} + +void Record::do_after_create_record(uv_work_t* req, int status){ + Baton* baton = static_cast(req->data); + RecordData* data = static_cast(baton->data); + Local result; + if(!baton->error && data->record){ + Local record_obj = Record::constructor->NewInstance(0, NULL); + Record* record = ObjectWrap::Unwrap(record_obj); + record->rec_ptr = data->record; + record->wgdb = baton->wgdb; + result = Local::New(record_obj); + } else{ + result = Local::New(Undefined()); + } + end_call(baton->has_cb, baton->callback, baton->error, result); + baton->callback.Dispose(); + delete baton; + delete req; +} + +/* + * + * WgDB Class Definitions + * +*/ + +void WgDB::Init(Handle target){ + Local tpl = FunctionTemplate::New(WgDB::New); + tpl->SetClassName(String::NewSymbol("wgdb")); + tpl->InstanceTemplate()->SetInternalFieldCount(2); + + tpl->PrototypeTemplate()->Set(String::NewSymbol("attach"), + FunctionTemplate::New(WgDB::Attach)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("delete"), + FunctionTemplate::New(WgDB::Delete)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("detach"), + FunctionTemplate::New(WgDB::Detach)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("createRecord"), + FunctionTemplate::New(WgDB::CreateRecord)->GetFunction()); + + Persistent constructor = Persistent::New(tpl->GetFunction()); + target->Set(String::NewSymbol("wgdb"), constructor); +} + + +Handle WgDB::New(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + if(!args.IsConstructCall()){ + return ThrowException(Exception::SyntaxError(String::New("wgdb requires the 'new' operator to create an instance"))); + } + + char* db_name = NULL; + int size = 0; + if(argc > 0 && args[0]->IsNumber()){ + db_name = get_str(args[0]->ToString()); + } else{ + return ThrowException(Exception::TypeError(String::New("wgdb argument 1 must be an integer"))); + } + + if(argc > 1 && args[1]->IsNumber()){ + size = (int)(args[1]->ToInt32()->Value()); + } + + WgDB* db = new WgDB(); + db->db_name = db_name; + db->size = size; + db->Wrap(args.This()); + return scope.Close(args.This()); +} + + +Handle WgDB::Attach(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + WgDB* db = ObjectWrap::Unwrap(args.This()); + + baton->wgdb = db; + db->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_attach, do_after_no_result); + + return scope.Close(Undefined()); +} + + +Handle WgDB::Delete(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + WgDB* db = ObjectWrap::Unwrap(args.This()); + baton->wgdb = db; + db->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_delete, do_after_no_result); + + return scope.Close(Undefined()); +} + + +Handle WgDB::Detach(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + WgDB* db = ObjectWrap::Unwrap(args.This()); + baton->wgdb = db; + db->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_detach, do_after_no_result); + + return scope.Close(Undefined()); +} + + +Handle WgDB::CreateRecord(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 1 && args[0]->IsNumber()){ + RecordData* data = new RecordData(); + data->length = (int)args[0]->ToInt32()->Value(); + baton->data = data; + + if(args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + } else{ + return ThrowException(Exception::TypeError(String::New("createRecord argument 1 must be an integer"))); + } + + WgDB* db = ObjectWrap::Unwrap(args.This()); + baton->wgdb = db; + db->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_create_record, Record::do_after_create_record); + + return scope.Close(Undefined()); +} + + +/* + * + * Record Class Definitions + * +*/ + +void Record::Init(){ + HandleScope scope; + Local tpl = FunctionTemplate::New(New); + tpl->SetClassName(String::NewSymbol("record")); + tpl->InstanceTemplate()->SetInternalFieldCount(2); + tpl->PrototypeTemplate()->Set(String::NewSymbol("next"), + FunctionTemplate::New(Record::Next)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("getLength"), + FunctionTemplate::New(Record::Length)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("getFields"), + FunctionTemplate::New(Record::Fields)->GetFunction()); + tpl->PrototypeTemplate()->Set(String::NewSymbol("setField"), + FunctionTemplate::New(Record::SetField)->GetFunction()); + constructor = Persistent::New(tpl->GetFunction()); +} + + +Handle Record::New(const Arguments& args){ + HandleScope scope; + + Record* record = new Record(); + record->Wrap(args.This()); + + return scope.Close(args.This()); +} + +Handle Record::Next(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + Record* record = ObjectWrap::Unwrap(args.This()); + baton->wgdb = record->wgdb; + baton->data = record; + record->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_record_next, Record::do_after_create_record); + + return scope.Close(Undefined()); +} + +Handle Record::Length(const Arguments& args){ + HandleScope scope; + + Record* record = ObjectWrap::Unwrap(args.This()); + wg_int length = wg_get_record_len(record->wgdb->db_ptr, record->rec_ptr); + if(length < 0){ + + } + + return scope.Close(Int32::New(length)); +} + +Handle Record::Fields(const Arguments& args){ + HandleScope scope; + int argc = args.Length(); + + Baton* baton = new Baton(); + if(argc > 0 && args[argc - 1]->IsFunction()){ + baton->has_cb = true; + baton->callback = Persistent::New(Local::Cast(args[argc - 1])); + } + + Record* record = ObjectWrap::Unwrap(args.This()); + baton->wgdb = record->wgdb; + baton->data = record; + record->Ref(); + + uv_work_t *req = new uv_work_t; + req->data = baton; + uv_queue_work(uv_default_loop(), req, do_record_fields, do_after_fields); + + return scope.Close(Undefined()); +} diff --git a/src/wgdb.h b/src/wgdb.h new file mode 100644 index 0000000..9d8d098 --- /dev/null +++ b/src/wgdb.h @@ -0,0 +1,47 @@ +#ifndef WGDB_DB_H +#define WGDB_DB_H + +#include +#include +#include + +#include "utils.h" + +using namespace node; +using namespace v8; + +class WgDB : ObjectWrap{ + public: + void* db_ptr; + char* db_name; + int size; + + static void Init(Handle target); + static Handle New(const Arguments& args); + static Handle Attach(const Arguments& args); + static Handle Detach(const Arguments& args); + static Handle Delete(const Arguments& args); + static Handle CreateRecord(const Arguments& args); + static Handle FirstRecord(const Arguments& args); +}; + +class Record : ObjectWrap{ + public: + WgDB* wgdb; + void* rec_ptr; + + static Persistent constructor; + static void Init(); + static Handle New(const Arguments& args); + static Handle Next(const Arguments& args); + + static Handle SetField(const Arguments& args); + static Handle GetField(const Arguments& args); + static Handle Length(const Arguments& args); + static Handle Fields(const Arguments& args); + static Handle Delete(const Arguments& args); + + static void do_after_create_record(uv_work_t* req, int status); +}; + +#endif