├── .npmignore ├── io ├── .gitignore ├── timer.c ├── util │ ├── hexdump.c │ └── lcb_luv_yolog.h ├── libcouchbase-libuv.h ├── plugin-libuv.c ├── socket.c ├── write.c ├── read.c ├── lcb_luv_internal.h └── common.c ├── AUTHORS ├── tests ├── runtests.sh ├── README.md ├── 06-add.js ├── setup.js ├── bug.js ├── 00-args.t.js ├── 04-delete.js ├── 05-replace.js ├── 07-exception-handling.js ├── 03-multiget.js ├── 08-views.js ├── 01-set.js └── 02-get.js ├── .gitignore ├── src ├── cas.h ├── namemap.cc ├── namemap.h ├── cookie.h ├── cas.cc ├── args.h ├── couchbase_impl.h ├── operations.cc ├── args.cc ├── notify.cc └── couchbase_impl.cc ├── package.json ├── example.js ├── Makefile ├── wscript ├── lib ├── couchbase.js └── bucket.js ├── README.md └── LICENSE /.npmignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | -------------------------------------------------------------------------------- /io/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | test-main 4 | *.swp 5 | uv/* 6 | .depend 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # The following people (sorted alphabetically) contributed to the 2 | # project in one way or another. 3 | 4 | Mark Nunberg 5 | Sergey Avseyev 6 | Trond Norbye 7 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | for t in $@; do 3 | node $t 4 | rv=$? 5 | case $rv in 6 | 0) 7 | echo "$t .. OK" 8 | ;; 9 | 64) 10 | echo "$t .. SKIP" 11 | ;; 12 | *) 13 | echo "$t .. FAIL" 14 | ;; 15 | esac 16 | done 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | 14 | # The original file after running astyle: 15 | *.orig 16 | 17 | 18 | *~ 19 | 20 | /.lock-wscript 21 | /build/ 22 | /tests/config.json 23 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | In order to be able to run the unit tests you need to create a file 2 | named config.json with the following content: 3 | 4 | { 5 | "hostname" : "localhost:8091", 6 | "username" : "Administrator", 7 | "password" : "password", 8 | "bucket" : "default" 9 | } 10 | 11 | You should of course use values that match your own setup ;) 12 | 13 | -------------------------------------------------------------------------------- /src/cas.h: -------------------------------------------------------------------------------- 1 | #ifndef CAS_H_ 2 | #define CAS_H_ 3 | 4 | #include 5 | namespace Couchnode 6 | { 7 | 8 | class Cas 9 | { 10 | public: 11 | 12 | static void initialize(); 13 | static v8::Persistent CreateCas(uint64_t); 14 | static uint64_t GetCas(v8::Handle); 15 | static v8::Handle GetHumanReadable(v8::Local, 16 | const v8::AccessorInfo &); 17 | }; 18 | 19 | 20 | } // namespace Couchnode 21 | 22 | #endif /* CAS_H_ */ 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "bugs" : { "url" : "www.couchbase.com/issues", "email" : "support@couchbase.com" }, 3 | "description": "Client library for accessing a Couchbase Server cluster", 4 | "engines" : { "node" : ">=0.8.8" }, 5 | "homepage": "http://www.couchbase.com/develop", 6 | "keywords": ["couchbase", "libcouchbase", "memcached", "nosql", "json", "document"], 7 | "main": "./lib/couchbase", 8 | "name": "couchbase", 9 | "dependencies": { 10 | "request": "2.11.x" 11 | }, 12 | "repository" : { "type" : "git", "url" : "http://github.com/couchbase/couchnode.git" }, 13 | "version": "0.0.3" 14 | } 15 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var couchbase = require(__dirname), 2 | http = require("http"); 3 | 4 | couchbase.connect({ 5 | "username" : "Administrator", 6 | "password":"animal", 7 | "hostname" : "localhost:8091", 8 | "bucket":"default" 9 | }, function(err, bucket) { 10 | if (err) {throw(err)} 11 | 12 | http.createServer(function(req, resp) { 13 | bucket.get("hitcount", function(err, doc, meta) { 14 | if (!doc) { 15 | doc = {count:0}; 16 | } 17 | doc.count++; 18 | resp.writeHead(200); 19 | bucket.set("hitcount", doc, meta, function(err) { 20 | resp.end('

The server has seen '+doc.count+' hits

'); 21 | }) 22 | }) 23 | }).listen(8080); 24 | }) 25 | -------------------------------------------------------------------------------- /tests/06-add.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup(function(err, cb) { 5 | assert(!err, "setup failure"); 6 | 7 | cb.on("error", function (message) { 8 | console.log("ERROR: [" + message + "]"); 9 | process.exit(1); 10 | }); 11 | 12 | var testkey = "06-add.js" 13 | 14 | cb.delete(testkey, function(){ 15 | cb.add(testkey, "bar", function(err, meta) { 16 | assert(!err, "Can add object at empty key"); 17 | assert.equal(testkey, meta.id, "Callback called with wrong key!") 18 | // try to add existing key, should fail 19 | cb.add(testkey, "baz", function (err, meta) { 20 | assert(err, "Can't add object at empty key"); 21 | process.exit(0) 22 | }); 23 | }); 24 | }); 25 | }) 26 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | var cb = require('../lib/couchbase.js'), 2 | fs = require('fs'); 3 | 4 | var config = JSON.parse(fs.readFileSync('./config.json')); 5 | 6 | module.exports = function(callback) { 7 | // this is turned off because it doesn't seem to work... 8 | // process.on("uncaughtException", function() { 9 | // console.log("uncaughtException", arguments) 10 | // process.exit(1) 11 | // }) 12 | setTimeout(function() { 13 | console.log("timeout, assuming failure") 14 | process.exit(1) 15 | }, 1000) 16 | cb.connect(config, callback); 17 | }; 18 | 19 | // very lightweight "test framework" 20 | var planned = 1; 21 | module.exports.plan = function(count) { 22 | planned = count; 23 | }; 24 | module.exports.end = function(code) { 25 | if (code > 0) { 26 | process.exit(code) 27 | } else { 28 | planned--; 29 | if (!planned) { 30 | process.exit(0); 31 | } 32 | } 33 | }; -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCE = src/couchbase_impl.cc src/couchbase_impl.h src/args.cc src/notify.cc \ 2 | src/namemap.cc src/operations.cc src/namemap.h src/cas.cc \ 3 | src/cas.h 4 | 5 | all: .lock-wscript $(SOURCE) 6 | @node-waf build 7 | 8 | .lock-wscript: wscript 9 | @node-waf configure 10 | 11 | clean: 12 | @node-waf clean 13 | 14 | install: 15 | @node-waf install 16 | 17 | dist: 18 | @node-waf dist 19 | 20 | check: 21 | (cd tests && ./runtests.sh 0*.js) 22 | 23 | reformat: 24 | @astyle --mode=c \ 25 | --quiet \ 26 | --style=1tbs \ 27 | --indent=spaces=4 \ 28 | --indent-namespaces \ 29 | --indent-col1-comments \ 30 | --max-instatement-indent=78 \ 31 | --pad-oper \ 32 | --pad-header \ 33 | --add-brackets \ 34 | --unpad-paren \ 35 | --align-pointer=name \ 36 | src/*.cc \ 37 | src/*.h 38 | -------------------------------------------------------------------------------- /tests/bug.js: -------------------------------------------------------------------------------- 1 | core.cb.get('index::users', 0, function(data, err, key, cas, flags, value){ 2 | console.log('Get 1: ' + data, err, key, cas, flags, value); 3 | core.cb.set('index::users', value, 0, cas, function(data, err, key, cas) { 4 | console.log('Set 1: ' + data, err, key, cas); 5 | }); 6 | }); 7 | core.cb.get('index::users', 0, function(data, err, key, cas, flags, value){ 8 | console.log('Get 2: ' + data, err, key, cas, flags, value); 9 | core.cb.set('index::users', value, 0, cas, function(data, err, key, cas) { 10 | console.log('Set 2: ' + data, err, key, cas); 11 | }); 12 | }); 13 | 14 | /* 15 | Results in the following output: 16 | Get 1: undefined false index::users 94312808895741950 0 ::60:: 17 | Get 2: undefined false index::users 94312808895741950 0 ::60:: 18 | Set 1: undefined false index::users 166370402933669900 19 | Set 2: undefined false index::users 238427996971597820 20 | 21 | Shouldn't Set 2 fail because the CAS changed when Set 1 occurred? 22 | */ 23 | -------------------------------------------------------------------------------- /tests/00-args.t.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup.plan(4) 5 | 6 | setup(function(err, cb) { 7 | assert(!err, "setup failure"); 8 | 9 | cb.on("error", function (message) { 10 | console.log("ERROR: [" + message + "]"); 11 | process.exit(1); 12 | }); 13 | 14 | // things that should work 15 | assert.doesNotThrow(function() { 16 | cb.get("has callback", setup.end) 17 | }) 18 | 19 | assert.doesNotThrow(function() { 20 | cb.set("has callback", "value", setup.end) 21 | }) 22 | 23 | assert.doesNotThrow(function() { 24 | // falsy values for CAS and exp 25 | [null, undefined, 0, false].forEach(function(fv) { 26 | cb.set("has falsy meta", "value", {cas : fv, exp : fv}, setup.end) 27 | }) 28 | }) 29 | 30 | // things that should error 31 | assert.throws(function() { 32 | cb.get("needs callback") 33 | }) 34 | 35 | assert.throws(function() { 36 | cb.set("needs callback") 37 | }) 38 | setup.end() 39 | }) 40 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # vim: ft=python 2 | 3 | import os 4 | import os.path 5 | 6 | def set_options(opt): 7 | opt.tool_options("compiler_cc") 8 | opt.tool_options("compiler_cxx") 9 | 10 | 11 | couchnode_mods = [ 12 | "args", 13 | "couchbase_impl", 14 | "namemap", 15 | "notify", 16 | "operations", 17 | "cas" 18 | ] 19 | 20 | lcb_luv_mods = [ 21 | "common", 22 | "socket", 23 | "read", 24 | "write", 25 | "timer", 26 | "plugin-libuv", 27 | os.path.join("util", "lcb_luv_yolog"), 28 | os.path.join("util", "hexdump") 29 | ] 30 | 31 | 32 | 33 | def configure(conf): 34 | conf.check_tool("compiler_cxx") 35 | conf.check_tool("compiler_cc") 36 | conf.check_tool("node_addon") 37 | 38 | def build(bld): 39 | obj = bld.new_task_gen("cc", "cxx", "node_addon", "cshlib") 40 | obj.cxxflags = ["-g", "-I.."] 41 | obj.cppflags = obj.cxxflags 42 | obj.ldflags = ["-lcouchbase" ] 43 | obj.target = "couchbase_impl" 44 | 45 | obj.source = [ os.path.join("src", mod) + ".cc" for mod in couchnode_mods ] 46 | obj.source += [ os.path.join("io", mod) + ".c" for mod in lcb_luv_mods ] 47 | -------------------------------------------------------------------------------- /tests/04-delete.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup(function(err, cb) { 5 | assert(!err, "setup failure"); 6 | 7 | cb.on("error", function (message) { 8 | console.log("ERROR: [" + message + "]"); 9 | process.exit(1); 10 | }); 11 | 12 | var testkey = "04-delete.js" 13 | 14 | cb.set(testkey, "bar", function (err, meta) { 15 | assert(!err, "Failed to store object"); 16 | assert.equal(testkey, meta.id, "Get callback called with wrong key!") 17 | 18 | cb.delete(testkey, function (err, meta) { 19 | assert(!err, "Failed to delete object"); 20 | assert.equal(testkey, meta.id, "Delete existing called with wrong key!") 21 | 22 | // now delete it even when it doesn't exist 23 | cb.delete(testkey, function (err, meta) { 24 | assert(err, "Can't delete object that is already deleted"); 25 | assert.equal(testkey, meta.id, "Delete missing called with wrong key!") 26 | process.exit(0); 27 | }); 28 | }); 29 | }); 30 | }) 31 | -------------------------------------------------------------------------------- /tests/05-replace.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup(function(err, cb) { 5 | assert(!err, "setup failure"); 6 | 7 | cb.on("error", function (message) { 8 | console.log("ERROR: [" + message + "]"); 9 | process.exit(1); 10 | }); 11 | 12 | var testkey = "05-replace.js" 13 | 14 | cb.delete(testkey, function(){ 15 | // try to replace a missing key, should fail 16 | cb.replace(testkey, "bar", function(err, meta) { 17 | assert(err, "Can't replace object that is already deleted"); 18 | cb.set(testkey, "bar", function (err, meta) { 19 | assert(!err, "Failed to store object"); 20 | cb.replace(testkey, "bazz", function (err, meta) { 21 | assert(!err, "Failed to replace object"); 22 | cb.get(testkey, function (err, doc) { 23 | assert(!err, "Failed to get object"); 24 | assert.equal("bazz", doc, "Replace didn't work") 25 | process.exit(0); 26 | }) 27 | }); 28 | }); 29 | }); 30 | }); 31 | }) 32 | -------------------------------------------------------------------------------- /tests/07-exception-handling.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | // something is breaking standard node.js exception behavior, 5 | // this will turn out to be an issue for any serious user. 6 | 7 | // there is not a simple way to test this, except to ask you to 8 | // comment out different code paths... so please try this with 9 | // normal set to true or false depending. 10 | 11 | // var useCouchbase = false; 12 | var useCouchbase = true; 13 | 14 | process.on("uncaughtException", function() { 15 | // console.log("uncaughtException", arguments) 16 | // console.log("exiting") 17 | process.exit() 18 | }) 19 | 20 | if (!useCouchbase) { 21 | // lets do something async just to minimize the difference between 22 | // the normal case and the Couchbase case 23 | setTimeout(function() { 24 | assert(false, "should exit after error") 25 | },100) 26 | } else { 27 | // with couchbase started up, the exceptions aren't handled right 28 | setup(function(err, cb) { 29 | // uncomment this line to see the same issue with 30 | // non-assert based exceptions. 31 | // foobar() 32 | assert(false, "should exit after error") 33 | }) 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/03-multiget.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup(function(err, cb) { 5 | assert(!err, "setup failure"); 6 | 7 | cb.on("error", function (message) { 8 | console.log("ERROR: [" + message + "]"); 9 | process.exit(1); 10 | }); 11 | 12 | // create a bunch of items and get them with multiget 13 | var calledTimes = 0, 14 | keys = ["key0", "key1", "key2", "key3", "key4", 15 | "key5", "key6", "key7", "key8", "key9"]; 16 | 17 | function setHandler(err) { 18 | assert(!err, "set error") 19 | calledTimes++; 20 | if (calledTimes > 9) { 21 | calledTimes = 0; 22 | doGets(); 23 | } 24 | }; 25 | 26 | keys.forEach(function(k) { 27 | cb.set(k, "value", setHandler); 28 | }) 29 | 30 | function getHandler(err, doc, meta) { 31 | assert(!err, "get error") 32 | calledTimes++; 33 | if (calledTimes > 9) { 34 | process.exit(0); 35 | } 36 | }; 37 | 38 | function doGets() { 39 | // normal get 40 | cb.get("key0", function(err, meta) { 41 | assert(!err, "get error"); 42 | // multiget 43 | cb.get(keys, getHandler); 44 | }) 45 | }; 46 | }) 47 | -------------------------------------------------------------------------------- /src/namemap.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #include "namemap.h" 18 | #include 19 | 20 | using namespace Couchnode; 21 | 22 | v8::Persistent NameMap::names[NameMap::MAX]; 23 | 24 | void NameMap::initialize() 25 | { 26 | install("expiry", EXPIRY); 27 | install("cas", CAS); 28 | install("data", DATA); 29 | install("initial", INITIAL); 30 | install("positional", OPSTYLE_POSITIONAL); 31 | install("dict", OPSTYLE_HASHTABLE); 32 | install("str", PROP_STR); 33 | } 34 | 35 | void NameMap::install(const char *name, dict_t val) 36 | { 37 | using namespace v8; 38 | names[val] = Persistent::New(String::New(name, strlen(name))); 39 | } 40 | -------------------------------------------------------------------------------- /tests/08-views.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup(function(err, cb) { 5 | assert(!err, "setup failure"); 6 | 7 | cb.on("error", function (message) { 8 | console.log("ERROR: [" + message + "]"); 9 | process.exit(1); 10 | }); 11 | 12 | var testkey = "08-views.js" 13 | 14 | cb.set(testkey, "bar", function (err, meta) { 15 | assert(!err, "Failed to store object"); 16 | assert.equal(testkey, meta.id, "Set callback called with wrong key!") 17 | 18 | cb.get(testkey, function (err, doc, meta) { 19 | assert(!err, "Failed to get object"); 20 | assert.equal(testkey, meta.id, "Get existing called with wrong key!") 21 | 22 | // todo: defineView code... 23 | // cb.defineView("test-design","test-view", { 24 | // map : 'function(doc, meta){emit(meta.id)}' 25 | // }, function(err) { 26 | // now lets find our key in the view 27 | cb.view("test-design","test-view", {key : testkey}, function(err, resp, view) { 28 | assert(!err, "error fetching view"); 29 | assert(view.rows.length > 0) 30 | assert.equal(testkey, view.rows[0].key) 31 | setup.end() 32 | }); 33 | // }); // defineView callback 34 | }); 35 | }); 36 | }) 37 | -------------------------------------------------------------------------------- /src/namemap.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #ifndef NAMEMAP_H 18 | #define NAMEMAP_H 1 19 | 20 | #define BUILDING_NODE_EXTENSION 21 | #include 22 | #include 23 | 24 | namespace Couchnode 25 | { 26 | // @todo refactor this into a more OO fashioned way.. 27 | 28 | class NameMap 29 | { 30 | public: 31 | // This is a cache of strings we will have hanging around. This makes 32 | // it easier to pass string 'literals' to functions which want a 33 | // v8::String 34 | typedef enum { 35 | EXPIRY = 0, 36 | CAS, 37 | DATA, 38 | INITIAL, 39 | OPSTYLE_POSITIONAL, 40 | OPSTYLE_HASHTABLE, 41 | PROP_STR, 42 | MAX 43 | } dict_t; 44 | static v8::Persistent names[MAX]; 45 | static void initialize(); 46 | protected: 47 | static void install(const char *name, dict_t val); 48 | }; 49 | 50 | } // namespace Couchnode 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /io/timer.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | 3 | struct my_timer_st { 4 | uv_timer_t uvt; 5 | lcb_luv_callback_t callback; 6 | void *cb_arg; 7 | }; 8 | 9 | 10 | static void 11 | timer_cb(uv_timer_t *uvt, int status) 12 | { 13 | struct my_timer_st *timer = (struct my_timer_st*)uvt; 14 | if (timer->callback) { 15 | timer->callback(-1, 0, timer->cb_arg); 16 | } 17 | } 18 | 19 | void * 20 | lcb_luv_create_timer(struct lcb_io_opt_st *iops) 21 | { 22 | struct my_timer_st *timer = calloc(1, sizeof(*timer)); 23 | uv_timer_init(IOPS_COOKIE(iops)->loop, &timer->uvt); 24 | IOPS_COOKIE(iops)->timer_count++; 25 | return timer; 26 | } 27 | 28 | int 29 | lcb_luv_update_timer(struct lcb_io_opt_st *iops, 30 | void *timer_opaque, 31 | lcb_uint32_t usec, 32 | void *cbdata, 33 | lcb_luv_callback_t callback) 34 | { 35 | struct my_timer_st *timer = (struct my_timer_st*)timer_opaque; 36 | timer->callback = callback; 37 | timer->cb_arg = cbdata; 38 | return uv_timer_start(&timer->uvt, timer_cb, usec, 0); 39 | } 40 | 41 | 42 | void 43 | lcb_luv_delete_timer(struct lcb_io_opt_st *iops, 44 | void *timer_opaque) 45 | { 46 | uv_timer_stop((uv_timer_t*)timer_opaque); 47 | ((struct my_timer_st*)timer_opaque)->callback = NULL; 48 | } 49 | 50 | static void 51 | timer_close_cb(uv_handle_t* handle) 52 | { 53 | free(handle); 54 | } 55 | 56 | void 57 | lcb_luv_destroy_timer(struct lcb_io_opt_st *iops, 58 | void *timer_opaque) 59 | { 60 | lcb_luv_delete_timer(iops, timer_opaque); 61 | IOPS_COOKIE(iops)->timer_count--; 62 | uv_close((uv_handle_t*)timer_opaque, timer_close_cb); 63 | } 64 | -------------------------------------------------------------------------------- /io/util/hexdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void lcb_luv_hexdump(void *data, int size) 6 | { 7 | /* dumps size bytes of *data to stdout. Looks like: 8 | * [0000] 75 6E 6B 6E 6F 77 6E 20 9 | * 30 FF 00 00 00 00 39 00 unknown 0.....9. 10 | * (in a single line of course) 11 | */ 12 | 13 | unsigned char *p = data; 14 | unsigned char c; 15 | int n; 16 | char bytestr[4] = {0}; 17 | char addrstr[10] = {0}; 18 | char hexstr[ 16*3 + 5] = {0}; 19 | char charstr[16*1 + 5] = {0}; 20 | for(n=1;n<=size;n++) { 21 | if (n%16 == 1) { 22 | /* store address for this line */ 23 | snprintf(addrstr, sizeof(addrstr), "%.4x", 24 | (unsigned int)(p - (unsigned char*)data)); 25 | } 26 | 27 | c = *p; 28 | if (isalnum(c) == 0) { 29 | c = '.'; 30 | } 31 | 32 | /* store hex str (for left side) */ 33 | snprintf(bytestr, sizeof(bytestr), "%02X ", *p); 34 | strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1); 35 | 36 | /* store char str (for right side) */ 37 | snprintf(bytestr, sizeof(bytestr), "%c", c); 38 | strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1); 39 | 40 | if(n%16 == 0) { 41 | /* line completed */ 42 | printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); 43 | hexstr[0] = 0; 44 | charstr[0] = 0; 45 | } else if(n%8 == 0) { 46 | /* half line: add whitespaces */ 47 | strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1); 48 | strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1); 49 | } 50 | p++; /* next byte */ 51 | } 52 | 53 | if (strlen(hexstr) > 0) { 54 | /* print rest of buffer if not empty */ 55 | printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /tests/01-set.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup.plan(2); // exit at second call to setup.end() 5 | 6 | setup(function(err, cb) { 7 | assert(!err, "setup failure"); 8 | 9 | cb.on("error", function (message) { 10 | console.log("ERROR: [" + message + "]"); 11 | process.exit(1); 12 | }); 13 | 14 | // test cas updates 15 | var testkey = "01-set.js" 16 | cb.set(testkey, "bar", function (err, firstmeta) { 17 | assert(!err, "Failed to store object"); 18 | assert.equal(testkey, firstmeta.id, "Callback called with wrong key!") 19 | 20 | cb.set(testkey, "baz", firstmeta, function(err, meta) { 21 | assert(!err, "Failed to set with cas"); 22 | assert.equal(testkey, meta.id, "Callback called with wrong key!") 23 | assert.notEqual(firstmeta.cas.str, meta.cas.str, "cas should change"); 24 | // use the old cas, should reject the write 25 | cb.set(testkey, "bam", firstmeta, function(err, meta) { 26 | assert(err, "Should error with cas mismatch"); 27 | cb.get(testkey, function(err, doc) { 28 | assert(!err, "Failed to load object"); 29 | assert.equal("baz", doc, "Document changed despite bad cas!") 30 | setup.end() 31 | }) 32 | }) 33 | }) 34 | }); 35 | 36 | // test non cas updates 37 | var testkey2 = "01-set.js2" 38 | cb.set(testkey2, {foo : "bar"}, function (err, meta) { 39 | assert(!err, "Failed to store object"); 40 | assert.equal(testkey2, meta.id, "Callback called with wrong key!") 41 | 42 | // non cas updates work too 43 | cb.set(testkey2, {foo : "baz"}, function(err, meta) { 44 | assert(!err, "Failed to set without cas"); 45 | assert.equal(testkey2, meta.id, "Callback called with wrong key!") 46 | setup.end(); 47 | }) 48 | }); 49 | }) 50 | -------------------------------------------------------------------------------- /src/cookie.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef COUCHNODE_COOKIE_H 3 | #define COUCHNODE_COOKIE_H 1 4 | 5 | #ifndef COUCHBASE_H 6 | #error "Include couchbase.h before including this file" 7 | #endif 8 | 9 | namespace Couchnode 10 | { 11 | 12 | class CouchbaseCookie 13 | { 14 | 15 | public: 16 | CouchbaseCookie(v8::Handle cbo, 17 | v8::Handle callback, 18 | v8::Handle data, 19 | unsigned int numRemaining); 20 | virtual ~CouchbaseCookie(); 21 | 22 | void result(lcb_error_t error, 23 | const void *key, lcb_size_t nkey, 24 | const void *bytes, 25 | lcb_size_t nbytes, 26 | lcb_uint32_t flags, 27 | lcb_cas_t cas); 28 | 29 | void result(lcb_error_t error, 30 | const void *key, lcb_size_t nkey, 31 | lcb_cas_t cas); 32 | 33 | void result(lcb_error_t error, 34 | const void *key, lcb_size_t nkey, 35 | lcb_uint64_t value, 36 | lcb_cas_t cas); 37 | 38 | void result(lcb_error_t error, 39 | const void *key, lcb_size_t nkey); 40 | 41 | protected: 42 | unsigned int remaining; 43 | void invoke(v8::Persistent &context, int argc, 44 | v8::Local *argv) { 45 | // Now, invoke the callback with the appropriate arguments 46 | ucallback->Call(v8::Context::GetEntered()->Global(), argc , argv); 47 | context.Dispose(); 48 | if (--remaining == 0) { 49 | delete this; 50 | } 51 | } 52 | 53 | private: 54 | v8::Persistent parent; 55 | v8::Persistent ucookie; 56 | v8::Persistent ucallback; 57 | }; 58 | 59 | } // namespace Couchnode 60 | #endif // COUCHNODE_COOKIE_H 61 | -------------------------------------------------------------------------------- /lib/couchbase.js: -------------------------------------------------------------------------------- 1 | var couchnode = require(__dirname + '/../build/Release/couchbase_impl'), 2 | bucket = require(__dirname + '/bucket'), 3 | verbose = false; // change to true to spew debug info 4 | 5 | // Helper functions, see below for public API methods 6 | function debugLog() { 7 | if (verbose) { 8 | console.log.apply(console, arguments); 9 | } 10 | }; 11 | 12 | function callOnce(callback) { 13 | var called = false; 14 | return function() { 15 | if (called) return; 16 | called = true; 17 | callback.apply(this, arguments); 18 | }; 19 | }; 20 | 21 | function prepareConfig(config) { 22 | var defaults = { 23 | hosts : [ "localhost:8091", "localhost:9000" ], 24 | bucket : "default", 25 | user : "Administrator" 26 | }; 27 | config = config || defaults; 28 | ["hosts", "bucket", "user"].forEach(function(key) { 29 | if (config[key] == undefined) { 30 | config[key] = defaults[key]; 31 | } 32 | }); 33 | debugLog('Using the following config:', config); 34 | return config; 35 | }; 36 | 37 | // ============ PUBLIC API ============ 38 | 39 | // Initialize a new connection 40 | // it calls your callback with the bucket object 41 | exports.connect = function(config, callback) { 42 | config = prepareConfig(config); 43 | var hosts = config.hosts.toString().replace(/,/g,';'), 44 | connection = new couchnode.CouchbaseImpl(hosts, config.user, config.password, config.bucket); 45 | 46 | // here we merge the responses to give a more idiomatic API 47 | // todo this could probably be done more cleanly in C-land 48 | var callbackOnce = callOnce(callback); 49 | 50 | connection.on("connect", function() { 51 | debugLog("connected", arguments); 52 | bucket.create(connection, config, function (readyBucket) { 53 | callbackOnce(false, readyBucket); 54 | }) 55 | }); 56 | connection.on("error", function(error) { 57 | debugLog("connection error", arguments); 58 | callbackOnce(error) 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /io/libcouchbase-libuv.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBCOUCHBASE_LIBUV_H_ 2 | #define LIBCOUCHBASE_LIBUV_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif /* __cplusplus */ 7 | 8 | /* needed for size_t used in couchbase.h */ 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | /* These variables define how much our read-ahead and write buffers 15 | * should be. Do not make these too low, but setting them to insanely 16 | * low amounts (for testing) should be nice. I've seen them work with 17 | * as little as 0x80. 18 | * 19 | * The size is static and not expanded, for each socket. 20 | */ 21 | #define LCB_LUV_READAHEAD 0x4000 22 | #define LCB_LUV_WRITEBUFSZ 0x4000 23 | 24 | typedef void (*lcb_luv_callback_t)(lcb_socket_t,short,void*); 25 | struct lcb_luv_socket_st; 26 | typedef struct lcb_luv_socket_st* lcb_luv_socket_t; 27 | 28 | struct lcb_luv_cookie_st; 29 | 30 | typedef void (*lcb_luv_start_cb_t)(struct lcb_luv_cookie_st *luv_cookie); 31 | typedef void (*lcb_luv_stop_cb_t)(struct lcb_luv_cookie_st *luv_cookie); 32 | 33 | /** 34 | * Structure we place inside the iops. This is usually global per-event loop 35 | */ 36 | struct lcb_luv_cookie_st { 37 | /* Private. Don't modify these or rely on them for meaning */ 38 | uv_loop_t *loop; 39 | lcb_luv_socket_t *socktable; 40 | uint16_t fd_next; 41 | uint16_t fd_max; 42 | int do_stop; 43 | unsigned timer_count; 44 | 45 | /* --- Public --- */ 46 | 47 | /* Data, put anything you want here */ 48 | void *data; 49 | 50 | /* These two functions will be called whenever 51 | * libcouchbase calls io->run_event_loop and io_stop_event_loop, 52 | * respectively 53 | */ 54 | lcb_luv_start_cb_t start_callback; 55 | lcb_luv_stop_cb_t stop_callback; 56 | }; 57 | 58 | /** 59 | * Create a new IO operation structure. 60 | * 61 | * @param loop The libuv loop (can be obtained normally via uv_default_loop()) 62 | * @param sock_max - The maximum amount of concurrent 'sockets' to be open. 63 | * sock_max * sizeof(char*) bytes are allocated per cookie you create, so you 64 | * probably don't want to make it too large. 8092 or such is a safe bet. 65 | */ 66 | struct lcb_io_opt_st * 67 | lcb_luv_create_io_opts(uv_loop_t *loop, uint16_t sock_max); 68 | 69 | /** 70 | * This macro 'safely' extracts and casts the cookie from the iops. 71 | */ 72 | #define lcb_luv_from_iops(iops) \ 73 | (struct lcb_luv_cookie_st *)(iops->cookie) 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif /* __cplusplus */ 78 | 79 | 80 | #endif /* LCB_LIBUV_H_ */ 81 | -------------------------------------------------------------------------------- /src/cas.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #include "cas.h" 3 | #include "couchbase_impl.h" 4 | #include 5 | static v8::Persistent CasTemplate; 6 | using namespace Couchnode; 7 | 8 | /** 9 | * If we have 64 bit pointers we can stuff the pointer into the field and 10 | * save on having to make a new uint64_t* 11 | */ 12 | #if defined(_LP64) && !defined(COUCHNODE_NO_CASINTPTR) 13 | static inline void *cas_to_pointer(uint64_t cas) 14 | { 15 | return (void *)(uintptr_t)cas; 16 | } 17 | static inline uint64_t cas_from_pointer(void *ptr) 18 | { 19 | return (uint64_t)(uintptr_t)ptr; 20 | } 21 | static inline void free_cas_pointer(void *) { } 22 | 23 | #else 24 | 25 | static inline void *cas_to_pointer(uint64_t cas) 26 | { 27 | return new uint64_t(cas); 28 | } 29 | 30 | static inline uint64_t cas_from_pointer(void *ptr) 31 | { 32 | if (!ptr) { 33 | return 0; 34 | } 35 | return *(uint64_t *)ptr; 36 | } 37 | 38 | static inline void free_cas_pointer(void *ptr) 39 | { 40 | if (ptr) { 41 | delete ptr; 42 | } 43 | } 44 | 45 | #endif 46 | 47 | void Cas::initialize() 48 | { 49 | CasTemplate = v8::Persistent::New( 50 | v8::ObjectTemplate::New()); 51 | CasTemplate->SetInternalFieldCount(1); 52 | 53 | CasTemplate->SetAccessor(NameMap::names[NameMap::PROP_STR], 54 | GetHumanReadable); 55 | } 56 | 57 | static void cas_object_dtor(v8::Persistent handle, void *) 58 | { 59 | v8::Handle obj = v8::Persistent::Cast(handle); 60 | free_cas_pointer(obj->GetPointerFromInternalField(0)); 61 | handle.Dispose(); 62 | } 63 | 64 | v8::Handle Cas::GetHumanReadable(v8::Local, 65 | const v8::AccessorInfo &accinfo) 66 | { 67 | std::stringstream ss; 68 | uint64_t cas = cas_from_pointer( 69 | accinfo.This()->GetPointerFromInternalField(0)); 70 | ss << cas; 71 | return v8::Local(v8::String::New(ss.str().c_str())); 72 | } 73 | 74 | v8::Persistent Cas::CreateCas(uint64_t cas) 75 | { 76 | v8::Persistent ret = v8::Persistent::New( 77 | CasTemplate->NewInstance()); 78 | 79 | ret->SetPointerInInternalField(0, cas_to_pointer(cas)); 80 | ret.MakeWeak(NULL, cas_object_dtor); 81 | 82 | return ret; 83 | } 84 | 85 | uint64_t Cas::GetCas(v8::Handle vstr) 86 | { 87 | uint64_t cas = 88 | cas_from_pointer(vstr->GetPointerFromInternalField(0)); 89 | if (!cas) { 90 | throw Couchnode::Exception("Invalid CAS", vstr); 91 | } 92 | return cas; 93 | } 94 | -------------------------------------------------------------------------------- /tests/02-get.js: -------------------------------------------------------------------------------- 1 | var setup = require('./setup'), 2 | assert = require('assert'); 3 | 4 | setup.plan(5); // exit at fourth call to setup.end() 5 | 6 | setup(function(err, cb) { 7 | assert(!err, "setup failure"); 8 | 9 | cb.on("error", function (message) { 10 | console.log("ERROR: [" + message + "]"); 11 | process.exit(1); 12 | }); 13 | 14 | var testkey = "02-get.js", testkey2 = "02-get.js2", testkey3 = "02-get.js3"; 15 | 16 | cb.set(testkey, "{bar}", function (err, meta) { 17 | assert(!err, "Failed to store object"); 18 | assert.equal(testkey, meta.id, "Callback called with wrong key!") 19 | 20 | cb.get(testkey, function (err, doc, meta) { 21 | assert.equal(testkey, meta.id, "Callback called with wrong key!") 22 | assert.equal("{bar}", doc, "Callback called with wrong value!") 23 | 24 | cb.set(testkey, "bam", meta, function (err, meta) { 25 | assert(!err, "Failed to set with cas"); 26 | assert.equal(testkey, meta.id, "Callback called with wrong key!"); 27 | cb.get(testkey, function (err, doc, meta) { 28 | assert(!err, "Failed to get"); 29 | assert.equal("bam", doc, "Callback called with wrong value!") 30 | setup.end(); 31 | }) 32 | }) 33 | }) 34 | }); 35 | 36 | cb.set(testkey2, {foo : "bar"}, function (err, meta) { 37 | assert(!err, "Failed to store object"); 38 | assert.equal(testkey2, meta.id, "Callback called with wrong key!") 39 | 40 | cb.get(testkey2, function (err, doc, meta) { 41 | assert.equal(testkey2, meta.id, "Callback called with wrong key!") 42 | assert.equal("bar", doc.foo, "JSON values should be converted back to objects") 43 | setup.end(); 44 | }) 45 | }); 46 | 47 | cb.set(testkey3, [1, "2", true], function (err, meta) { 48 | assert(!err, "Failed to store object"); 49 | assert.equal(testkey3, meta.id, "Callback called with wrong key!") 50 | 51 | cb.get(testkey3, function (err, doc, meta) { 52 | assert.equal(testkey3, meta.id, "Callback called with wrong key!") 53 | assert.equal("2", doc[1], "JSON values should be converted back to objects") 54 | setup.end(); 55 | }) 56 | }); 57 | 58 | var testkey4 = "get-test-4", testJSON4 = ['☆']; 59 | cb.set(testkey4, testJSON4, function (err, meta) { 60 | assert(!err, "Failed to store object"); 61 | assert.equal(testkey4, meta.id, "Callback called with wrong key!") 62 | 63 | cb.get(testkey4, function (err, doc, meta) { 64 | assert.equal(testkey4, meta.id, "Callback called with wrong key!") 65 | // prove the JSON parser isn't the issue 66 | assert.equal(testJSON4[0], JSON.parse(JSON.stringify(testJSON4))[0]) 67 | assert.equal(testJSON4[0], doc[0], "JSON values should be converted back to objects") 68 | setup.end(); 69 | }) 70 | }); 71 | 72 | // test the same without JSON 73 | var testkey5 = "get-test-4", testString5 = '☆'; 74 | cb.set(testkey5, testString5, function (err, meta) { 75 | assert(!err, "Failed to store object"); 76 | assert.equal(testkey5, meta.id, "Callback called with wrong key!") 77 | 78 | cb.get(testkey5, function (err, doc, meta) { 79 | assert.equal(testkey5, meta.id, "Callback called with wrong key!") 80 | assert.equal(testString5, doc, "Unicode characters should round trip.") 81 | setup.end(); 82 | }) 83 | }); 84 | }) 85 | 86 | -------------------------------------------------------------------------------- /io/plugin-libuv.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | 3 | static int _yolog_initialized = 0; 4 | 5 | static void __attribute__((unused)) 6 | lcb_luv_noop(struct lcb_io_opt_st *iops) 7 | { 8 | (void)iops; 9 | } 10 | 11 | static void 12 | lcb_luv_dtor(struct lcb_io_opt_st *iops) 13 | { 14 | /** 15 | * First, clean up any dangling sockets 16 | */ 17 | int ii; 18 | struct lcb_luv_cookie_st *cookie = IOPS_COOKIE(iops); 19 | uv_loop_t *l = cookie->loop; 20 | 21 | for (ii = 0; ii < cookie->fd_max; ii++) { 22 | if (cookie->socktable[ii]) { 23 | log_iops_warn("Dangling socket structure %p with index %d", 24 | cookie->socktable + ii, 25 | ii); 26 | } 27 | } 28 | 29 | log_iops_debug("Destroying %p", iops); 30 | log_iops_warn("Still have %d timers", cookie->timer_count); 31 | assert (cookie->timer_count == 0); 32 | free (cookie->socktable); 33 | free (cookie); 34 | free (iops); 35 | } 36 | 37 | static void 38 | invoke_startstop_callback(struct lcb_luv_cookie_st *cookie, 39 | lcb_luv_start_cb_t cb) 40 | { 41 | if (cb) { 42 | cb(cookie); 43 | } 44 | } 45 | 46 | static void 47 | invoke_start_callback(struct lcb_io_opt_st *iops) 48 | { 49 | log_iops_debug("Start event loop.."); 50 | invoke_startstop_callback(IOPS_COOKIE(iops), IOPS_COOKIE(iops)->start_callback); 51 | } 52 | 53 | static void 54 | invoke_stop_callback(struct lcb_io_opt_st *iops) 55 | { 56 | invoke_startstop_callback(IOPS_COOKIE(iops), IOPS_COOKIE(iops)->stop_callback); 57 | } 58 | 59 | static void sync_loop_run(struct lcb_io_opt_st *iops) 60 | { 61 | log_iops_info("=== LOOP: run ==="); 62 | IOPS_COOKIE(iops)->do_stop = 0; 63 | /** node's libuv does not export uv_run_once */ 64 | #ifdef LCB_LUV_VANILLA 65 | while (IOPS_COOKIE(iops)->do_stop == 0) { 66 | uv_run_once(IOPS_COOKIE(iops)->loop); 67 | } 68 | #endif /* LCB_LUV_NODEJS */ 69 | } 70 | 71 | static void sync_loop_stop(struct lcb_io_opt_st *iops) 72 | { 73 | log_iops_info("=== LOOP: stop ==="); 74 | IOPS_COOKIE(iops)->do_stop = 1; 75 | } 76 | 77 | struct lcb_io_opt_st * 78 | lcb_luv_create_io_opts(uv_loop_t *loop, uint16_t sock_max) 79 | { 80 | struct lcb_io_opt_st *ret = calloc(1, sizeof(*ret)); 81 | struct lcb_luv_cookie_st *cookie = calloc(1, sizeof(*cookie)); 82 | 83 | if (!_yolog_initialized) { 84 | lcb_luv_yolog_init(NULL); 85 | _yolog_initialized = 1; 86 | } 87 | 88 | assert(loop); 89 | cookie->loop = loop; 90 | assert(loop); 91 | 92 | cookie->socktable = calloc(sock_max, sizeof(struct lcb_luv_socket_st*)); 93 | cookie->fd_max = sock_max; 94 | cookie->fd_next = 0; 95 | ret->v.v0.cookie = cookie; 96 | 97 | /* socket.c */ 98 | ret->v.v0.connect = lcb_luv_connect; 99 | ret->v.v0.socket = lcb_luv_socket; 100 | ret->v.v0.close = lcb_luv_close; 101 | 102 | ret->v.v0.create_event = lcb_luv_create_event; 103 | ret->v.v0.update_event = lcb_luv_update_event; 104 | ret->v.v0.delete_event = lcb_luv_delete_event; 105 | ret->v.v0.destroy_event = lcb_luv_destroy_event; 106 | 107 | /* read.c */ 108 | ret->v.v0.recv = lcb_luv_recv; 109 | ret->v.v0.recvv = lcb_luv_recvv; 110 | 111 | /* write.c */ 112 | ret->v.v0.send = lcb_luv_send; 113 | ret->v.v0.sendv = lcb_luv_sendv; 114 | 115 | /* timer.c */ 116 | ret->v.v0.create_timer = lcb_luv_create_timer; 117 | ret->v.v0.delete_timer = lcb_luv_delete_timer; 118 | ret->v.v0.update_timer = lcb_luv_update_timer; 119 | ret->v.v0.destroy_timer = lcb_luv_destroy_timer; 120 | 121 | /* noops */ 122 | ret->v.v0.run_event_loop = invoke_start_callback; 123 | ret->v.v0.stop_event_loop = invoke_stop_callback; 124 | 125 | ret->v.v0.destructor = lcb_luv_dtor; 126 | 127 | return ret; 128 | } 129 | -------------------------------------------------------------------------------- /src/args.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef COUCHNODE_ARGS_H 3 | #define COUCHNODE_ARGS_H 1 4 | #ifndef COUCHBASE_H 5 | #error "Include couchbase.h before including this file" 6 | #endif 7 | 8 | namespace Couchnode 9 | { 10 | class CommonArgs 11 | { 12 | public: 13 | 14 | CommonArgs(const v8::Arguments &, int pmax = 0, int reqmax = 0); 15 | virtual ~CommonArgs(); 16 | 17 | virtual bool parse(); 18 | 19 | virtual CouchbaseCookie *makeCookie(); 20 | 21 | virtual bool extractKey(); 22 | 23 | bool extractUdata(); 24 | void extractExpiry(const v8::Handle &, time_t *); 25 | void extractCas(const v8::Handle &, lcb_cas_t *); 26 | 27 | void getParam(int aix, int dcix, v8::Handle *vp) { 28 | if (use_dictparams) { 29 | if (dict.IsEmpty() == false) { 30 | *vp = dict->Get(NameMap::names[dcix]); 31 | } 32 | } else if (args.Length() >= aix - 1) { 33 | *vp = args[aix]; 34 | } 35 | } 36 | 37 | virtual void bailout(CouchbaseCookie *, lcb_error_t); 38 | 39 | void invalidate() { 40 | stale = true; 41 | } 42 | 43 | // Hooks for deep-copy post assignment.. (manual step).. 44 | virtual void sync(const CommonArgs &) { } 45 | 46 | const v8::Arguments &args; 47 | v8::Handle excerr; 48 | v8::Local ucb; 49 | v8::Local udata; 50 | char *key; 51 | size_t nkey; 52 | 53 | // last index for operation-specific parameters, this is the length 54 | // of all arguments minus the key (at the beginning) and callback data 55 | // (at the end) 56 | int params_max; 57 | int required_max; 58 | bool use_dictparams; 59 | v8::Local dict; 60 | bool stale; 61 | 62 | }; 63 | 64 | class StorageArgs : public CommonArgs 65 | { 66 | public: 67 | StorageArgs(const v8::Arguments &, int nvparams = 0); 68 | virtual ~StorageArgs(); 69 | 70 | virtual bool parse(); 71 | virtual bool extractValue(); 72 | 73 | char *data; 74 | size_t ndata; 75 | time_t exp; 76 | uint64_t cas; 77 | lcb_storage_t storop; 78 | }; 79 | 80 | class MGetArgs : public CommonArgs 81 | { 82 | public: 83 | MGetArgs(const v8::Arguments &, int nkparams = 1); 84 | virtual ~MGetArgs(); 85 | 86 | virtual CouchbaseCookie *makeCookie() { 87 | return new CouchbaseCookie(args.This(), ucb, udata, kcount); 88 | } 89 | 90 | virtual void bailout(CouchbaseCookie *, lcb_error_t); 91 | 92 | virtual void sync(const MGetArgs &other) { 93 | if (other.keys == &other.key) { 94 | keys = &key; 95 | } 96 | if (other.sizes == &other.nkey) { 97 | sizes = &nkey; 98 | } 99 | if (other.exps == &other.single_exp) { 100 | exps = &single_exp; 101 | } 102 | } 103 | 104 | size_t kcount; 105 | time_t single_exp; 106 | 107 | char **keys; 108 | size_t *sizes; 109 | time_t *exps; 110 | 111 | virtual bool extractKey(); 112 | }; 113 | 114 | class KeyopArgs : public CommonArgs 115 | { 116 | 117 | public: 118 | KeyopArgs(const v8::Arguments &); 119 | virtual bool parse(); 120 | uint64_t cas; 121 | }; 122 | 123 | class ArithmeticArgs : public StorageArgs 124 | { 125 | public: 126 | ArithmeticArgs(const v8::Arguments &); 127 | 128 | virtual bool extractValue(); 129 | 130 | int64_t delta; 131 | uint64_t initial; 132 | bool create; 133 | }; 134 | 135 | } // namespace Couchnode 136 | #endif // COUCHNODE_ARGS_H 137 | -------------------------------------------------------------------------------- /io/socket.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | 3 | 4 | 5 | lcb_socket_t 6 | lcb_luv_socket(struct lcb_io_opt_st *iops, 7 | int domain, 8 | int type, 9 | int protocol) 10 | { 11 | lcb_luv_socket_t newsock; 12 | iops->v.v0.error = EINVAL; 13 | 14 | if ( (domain != AF_INET && domain != AF_INET6) || 15 | type != SOCK_STREAM || protocol != IPPROTO_TCP) { 16 | log_socket_error("Bad arguments: domain=%d, type=%d, protocol=%d", 17 | domain, type, protocol); 18 | return -1; 19 | } 20 | 21 | newsock = lcb_luv_socket_new(iops); 22 | if (newsock == NULL) { 23 | return -1; 24 | } 25 | 26 | return newsock->idx; 27 | } 28 | 29 | static void 30 | connect_cb(uv_connect_t* req, int status) 31 | { 32 | lcb_luv_socket_t sock = (lcb_luv_socket_t)req->handle; 33 | struct lcb_luv_evstate_st *evstate = sock->evstate + LCB_LUV_EV_CONNECT; 34 | evstate->flags |= LCB_LUV_EVf_PENDING; 35 | log_socket_debug("Connection callback: status=%d", status); 36 | 37 | if (status) { 38 | /* Error */ 39 | evstate->err = 40 | lcb_luv_errno_map((uv_last_error(sock->parent->loop)).code); 41 | } else { 42 | evstate->err = 0; 43 | } 44 | 45 | /* Since this is a write event on a socket, see if we should send the 46 | * callback 47 | */ 48 | if (sock->event && (sock->event->lcb_events & LCB_WRITE_EVENT)) { 49 | log_socket_debug("Invoking libcouchbase write callback..."); 50 | sock->event->lcb_cb(sock->idx, LCB_WRITE_EVENT, sock->event->lcb_arg); 51 | } 52 | 53 | lcb_luv_socket_unref(sock); 54 | } 55 | 56 | int 57 | lcb_luv_connect(struct lcb_io_opt_st *iops, 58 | lcb_socket_t sock_i, 59 | const struct sockaddr *saddr, 60 | unsigned int saddr_len) 61 | { 62 | int status, retval; 63 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 64 | struct lcb_luv_evstate_st *evstate; 65 | 66 | if (sock == NULL) { 67 | iops->v.v0.error = EBADF; 68 | return -1; 69 | } 70 | 71 | 72 | evstate = sock->evstate + LCB_LUV_EV_CONNECT; 73 | 74 | /* Subsequent calls to connect() */ 75 | if (EVSTATE_IS(evstate, ACTIVE)) { 76 | log_socket_trace("We were called again for connect()"); 77 | if (EVSTATE_IS(evstate, PENDING)) { 78 | retval = evstate->err; 79 | if (retval) { 80 | iops->v.v0.error = retval; 81 | retval = -1; 82 | } else { 83 | evstate->flags |= LCB_LUV_EVf_CONNECTED; 84 | iops->v.v0.error = 0; 85 | } 86 | evstate->flags &= ~(LCB_LUV_EVf_PENDING); 87 | } else { 88 | retval = -1; 89 | if (EVSTATE_IS(evstate, CONNECTED)) { 90 | iops->v.v0.error = EISCONN; 91 | } else { 92 | iops->v.v0.error = EINPROGRESS; 93 | } 94 | } 95 | log_socket_trace("Returning %d for status", retval); 96 | return retval; 97 | } 98 | 99 | retval = -1; 100 | /* Else, first call to connect() */ 101 | if (saddr_len == sizeof(struct sockaddr_in)) { 102 | status = uv_tcp_connect(&sock->u_req.connect, &sock->tcp, 103 | *(struct sockaddr_in*)saddr, connect_cb); 104 | } else if (saddr_len == sizeof(struct sockaddr_in6)) { 105 | status = uv_tcp_connect6(&sock->u_req.connect, &sock->tcp, 106 | *(struct sockaddr_in6*)saddr, connect_cb); 107 | } else { 108 | /* Neither AF_INET or AF_INET6 */ 109 | iops->v.v0.error = EAFNOSUPPORT; 110 | return -1; 111 | } 112 | 113 | lcb_luv_socket_ref(sock); 114 | 115 | if (status == 0) { 116 | iops->v.v0.error = EINPROGRESS; 117 | evstate->flags |= LCB_LUV_EVf_ACTIVE; 118 | 119 | } else { 120 | iops->v.v0.error = 121 | lcb_luv_errno_map( 122 | (uv_last_error(IOPS_COOKIE(iops)->loop)).code); 123 | } 124 | return retval; 125 | } 126 | 127 | void 128 | lcb_luv_close(struct lcb_io_opt_st *iops, lcb_socket_t sock_i) 129 | { 130 | /* Free a socket */ 131 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 132 | if (!sock) { 133 | log_socket_crit("Attempt to close already-closed socket. Abort"); 134 | abort(); 135 | iops->v.v0.error = EBADF; 136 | return; 137 | } 138 | 139 | lcb_luv_socket_deinit(sock); 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | couchnode - node.js access to libcouchbase 2 | ========================================== 3 | 4 | This library allows you to connect to a Couchbase cluster from node.js 5 | 6 | Basic installation and usage 7 | -------------------- 8 | 9 | To install this module, we'll assume you are using [NPM](https://npmjs.org). 10 | However it is not as simple as a regular JavaScript module, 11 | as it depends on the C-library for Couchbase clients, 12 | [libcouchbase](https://github.com/couchbase/libcouchbase). Libcouchbase 13 | also powers other dynamic language clients, such as Ruby and PHP, so 14 | if you've worked with those clients you should feel right at home. 15 | 16 | First step is to install libcouchbase (the 2.0 beta verion). On a Mac 17 | with homebrew this should be as easy as running: 18 | 19 | brew install https://github.com/couchbase/homebrew/raw/preview/Library/Formula/libcouchbase.rb 20 | 21 | Once you have libcouchbase installed, you can proceed to install the 22 | `couchbase` module by running: 23 | 24 | npm install couchbase 25 | 26 | Do note that this module requires the very latest version of libcouchbase, 27 | so if you see errors like `error: ‘struct lcb_io_opt_st’ has no member named ‘v’`, you may have to install libcouchbase from 28 | source until we cut another release. 29 | 30 | For API illustration, the best bet at the current time is [a small example http hit counter](https://github.com/couchbase/couchnode/tree/master/example.js). There is also [the test suite which shows more details.](https://github.com/couchbase/couchnode/tree/master/tests) 31 | 32 | Contributing changes 33 | -------------------- 34 | 35 | We've decided to use "gerrit" for our code review system, making it 36 | easier for all of us to contribute with code and comments. You'll 37 | find our gerrit server running at: 38 | 39 | http://review.couchbase.org/ 40 | 41 | The first thing you would do would be to "sign up" for an account 42 | there, before you jump to: 43 | 44 | http://review.couchbase.org/#/q/status:open+project:couchnode,n,z 45 | 46 | With that in place you should probably jump on IRC and join the #libcouchbase 47 | chatroom to hang out with the rest of us :-) 48 | 49 | There is a pretty tight relationship between our node.js driver 50 | development and the development of libcouchbase, so for now you have 51 | to run the latest versions f both projects to be sure that everything 52 | works. I have however made it easy for you to develop on couchstore by 53 | using a repo manifest file. If you haven't done so already you should 54 | download repo from http://code.google.com/p/git-repo/downloads/list 55 | and put it in your path. 56 | 57 | All you should need to set up your development environment should be: 58 | 59 | trond@ok ~> mkdir sdk 60 | trond@ok ~> cd sdk 61 | trond@ok ~/sdk> repo init -u git://github.com/trondn/manifests.git -m sdk.xml 62 | trond@ok ~/sdk> repo sync 63 | trond@ok ~/sdk> repo start my-branch-name --all 64 | trond@ok ~/sdk> gmake nodejs 65 | 66 | This will build the latest version of libcouchbase and the couchnode 67 | driver. You must have a C and C++ compiler installed, automake, 68 | autoconf, node and v8. 69 | 70 | If you have to make any changes in libcouchbase or couchnode, 71 | all you can just commit them before you upload them to gerrit with the 72 | following command: 73 | 74 | trond@ok ~/sdk> repo upload 75 | 76 | You might experience a problem trying to upload the patches if you've 77 | selected a different login name review.couchbase.org than your login 78 | name. Don't worry, all you need to do is to add the following to your 79 | ~/.gitconfig file: 80 | 81 | [review "review.couchbase.org"] 82 | username = trond 83 | 84 | I normally don't go looking for stuff in gerrit, so you should add at 85 | least me (trond.norbye@gmail.com) as a reviewer for your patch (and 86 | I'll know who else to add and add them for you). 87 | 88 | Install description 89 | ------------------- 90 | 91 | You would need to have libcouchbase installed _before_ you can build 92 | the node extension. If you have installed libcouchbase in a location 93 | that isn't part of the default search path for your compiler, linker 94 | and runtime linker, you have to set the appropriate flags. ex: 95 | 96 | CPPFLAGS="-I/opt/local/include" LDFLAGS="-L/opt/local/lib -Wl,-rpath,/opt/local/lib" node-waf configure 97 | 98 | To build and install the module, simply execute: 99 | 100 | trond@ok> node-waf configure build install 101 | 102 | API description 103 | --------------- 104 | 105 | Unfortunately this isn't documented yet, but you should be able to 106 | get a pretty good idea on how it works from looking at the tests :) 107 | -------------------------------------------------------------------------------- /lib/bucket.js: -------------------------------------------------------------------------------- 1 | var request = require("request").defaults({json:true}), 2 | qs = require('querystring'); 3 | 4 | exports.create = function(connection, config, ready) { 5 | // We aren't using prototype inheritance here for two reasons: 6 | // 1) even though they may be faster, this constructor won't be called frequently 7 | // 2) doing it this way means we get better inspectability in the repl, etc. 8 | 9 | function on(event, callback) { 10 | connection.on(event, callback); 11 | }; 12 | 13 | function get(key, callback) { 14 | requiredArgs(key, callback); 15 | if (key instanceof Array) { 16 | for (i = 0; i < key.length; i++) { 17 | connection.get(key[i], undefined, getHandler, callback); 18 | } 19 | } else { 20 | connection.get(key, undefined, getHandler, callback); 21 | } 22 | }; 23 | 24 | function set(key, doc, meta, callback) { 25 | if (meta instanceof Function) { 26 | callback = meta; 27 | meta = {}; 28 | } 29 | requiredArgs(key, doc, callback); 30 | connection.set(key, makeDoc(doc), meta.flags, meta.cas, setHandler, callback); 31 | }; 32 | 33 | function _delete(key, meta, callback) { 34 | if (meta instanceof Function) { 35 | callback = meta; 36 | meta = {}; 37 | } 38 | requiredArgs(key, callback); 39 | connection.delete(key, meta.cas, deleteHandler, callback); 40 | } 41 | 42 | function replace(key, doc, meta, callback) { 43 | if (meta instanceof Function) { 44 | callback = meta; 45 | meta = {}; 46 | } 47 | requiredArgs(key, doc, callback); 48 | connection.replace(key, doc, meta.flags, meta.cas, setHandler, callback); 49 | }; 50 | 51 | function add(key, doc, meta, callback) { 52 | if (meta instanceof Function) { 53 | callback = meta; 54 | meta = {}; 55 | } 56 | requiredArgs(key, doc, callback); 57 | connection.add(key, doc, meta.flags, meta.cas, setHandler, callback); 58 | }; 59 | 60 | var viewHosts; 61 | 62 | // todo this function should be triggered on topology change 63 | function updateClusterMap(callback) { 64 | var uiHost = connection.getRestUri(); 65 | request("http://"+uiHost+"/pools/"+encodeURIComponent(config.bucket), 66 | function(err, resp, clusterMap) { 67 | if (err) {throw(err);} 68 | viewHosts = clusterMap.nodes.map(function(info) { 69 | return info.couchApiBase; 70 | }); 71 | if (callback) { 72 | callback(); 73 | } 74 | }); 75 | }; 76 | 77 | function view(ddoc, name, query, callback) { 78 | var jsonFields = ["key", "startkey", "endkey", "start_key", "end_key"], 79 | // distribute queries across the cluster randomly 80 | viewHost = viewHosts[Math.floor(Math.random() * viewHosts.length)]; 81 | 82 | for (q in query) { 83 | if (jsonFields.indexOf(q) != -1) { 84 | query[q] = JSON.stringify(query[q]); 85 | } 86 | } 87 | 88 | var url = viewHost + 89 | [config.bucket, "_design", ddoc, 90 | "_view", name].map(encodeURIComponent).join('/') + 91 | '?' + qs.stringify(query); 92 | 93 | return request(url, callback); 94 | }; 95 | 96 | updateClusterMap(function() { 97 | ready({ 98 | on : on, 99 | get : get, 100 | set : set, 101 | 'delete' : _delete, 102 | replace : replace, 103 | add : add, 104 | view : view 105 | }); 106 | }); 107 | }; 108 | 109 | function requiredArgs() { 110 | for (var i = 0; i < arguments.length; i++) { 111 | if (typeof arguments[i] == 'undefined') { 112 | throw new ReferenceError("missing required argument") 113 | } 114 | }; 115 | }; 116 | 117 | function makeDoc(doc) { 118 | if (typeof doc == "string") { 119 | return doc; 120 | } else { 121 | return JSON.stringify(doc) 122 | } 123 | }; 124 | 125 | // convert the c-based set callback format to something sane 126 | function setHandler(data, error, key, cas) { 127 | data(error, {id : key, cas : cas}); 128 | } 129 | 130 | function getHandler(data, error, key, cas, flags, value) { 131 | // if it looks like it might be JSON, try to parse it 132 | if (/[\{\[]/.test(value)) { 133 | try { 134 | value = JSON.parse(value) 135 | } catch (e) { 136 | // console.log("JSON.parse error", e, value) 137 | } 138 | } 139 | data(error, value, {id : key, cas: cas, flags : flags}); 140 | }; 141 | 142 | function deleteHandler(data, error, key) { 143 | data(error, {id : key}); 144 | } 145 | -------------------------------------------------------------------------------- /io/write.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | 3 | /* Writing is a bit more complex than reading, since an event-based write 4 | * mechanism needs us to let it known when it can write. 5 | * In this case we will utilize a uv_prepare_t structure, which will trigger 6 | * the lcb write callback in cases where we have previously pretended that a write 7 | * will not block. 8 | */ 9 | 10 | static void 11 | write_cb(uv_write_t *req, int status) 12 | { 13 | lcb_luv_socket_t sock = (lcb_luv_socket_t)req->data; 14 | struct lcb_luv_evstate_st *evstate; 15 | 16 | if (!sock) { 17 | fprintf(stderr, "Got write callback (req=%p) without socket\n", req); 18 | return; 19 | } 20 | 21 | evstate = EVSTATE_FIND(sock, WRITE); 22 | 23 | if (status) { 24 | evstate->err = 25 | lcb_luv_errno_map((uv_last_error(sock->parent->loop)).code); 26 | } 27 | log_write_debug("Flush done. Flushed %d bytes", sock->write.buf.len); 28 | sock->write.pos = 0; 29 | sock->write.nb = 0; 30 | evstate->flags |= LCB_LUV_EVf_PENDING; 31 | evstate->flags &= ~(LCB_LUV_EVf_FLUSHING); 32 | 33 | if (SOCK_EV_ENABLED(sock, WRITE)) { 34 | sock->event->lcb_cb(sock->idx, LCB_WRITE_EVENT, sock->event->lcb_arg); 35 | 36 | if (sock->write.nb) { 37 | evstate->flags &= ~(LCB_LUV_EVf_PENDING); 38 | lcb_luv_flush(sock); 39 | } 40 | } 41 | 42 | lcb_luv_socket_unref(sock); 43 | } 44 | 45 | 46 | /** 47 | * Flush the write buffers 48 | */ 49 | void 50 | lcb_luv_flush(lcb_luv_socket_t sock) 51 | { 52 | int status; 53 | struct lcb_luv_evstate_st *evstate; 54 | if (sock->write.nb == 0) { 55 | return; 56 | } 57 | 58 | evstate = EVSTATE_FIND(sock, WRITE); 59 | if(EVSTATE_IS(evstate, FLUSHING)) { 60 | log_write_info("Not flushing because we are in the middle of a flush"); 61 | return; 62 | } 63 | 64 | sock->write.buf.base = sock->write.data; 65 | sock->write.buf.len = sock->write.nb; 66 | log_write_debug("Will flush"); 67 | status = uv_write(&sock->u_req.write, 68 | (uv_stream_t*)&sock->tcp, 69 | &sock->write.buf, 1, write_cb); 70 | lcb_luv_socket_ref(sock); 71 | 72 | if (status) { 73 | evstate->err = 74 | lcb_luv_errno_map((uv_last_error(sock->parent->loop)).code); 75 | } 76 | evstate->flags |= LCB_LUV_EVf_FLUSHING; 77 | } 78 | 79 | 80 | static lcb_ssize_t 81 | write_common(lcb_luv_socket_t sock, const void *buf, size_t len, int *errno_out) 82 | { 83 | lcb_ssize_t ret; 84 | struct lcb_luv_evstate_st *evstate = EVSTATE_FIND(sock, WRITE); 85 | 86 | log_write_debug("%d: Requested to write %d bytes from %p", 87 | sock->idx, len, buf); 88 | 89 | if (evstate->err) { 90 | log_write_warn("Socket has pending error %d", evstate->err); 91 | *errno_out = evstate->err; 92 | evstate->err = 0; 93 | return -1; 94 | } 95 | 96 | if (EVSTATE_IS(evstate, FLUSHING)) { 97 | log_write_info("Will not write because we are inside a flush"); 98 | *errno_out = EWOULDBLOCK; 99 | return -1; 100 | } 101 | 102 | ret = MINIMUM(len, sizeof(sock->write.data) - sock->write.nb); 103 | if (ret == 0) { 104 | log_write_info("We have no more space inside the buffer"); 105 | *errno_out = EWOULDBLOCK; 106 | return -1; 107 | } 108 | 109 | 110 | 111 | memcpy(sock->write.data + sock->write.pos, buf, ret); 112 | // lcb_luv_hexdump(sock->write.data + sock->write.pos, ret); 113 | sock->write.pos += ret; 114 | sock->write.nb += ret; 115 | log_write_trace("Returning %d", ret); 116 | return ret; 117 | } 118 | 119 | lcb_ssize_t 120 | lcb_luv_send(struct lcb_io_opt_st *iops, 121 | lcb_socket_t sock_i, 122 | const void *msg, 123 | lcb_size_t len, 124 | int flags) 125 | { 126 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 127 | lcb_ssize_t ret; 128 | if (sock == NULL) { 129 | iops->v.v0.error = EBADF; 130 | return -1; 131 | } 132 | ret = write_common(sock, msg, len, &iops->v.v0.error); 133 | if (ret > 0) { 134 | lcb_luv_schedule_enable(sock); 135 | } 136 | return ret; 137 | } 138 | 139 | lcb_ssize_t 140 | lcb_luv_sendv(struct lcb_io_opt_st *iops, 141 | lcb_socket_t sock_i, 142 | struct lcb_iovec_st *iov, 143 | lcb_size_t niov) 144 | { 145 | lcb_ssize_t nr = 0, iret; 146 | int ii, my_errno = 0; 147 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 148 | if (sock == NULL) { 149 | iops->v.v0.error = EBADF; 150 | return -1; 151 | } 152 | for (ii = 0; ii < niov; ii++) { 153 | 154 | if (iov[ii].iov_len == 0) { 155 | break; 156 | } 157 | 158 | iret = write_common(sock, iov[ii].iov_base, iov[ii].iov_len, &my_errno); 159 | if (iret > 0) { 160 | nr += iret; 161 | } else { 162 | break; 163 | } 164 | } 165 | if (my_errno) { 166 | iops->v.v0.error = my_errno; 167 | } 168 | if (nr > 0) { 169 | lcb_luv_schedule_enable(sock); 170 | return nr; 171 | } else { 172 | return -1; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/couchbase_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #ifndef COUCHBASE_H 18 | #define COUCHBASE_H 1 19 | 20 | #define BUILDING_NODE_EXTENSION 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include "namemap.h" 30 | #include "cookie.h" 31 | #include "args.h" 32 | #include "cas.h" 33 | 34 | namespace Couchnode 35 | { 36 | class QueuedCommand; 37 | 38 | class CouchbaseImpl: public node::ObjectWrap 39 | { 40 | public: 41 | CouchbaseImpl(lcb_t inst); 42 | virtual ~CouchbaseImpl(); 43 | 44 | // Methods called directly from JavaScript 45 | static void Init(v8::Handle target); 46 | static v8::Handle New(const v8::Arguments &args); 47 | static v8::Handle GetVersion(const v8::Arguments &); 48 | static v8::Handle SetTimeout(const v8::Arguments &); 49 | static v8::Handle GetTimeout(const v8::Arguments &); 50 | static v8::Handle GetRestUri(const v8::Arguments &); 51 | static v8::Handle SetSynchronous(const v8::Arguments &); 52 | static v8::Handle IsSynchronous(const v8::Arguments &); 53 | static v8::Handle SetHandler(const v8::Arguments &); 54 | static v8::Handle GetLastError(const v8::Arguments &); 55 | static v8::Handle Get(const v8::Arguments &); 56 | static v8::Handle Set(const v8::Arguments &); 57 | static v8::Handle Add(const v8::Arguments &); 58 | static v8::Handle Replace(const v8::Arguments &); 59 | static v8::Handle Append(const v8::Arguments &); 60 | static v8::Handle Prepend(const v8::Arguments &); 61 | static v8::Handle Arithmetic(const v8::Arguments &); 62 | static v8::Handle Remove(const v8::Arguments &); 63 | static v8::Handle Touch(const v8::Arguments &); 64 | static v8::Handle OpCallStyle(const v8::Arguments &); 65 | // Setting up the event emitter 66 | static v8::Handle On(const v8::Arguments &); 67 | v8::Handle on(const v8::Arguments &); 68 | 69 | // Method called from libcouchbase 70 | void onConnect(lcb_configuration_t config); 71 | bool onTimeout(void); 72 | 73 | void errorCallback(lcb_error_t err, const char *errinfo); 74 | 75 | void scheduleCommand(QueuedCommand &cmd) { 76 | queued_commands.push_back(cmd); 77 | } 78 | 79 | void runScheduledCommands(void); 80 | 81 | void setLastError(lcb_error_t err) { 82 | lastError = err; 83 | } 84 | 85 | lcb_t getLibcouchbaseHandle(void) { 86 | return instance; 87 | } 88 | 89 | bool isConnected(void) const { 90 | return connected; 91 | } 92 | 93 | bool isUsingHashtableParams(void) const { 94 | return useHashtableParams; 95 | } 96 | protected: 97 | bool connected; 98 | bool useHashtableParams; 99 | lcb_t instance; 100 | lcb_error_t lastError; 101 | 102 | typedef std::vector QueuedCommandList; 103 | QueuedCommandList queued_commands; 104 | 105 | typedef std::map > EventMap; 106 | EventMap events; 107 | v8::Persistent connectHandler; 108 | void setupLibcouchbaseCallbacks(void); 109 | 110 | #ifdef COUCHNODE_DEBUG 111 | static unsigned int objectCount; 112 | #endif 113 | }; 114 | 115 | class QueuedCommand 116 | { 117 | public: 118 | typedef lcb_error_t (*operfunc)( 119 | lcb_t, CommonArgs *, CouchbaseCookie *); 120 | 121 | QueuedCommand(CouchbaseCookie *c, CommonArgs *a, operfunc f) : 122 | cookie(c), args(a), ofn(f) { } 123 | 124 | virtual ~QueuedCommand() { } 125 | 126 | void setDone(void) { 127 | delete args; 128 | } 129 | 130 | CouchbaseCookie *cookie; 131 | CommonArgs *args; 132 | operfunc ofn; 133 | }; 134 | 135 | /** 136 | * Base class of the Exceptions thrown by the internals of 137 | * Couchnode 138 | */ 139 | class Exception 140 | { 141 | public: 142 | Exception(const char *msg) : message(msg) { 143 | /* Empty */ 144 | } 145 | 146 | Exception(const char *msg, const v8::Handle &at) 147 | : message(msg) { 148 | v8::String::AsciiValue valstr(at); 149 | if (*valstr) { 150 | message += " at '"; 151 | message += *valstr; 152 | message += "'"; 153 | } 154 | } 155 | 156 | virtual ~Exception() { 157 | // Empty 158 | } 159 | 160 | virtual const std::string &getMessage() const { 161 | return message; 162 | } 163 | 164 | protected: 165 | std::string message; 166 | }; 167 | 168 | v8::Handle ThrowException(const char *str); 169 | v8::Handle ThrowIllegalArgumentsException(void); 170 | } // namespace Couchnode 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /io/read.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | 3 | uv_buf_t 4 | alloc_cb(uv_handle_t *handle, size_t suggested_size) 5 | { 6 | lcb_luv_socket_t sock = (lcb_luv_socket_t)handle; 7 | return sock->read.buf; 8 | } 9 | 10 | void 11 | read_cb(uv_stream_t *stream, ssize_t nread, uv_buf_t buf) 12 | { 13 | /* This is the same buffer structure we had before */ 14 | lcb_luv_socket_t sock = (lcb_luv_socket_t)stream; 15 | int is_stopped = 0; 16 | lcb_luv_socket_ref(sock); 17 | log_read_debug("%d: nr=%d, len=%d", sock->idx, nread, buf.len); 18 | 19 | if (nread == -1) { 20 | uv_err_t last_err = uv_last_error(sock->parent->loop); 21 | if (last_err.code == UV_EOF) { 22 | sock->eof = 1; 23 | } else { 24 | sock->evstate[LCB_LUV_EV_READ].err = 25 | lcb_luv_errno_map(last_err.code); 26 | } 27 | lcb_luv_read_stop(sock); 28 | } else if (nread) { 29 | sock->read.buf.len -= nread; 30 | sock->read.buf.base += (size_t)nread; 31 | sock->read.nb += nread; 32 | 33 | /* We don't have any more space */ 34 | if (!sock->read.buf.len) { 35 | lcb_luv_read_stop(sock); 36 | } 37 | } else { 38 | /* nread == 0 */ 39 | goto GT_RET; 40 | } 41 | 42 | sock->evstate[LCB_LUV_EV_READ].flags |= LCB_LUV_EVf_PENDING; 43 | if (sock->event && (sock->event->lcb_events & LCB_READ_EVENT)) { 44 | sock->event->lcb_cb(sock->idx, LCB_READ_EVENT, 45 | sock->event->lcb_arg); 46 | } 47 | 48 | GT_RET: 49 | if (is_stopped) { 50 | lcb_luv_socket_unref(sock); 51 | } 52 | lcb_luv_socket_unref(sock); 53 | } 54 | 55 | void 56 | lcb_luv_read_nudge(lcb_luv_socket_t sock) 57 | { 58 | int status; 59 | if (sock->read.readhead_active) { 60 | log_read_trace("Read-ahead already active"); 61 | return; /* nothing to do here */ 62 | } 63 | 64 | status = uv_read_start((uv_stream_t*)&sock->tcp, alloc_cb, read_cb); 65 | 66 | if (status) { 67 | sock->evstate[LCB_LUV_EV_READ].err = 68 | lcb_luv_errno_map( 69 | (uv_last_error(sock->parent->loop)).code); 70 | log_read_error("Couldn't start read: %d", 71 | sock->evstate[LCB_LUV_EV_READ].err); 72 | } else { 73 | log_read_debug("read-ahead initialized"); 74 | sock->read.buf.len = LCB_LUV_READAHEAD; 75 | sock->read.buf.base = sock->read.data; 76 | lcb_luv_socket_ref(sock); 77 | sock->read.readhead_active = 1; 78 | } 79 | } 80 | 81 | void 82 | lcb_luv_read_stop(lcb_luv_socket_t sock) 83 | { 84 | if (sock->read.readhead_active == 0) { 85 | return; 86 | } 87 | uv_read_stop((uv_stream_t*)&sock->tcp); 88 | sock->read.readhead_active = 0; 89 | lcb_luv_socket_unref(sock); 90 | } 91 | 92 | 93 | static lcb_ssize_t 94 | read_common(lcb_luv_socket_t sock, void *buffer, lcb_size_t len, 95 | int *errno_out) 96 | { 97 | struct lcb_luv_evstate_st *evstate = sock->evstate + LCB_LUV_EV_READ; 98 | lcb_ssize_t ret; 99 | size_t read_offset, toRead; 100 | 101 | log_read_debug("%d: Requested to read %d bytes. have %d", 102 | sock->idx, len, sock->read.nb); 103 | 104 | 105 | /* basic error checking */ 106 | if (evstate->err) { 107 | *errno_out = evstate->err; 108 | evstate->err = 0; 109 | return -1; 110 | } 111 | 112 | if (sock->eof) { 113 | return 0; 114 | } 115 | 116 | /* Check how much data we can send back, and where do we read from */ 117 | toRead = MINIMUM(len, sock->read.nb); 118 | read_offset = sock->read.pos; 119 | 120 | /* copy the data */ 121 | if (toRead) { 122 | memcpy(buffer, sock->read.data + read_offset, toRead); 123 | ret = toRead; 124 | *errno_out = 0; 125 | } else { 126 | *errno_out = EWOULDBLOCK; 127 | ret = -1; 128 | } 129 | 130 | /** 131 | * Buffer positioning is somewhat complicated. If we are in middle of a partial 132 | * read (readahead is active), then the next bytes will still happen from within 133 | * the position of our current buffer, so we want to maintain our position. 134 | * 135 | * On the other hand, if readahead is not active, then the next read will begin 136 | * from the beginning of the buffer 137 | */ 138 | sock->read.nb -= toRead; 139 | sock->read.pos += toRead; 140 | 141 | if (sock->read.nb == 0 && sock->read.readhead_active == 0) { 142 | sock->read.pos = 0; 143 | } 144 | 145 | 146 | if (toRead < len) { 147 | evstate->flags &= ~(LCB_LUV_EVf_PENDING); 148 | lcb_luv_read_nudge(sock); 149 | } 150 | 151 | return ret; 152 | } 153 | 154 | lcb_ssize_t 155 | lcb_luv_recv(struct lcb_io_opt_st *iops, 156 | lcb_socket_t sock_i, 157 | void *buffer, 158 | lcb_size_t len, 159 | int flags) 160 | { 161 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 162 | if (sock == NULL) { 163 | iops->v.v0.error = EBADF; 164 | return -1; 165 | } 166 | 167 | return read_common(sock, buffer, len, &iops->v.v0.error); 168 | } 169 | 170 | lcb_ssize_t 171 | lcb_luv_recvv(struct lcb_io_opt_st *iops, 172 | lcb_socket_t sock_i, 173 | struct lcb_iovec_st *iov, 174 | lcb_size_t niov) 175 | { 176 | lcb_ssize_t nr = 0, iret = -1; 177 | int ii, my_errno; 178 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 179 | 180 | if (sock == NULL) { 181 | iops->v.v0.error = EBADF; 182 | return -1; 183 | } 184 | 185 | for (ii = 0; ii < niov; ii++) { 186 | if (iov[ii].iov_len == 0) { 187 | break; 188 | } 189 | iret = read_common(sock, iov[ii].iov_base, iov[ii].iov_len, &my_errno); 190 | if (iret > 0) { 191 | nr += iret; 192 | } else { 193 | break; 194 | } 195 | } 196 | if (!nr) { 197 | iops->v.v0.error = my_errno; 198 | return -1; 199 | } else { 200 | return nr; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /io/lcb_luv_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef LCB_LUV_INTERNAL_H_ 2 | #define LCB_LUV_INTERNAL_H_ 3 | 4 | #ifndef COUCHNODE_DEBUG 5 | #define LCB_LUV_YOLOG_DEBUG_LEVEL 100 6 | #endif 7 | 8 | 9 | #include "libcouchbase-libuv.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "util/lcb_luv_yolog.h" 16 | 17 | #define EVSTATE_IS(p, b) \ 18 | ( ( (p)->flags ) & LCB_LUV_EVf_ ## b) 19 | 20 | #define EVSTATE_FIND(sock, evtype) \ 21 | (sock->evstate + LCB_LUV_EV_ ## evtype) 22 | 23 | #define SOCK_EV_ENABLED(sock, evtype) \ 24 | (sock->event && (sock->event->lcb_events & LCB_ ## evtype ## _EVENT)) 25 | 26 | #define IOPS_COOKIE(iops) \ 27 | ((struct lcb_luv_cookie_st*)(iops->v.v0.cookie)) 28 | 29 | #define MINIMUM(a,b) \ 30 | ((a < b) ? a : b) 31 | 32 | /** 33 | * Fields representing various events 34 | */ 35 | enum { 36 | LCB_LUV_EV_READ = 0, 37 | LCB_LUV_EV_WRITE = 1, 38 | 39 | LCB_LUV_EV_RDWR_MAX = 2, 40 | 41 | LCB_LUV_EV_CONNECT = 2, 42 | LCB_LUV_EV_MAX 43 | }; 44 | 45 | 46 | /** 47 | * Structure representing an event 48 | */ 49 | struct lcb_luv_event_st { 50 | /* socket */ 51 | lcb_luv_socket_t handle; 52 | 53 | /* libcouchbase function to be invoked */ 54 | lcb_luv_callback_t lcb_cb; 55 | /* argument to pass to callback */ 56 | void *lcb_arg; 57 | 58 | /* which events to monitor */ 59 | short lcb_events; 60 | }; 61 | 62 | typedef enum { 63 | LCB_LUV_EVf_CONNECTED = 1 << 0, 64 | LCB_LUV_EVf_ACTIVE = 1 << 1, 65 | 66 | LCB_LUV_EVf_PENDING = 1 << 2, 67 | LCB_LUV_EVf_FLUSHING = 1 << 3, 68 | } lcb_luv_evstate_flags_t; 69 | 70 | 71 | 72 | 73 | struct lcb_luv_evstate_st { 74 | lcb_luv_evstate_flags_t flags; 75 | /* Recorded errno */ 76 | int err; 77 | }; 78 | 79 | /** 80 | * Structure representing a TCP network connection. 81 | */ 82 | struct lcb_luv_socket_st { 83 | /* Should be first */ 84 | uv_tcp_t tcp; 85 | 86 | /* Union for our requests */ 87 | union uv_any_req u_req; 88 | 89 | /* Index into the 'fd' table */ 90 | long idx; 91 | 92 | int eof; 93 | 94 | uv_prepare_t prep; 95 | int prep_active; 96 | 97 | unsigned long refcount; 98 | 99 | struct { 100 | /* Readahead buffer*/ 101 | uv_buf_t buf; 102 | char data[LCB_LUV_READAHEAD]; 103 | size_t pos; 104 | size_t nb; 105 | int readhead_active; 106 | } read; 107 | 108 | struct { 109 | uv_buf_t buf; 110 | /* how much data does our buffer have */ 111 | char data[LCB_LUV_WRITEBUFSZ]; 112 | size_t pos; 113 | size_t nb; 114 | } write; 115 | 116 | 117 | 118 | /* various information on different operations */ 119 | struct lcb_luv_evstate_st evstate[LCB_LUV_EV_MAX]; 120 | 121 | /* Pointer to libcouchbase opaque event, if any */ 122 | struct lcb_luv_event_st *event; 123 | 124 | /* Pointer to our cookie */ 125 | struct lcb_luv_cookie_st *parent; 126 | unsigned handle_count; 127 | }; 128 | 129 | 130 | 131 | void * 132 | lcb_luv_create_event(struct lcb_io_opt_st *iops); 133 | 134 | int 135 | lcb_luv_update_event(struct lcb_io_opt_st *iops, 136 | lcb_socket_t sock_i, 137 | void *event_opaque, 138 | short flags, 139 | void *cb_data, 140 | lcb_luv_callback_t cb); 141 | 142 | void 143 | lcb_luv_delete_event(struct lcb_io_opt_st *iops, 144 | lcb_socket_t sock_i, 145 | void *event_opaque); 146 | 147 | void 148 | lcb_luv_destroy_event(struct lcb_io_opt_st *iops, 149 | void *event_opaque); 150 | 151 | lcb_socket_t 152 | lcb_luv_socket(struct lcb_io_opt_st *iops, 153 | int domain, int type, int protocol); 154 | 155 | int 156 | lcb_luv_connect(struct lcb_io_opt_st *iops, 157 | lcb_socket_t sock_i, 158 | const struct sockaddr *saddr, 159 | unsigned int saddr_len); 160 | 161 | 162 | void 163 | lcb_luv_close(struct lcb_io_opt_st *iops, lcb_socket_t sock_i); 164 | 165 | 166 | /* READ */ 167 | lcb_ssize_t 168 | lcb_luv_recv(struct lcb_io_opt_st *iops, 169 | lcb_socket_t sock_i, 170 | void *buffer, 171 | lcb_size_t len, 172 | int flags); 173 | lcb_ssize_t 174 | lcb_luv_recvv(struct lcb_io_opt_st *iops, 175 | lcb_socket_t sock_i, 176 | struct lcb_iovec_st *iov, 177 | lcb_size_t niov); 178 | 179 | 180 | /* WRITE */ 181 | lcb_ssize_t 182 | lcb_luv_send(struct lcb_io_opt_st *iops, 183 | lcb_socket_t sock_i, 184 | const void *msg, 185 | lcb_size_t len, 186 | int flags); 187 | 188 | lcb_ssize_t 189 | lcb_luv_sendv(struct lcb_io_opt_st *iops, 190 | lcb_socket_t sock_i, 191 | struct lcb_iovec_st *iov, 192 | lcb_size_t niov); 193 | 194 | 195 | /* TIMER */ 196 | void * 197 | lcb_luv_create_timer(struct lcb_io_opt_st *iops); 198 | 199 | int 200 | lcb_luv_update_timer(struct lcb_io_opt_st *iops, 201 | void *timer_opaque, 202 | lcb_uint32_t usecs, 203 | void *cbdata, 204 | lcb_luv_callback_t callback); 205 | 206 | void 207 | lcb_luv_delete_timer(struct lcb_io_opt_st *iops, 208 | void *timer_opaque); 209 | 210 | void 211 | lcb_luv_destroy_timer(struct lcb_io_opt_st *iops, 212 | void *timer_opaque); 213 | 214 | /** 215 | * These are functions private to lcb-luv. They are not iops functions 216 | */ 217 | 218 | /** 219 | * This will allocate a new 'socket'. Returns the new socket, or NULL on error 220 | */ 221 | lcb_luv_socket_t 222 | lcb_luv_socket_new(struct lcb_io_opt_st *iops); 223 | 224 | /** 225 | * This deinitializes a socket. 226 | * The reference count of the new socket is 1 227 | */ 228 | void 229 | lcb_luv_socket_deinit(lcb_luv_socket_t sock); 230 | 231 | /** 232 | * This will decrement the reference count and free the socket if the reference 233 | * count is 0. 234 | * The return value is the reference count. If the return is 0, then it means 235 | * the socket is freed and does not point to valid data. 236 | */ 237 | unsigned long 238 | lcb_luv_socket_unref(lcb_luv_socket_t sock); 239 | 240 | #define lcb_luv_socket_ref(sock) sock->refcount++ 241 | 242 | /** 243 | * Frees the socket 244 | */ 245 | void 246 | lcb_luv_socket_free(lcb_luv_socket_t sock); 247 | 248 | /** 249 | * This will get a socket structure from an index 250 | */ 251 | lcb_luv_socket_t 252 | lcb_luv_sock_from_idx(struct lcb_io_opt_st *iops, lcb_socket_t idx); 253 | 254 | /** 255 | * Will try and let us get read events on this socket 256 | */ 257 | void 258 | lcb_luv_read_nudge(lcb_luv_socket_t sock); 259 | 260 | /** 261 | * Will stop whatever read_nudge started. It will stop trying to readahead 262 | */ 263 | void 264 | lcb_luv_read_stop(lcb_luv_socket_t sock); 265 | 266 | /** 267 | * Will enable our per-iteration callback, unless already enabled 268 | */ 269 | void 270 | lcb_luv_schedule_enable(lcb_luv_socket_t sock); 271 | 272 | /** 273 | * Will flush data to libuv. This is called when the event loop has control 274 | */ 275 | void 276 | lcb_luv_flush(lcb_luv_socket_t sock); 277 | 278 | void 279 | lcb_luv_hexdump(void* data, int size); 280 | 281 | int 282 | lcb_luv_errno_map(int uverr); 283 | 284 | #endif /* LCB_LUV_INTERNAL_H_ */ 285 | -------------------------------------------------------------------------------- /src/operations.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #include "couchbase_impl.h" 18 | using namespace Couchnode; 19 | 20 | /** 21 | * These are simplistic functions which each take a CommonArgs object 22 | * and unpack it into the appropriate libcouchbase entry point. 23 | */ 24 | 25 | static lcb_error_t doGet(lcb_t instance, 26 | CommonArgs *args, 27 | CouchbaseCookie *cookie) 28 | { 29 | MGetArgs *cargs = static_cast(args); 30 | 31 | // @todo move this over to the MGetArgs structs 32 | lcb_get_cmd_t **commands = new lcb_get_cmd_t*[cargs->kcount]; 33 | for (size_t ii = 0; ii < cargs->kcount; ++ii) { 34 | if (cargs->exps == NULL) { 35 | commands[ii] = new lcb_get_cmd_t(cargs->keys[ii], 36 | cargs->sizes[ii]); 37 | } else { 38 | // @todo how do the single thing work? 39 | commands[ii] = new lcb_get_cmd_t(cargs->keys[ii], 40 | cargs->sizes[ii], 41 | cargs->exps[ii]); 42 | } 43 | } 44 | 45 | lcb_error_t ret = lcb_get(instance, cookie, cargs->kcount, commands); 46 | for (size_t ii = 0; ii < cargs->kcount; ++ii) { 47 | delete commands[ii]; 48 | } 49 | delete []commands; 50 | 51 | return ret; 52 | } 53 | 54 | static lcb_error_t doSet(lcb_t instance, 55 | CommonArgs *args, 56 | CouchbaseCookie *cookie) 57 | { 58 | StorageArgs *cargs = static_cast(args); 59 | 60 | lcb_store_cmd_t cmd(cargs->storop, cargs->key, cargs->nkey, 61 | cargs->data, cargs->ndata, 0, cargs->exp, 62 | cargs->cas); 63 | lcb_store_cmd_t *commands[] = { &cmd }; 64 | return lcb_store(instance, cookie, 1, commands); 65 | } 66 | 67 | static lcb_error_t doTouch(lcb_t instance, 68 | CommonArgs *args, 69 | CouchbaseCookie *cookie) 70 | { 71 | MGetArgs *cargs = static_cast(args); 72 | 73 | 74 | // @todo move this over to the MGetArgs structs 75 | lcb_touch_cmd_t **commands = new lcb_touch_cmd_t*[cargs->kcount]; 76 | for (size_t ii = 0; ii < cargs->kcount; ++ii) { 77 | if (cargs->exps == NULL) { 78 | commands[ii] = new lcb_touch_cmd_t(cargs->keys[ii], 79 | cargs->sizes[ii]); 80 | } else { 81 | // @todo how do the single thing work? 82 | commands[ii] = new lcb_touch_cmd_t(cargs->keys[ii], 83 | cargs->sizes[ii], 84 | cargs->exps[ii]); 85 | } 86 | } 87 | 88 | lcb_error_t ret = lcb_touch(instance, cookie, cargs->kcount, commands); 89 | for (size_t ii = 0; ii < cargs->kcount; ++ii) { 90 | delete commands[ii]; 91 | } 92 | delete []commands; 93 | 94 | return ret; 95 | } 96 | 97 | static lcb_error_t doArithmetic(lcb_t instance, 98 | CommonArgs *args, 99 | CouchbaseCookie *cookie) 100 | { 101 | ArithmeticArgs *cargs = static_cast(args); 102 | lcb_arithmetic_cmd_t cmd(cargs->key, cargs->nkey, cargs->delta, 103 | cargs->create, cargs->initial, cargs->exp); 104 | lcb_arithmetic_cmd_t *commands[] = { &cmd }; 105 | return lcb_arithmetic(instance, cookie, 1, commands); 106 | } 107 | 108 | static lcb_error_t doRemove(lcb_t instance, 109 | CommonArgs *args, 110 | CouchbaseCookie *cookie) 111 | { 112 | KeyopArgs *cargs = static_cast(args); 113 | lcb_remove_cmd_t cmd(cargs->key, cargs->nkey, cargs->cas); 114 | lcb_remove_cmd_t *commands[] = { &cmd }; 115 | return lcb_remove(instance, cookie, 1, commands); 116 | } 117 | 118 | /** 119 | * Entry point for API calls. 120 | */ 121 | template 122 | static inline v8::Handle makeOperation(CouchbaseImpl *me, 123 | Targs *cargs, 124 | QueuedCommand::operfunc ofn) 125 | { 126 | cargs->use_dictparams = me->isUsingHashtableParams(); 127 | try { 128 | if (!cargs->parse()) { 129 | return cargs->excerr; 130 | } 131 | } catch (Couchnode::Exception &exp) { 132 | return ThrowException(exp.getMessage().c_str()); 133 | } 134 | 135 | CouchbaseCookie *cookie = cargs->makeCookie(); 136 | 137 | lcb_error_t err = ofn(me->getLibcouchbaseHandle(), cargs, cookie); 138 | 139 | if (err != LCB_SUCCESS) { 140 | me->setLastError(err); 141 | if (me->isConnected()) { 142 | cargs->bailout(cookie, err); 143 | return v8::False(); 144 | } 145 | Targs *newargs = new Targs(*cargs); 146 | newargs->sync(*cargs); 147 | cargs->invalidate(); 148 | QueuedCommand cmd(cookie, newargs, ofn); 149 | me->scheduleCommand(cmd); 150 | } 151 | 152 | return v8::True(); 153 | } 154 | 155 | void CouchbaseImpl::runScheduledCommands(void) 156 | { 157 | for (QueuedCommandList::iterator iter = queued_commands.begin(); 158 | iter != queued_commands.end(); iter++) { 159 | 160 | lcb_error_t err = iter->ofn(instance, iter->args, iter->cookie); 161 | if (err != LCB_SUCCESS) { 162 | lastError = err; 163 | iter->args->bailout(iter->cookie, err); 164 | } 165 | iter->setDone(); 166 | } 167 | queued_commands.clear(); 168 | } 169 | 170 | #define COUCHNODE_API_INIT_COMMON(argcls) \ 171 | v8::HandleScope scope; \ 172 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); \ 173 | argcls cargs = argcls(args); \ 174 | 175 | v8::Handle CouchbaseImpl::Get(const v8::Arguments &args) 176 | { 177 | COUCHNODE_API_INIT_COMMON(MGetArgs); 178 | return makeOperation(me, &cargs, doGet); 179 | } 180 | 181 | v8::Handle CouchbaseImpl::Touch(const v8::Arguments &args) 182 | { 183 | COUCHNODE_API_INIT_COMMON(MGetArgs); 184 | return makeOperation(me, &cargs, doTouch); 185 | } 186 | 187 | #define COUCHBASE_STOREFN_DEFINE(name, mode) \ 188 | v8::Handle CouchbaseImpl::name(const v8::Arguments& args) { \ 189 | COUCHNODE_API_INIT_COMMON(StorageArgs); \ 190 | cargs.storop = LCB_##mode; \ 191 | return makeOperation(me, &cargs, doSet); \ 192 | } 193 | 194 | COUCHBASE_STOREFN_DEFINE(Set, SET) 195 | COUCHBASE_STOREFN_DEFINE(Add, ADD) 196 | COUCHBASE_STOREFN_DEFINE(Replace, REPLACE) 197 | COUCHBASE_STOREFN_DEFINE(Append, APPEND) 198 | COUCHBASE_STOREFN_DEFINE(Prepend, PREPEND) 199 | 200 | v8::Handle CouchbaseImpl::Arithmetic(const v8::Arguments &args) 201 | { 202 | COUCHNODE_API_INIT_COMMON(ArithmeticArgs); 203 | return makeOperation(me, &cargs, doArithmetic); 204 | } 205 | 206 | v8::Handle CouchbaseImpl::Remove(const v8::Arguments &args) 207 | { 208 | COUCHNODE_API_INIT_COMMON(KeyopArgs); 209 | return makeOperation(me, &cargs, doRemove); 210 | } 211 | -------------------------------------------------------------------------------- /src/args.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #include "couchbase_impl.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace Couchnode; 8 | using namespace std; 9 | 10 | static bool get_string(const v8::Handle &jv, 11 | char **strp, 12 | size_t *szp) 13 | { 14 | if (!jv->IsString()) { 15 | #ifdef COUCHNODE_DEBUG 16 | printf("Not a string..\n"); 17 | #endif 18 | return false; 19 | } 20 | 21 | v8::Local s = jv->ToString(); 22 | *szp = s->Length(); 23 | if (!*szp) { 24 | return false; 25 | } 26 | 27 | *strp = new char[*szp]; 28 | // it looks like this is where the unicode bug lives 29 | s->WriteAscii(*strp, 0, *szp, v8::String::NO_NULL_TERMINATION); 30 | return true; 31 | } 32 | 33 | #define GET_DPARAM(lval, bidx) \ 34 | if (!dict.IsEmpty()) { \ 35 | lval = dict->Get(NameMap::names[NameMap::##bidx]); \ 36 | } 37 | 38 | 39 | static inline bool isFalseValue(const v8::Handle &v) 40 | { 41 | return (v.IsEmpty() || v->BooleanValue() == false); 42 | } 43 | 44 | CommonArgs::CommonArgs(const v8::Arguments &argv, int pmax, int reqmax) 45 | : args(argv), key(NULL), params_max(pmax), required_max(reqmax), stale(false) 46 | { 47 | } 48 | 49 | bool CommonArgs::parse() 50 | { 51 | if (use_dictparams) { 52 | if (args.Length() < required_max + 2) { 53 | throw Couchnode::Exception("Bad arguments"); 54 | } 55 | if (args.Length() == required_max + 3) { 56 | if (!args[required_max + 2]->IsObject()) { 57 | throw Couchnode::Exception( 58 | "Have last argument, but it's not an Object", 59 | args[required_max + 2]); 60 | } 61 | dict = args[required_max + 2]->ToObject(); 62 | } 63 | } else if (args.Length() < (params_max + 2)) { 64 | throw Couchnode::Exception("Bad arguments"); 65 | } 66 | 67 | if (!extractKey()) { 68 | return false; 69 | } 70 | 71 | if (!extractUdata()) { 72 | return false; 73 | } 74 | return true; 75 | } 76 | 77 | bool CommonArgs::extractKey() 78 | { 79 | if (!get_string(args[0], &key, &nkey)) { 80 | #ifdef COUCHNODE_DEBUG 81 | printf("Arg at pos %d\n", 0); 82 | #endif 83 | throw Couchnode::Exception("Couldn't extract string"); 84 | } 85 | return true; 86 | } 87 | 88 | bool CommonArgs::extractUdata() 89 | { 90 | // { "key", .. params_max ..., function() { .. }, "Data" } 91 | // Index should be 1 + params_max 92 | 93 | int ix; 94 | if (use_dictparams) { 95 | ix = required_max + 1; 96 | } else { 97 | ix = params_max + 1; 98 | } 99 | 100 | ucb = v8::Local::Cast(args[ix]); 101 | if (!ucb->IsFunction()) { 102 | #ifdef COUCHNODE_DEBUG 103 | printf("Not a function at index %d\n", ix); 104 | #endif 105 | throw Couchnode::Exception("Not a function", args[ix]); 106 | } 107 | 108 | getParam(ix + 1, NameMap::DATA, &udata); 109 | return true; 110 | } 111 | 112 | void CommonArgs::extractCas(const v8::Handle &arg, 113 | lcb_cas_t *cas) 114 | { 115 | if (isFalseValue(arg)) { 116 | *cas = 0; 117 | } else if (arg->IsObject()) { 118 | *cas = Cas::GetCas(arg->ToObject()); 119 | } else { 120 | throw Couchnode::Exception("Couldn't parse CAS", arg); 121 | } 122 | } 123 | 124 | void CommonArgs::extractExpiry(const v8::Handle &arg, time_t *exp) 125 | { 126 | if (isFalseValue(arg)) { 127 | *exp = 0; 128 | } else if (arg->IsNumber()) { 129 | *exp = arg->Uint32Value(); 130 | } else { 131 | throw Couchnode::Exception("Couldn't extract expiration", arg); 132 | } 133 | } 134 | 135 | CouchbaseCookie *CommonArgs::makeCookie() 136 | { 137 | return new CouchbaseCookie(args.This(), ucb, udata, 1); 138 | } 139 | 140 | void CommonArgs::bailout(CouchbaseCookie *cookie, lcb_error_t err) 141 | { 142 | cookie->result(err, key, nkey); 143 | } 144 | 145 | CommonArgs::~CommonArgs() 146 | { 147 | if (stale) { 148 | return; 149 | } 150 | 151 | if (key) { 152 | delete[] key; 153 | key = NULL; 154 | } 155 | } 156 | 157 | // store(key, value, exp, cas, cb, data) 158 | StorageArgs::StorageArgs(const v8::Arguments &argv, int vparams) 159 | : CommonArgs(argv, vparams + 3, 1), 160 | 161 | data(NULL), ndata(0), exp(0), cas(0) 162 | { 163 | } 164 | 165 | bool StorageArgs::parse() 166 | { 167 | 168 | if (!CommonArgs::parse()) { 169 | return false; 170 | } 171 | 172 | if (!extractValue()) { 173 | return false; 174 | } 175 | 176 | v8::Local arg_exp, arg_cas; 177 | getParam(params_max, NameMap::CAS, &arg_cas); 178 | getParam(params_max - 1, NameMap::EXPIRY, &arg_exp); 179 | 180 | extractExpiry(arg_exp, &exp); 181 | extractCas(arg_cas, &cas); 182 | return true; 183 | } 184 | 185 | bool StorageArgs::extractValue() 186 | { 187 | if (!get_string(args[1], &data, &ndata)) { 188 | throw Couchnode::Exception("Bad value", args[1]); 189 | } 190 | 191 | return true; 192 | } 193 | 194 | StorageArgs::~StorageArgs() 195 | { 196 | if (stale) { 197 | return; 198 | } 199 | 200 | if (data) { 201 | delete[] data; 202 | data = NULL; 203 | } 204 | } 205 | 206 | MGetArgs::MGetArgs(const v8::Arguments &args, int nkparams) 207 | : CommonArgs(args, nkparams), 208 | kcount(0), single_exp(0), keys(NULL), sizes(NULL), exps(NULL) 209 | { 210 | } 211 | 212 | bool MGetArgs::extractKey() 213 | { 214 | 215 | if (args[0]->IsString()) { 216 | if (!CommonArgs::extractKey()) { 217 | return false; 218 | } 219 | 220 | kcount = 1; 221 | v8::Local arg_exp; 222 | getParam(1, NameMap::EXPIRY, &arg_exp); 223 | 224 | extractExpiry(arg_exp, &single_exp); 225 | assert(key); 226 | 227 | keys = &key; 228 | sizes = &nkey; 229 | 230 | if (single_exp) { 231 | exps = &single_exp; 232 | } 233 | 234 | return true; 235 | } 236 | 237 | exps = NULL; 238 | 239 | if (!args[0]->IsArray()) { 240 | return false; 241 | } 242 | 243 | v8::Local karry = v8::Local::Cast(args[0]); 244 | kcount = karry->Length(); 245 | 246 | keys = new char*[kcount]; 247 | sizes = new size_t[kcount]; 248 | 249 | memset(keys, 0, sizeof(char *) * kcount); 250 | memset(sizes, 0, sizeof(size_t) * kcount); 251 | 252 | for (unsigned ii = 0; ii < karry->Length(); ii++) { 253 | if (!get_string(karry->Get(ii), keys + ii, sizes + ii)) { 254 | return false; 255 | } 256 | } 257 | return true; 258 | } 259 | 260 | void MGetArgs::bailout(CouchbaseCookie *cookie, lcb_error_t err) 261 | { 262 | for (unsigned ii = 0; ii < kcount; ii++) { 263 | cookie->result(err, keys[ii], sizes[ii]); 264 | } 265 | } 266 | 267 | MGetArgs::~MGetArgs() 268 | { 269 | if (stale) { 270 | return; 271 | } 272 | 273 | if (exps && exps != &single_exp) { 274 | delete[] exps; 275 | } 276 | 277 | if (sizes && sizes != &nkey) { 278 | delete[] sizes; 279 | } 280 | 281 | if (keys && keys != &key) { 282 | for (unsigned ii = 0; ii < kcount; ii++) { 283 | delete[] keys[ii]; 284 | } 285 | delete[] keys; 286 | } 287 | 288 | exps = NULL; 289 | sizes = NULL; 290 | keys = NULL; 291 | } 292 | 293 | KeyopArgs::KeyopArgs(const v8::Arguments &args) 294 | : CommonArgs(args, 1), cas(0) 295 | { 296 | } 297 | 298 | bool KeyopArgs::parse() 299 | { 300 | if (!CommonArgs::parse()) { 301 | return false; 302 | } 303 | 304 | v8::Local arg_cas; 305 | getParam(1, NameMap::CAS, &arg_cas); 306 | 307 | extractCas(arg_cas, &cas); 308 | return true; 309 | } 310 | 311 | // arithmetic(key, delta, initial, exp, cas, ...) 312 | ArithmeticArgs::ArithmeticArgs(const v8::Arguments &args) 313 | : StorageArgs(args, 1), 314 | delta(0), initial(0), create(false) 315 | { 316 | } 317 | 318 | bool ArithmeticArgs::extractValue() 319 | { 320 | if (args[1]->IsNumber() == false) { 321 | throw Couchnode::Exception("Delta must be numeric", args[1]); 322 | } 323 | 324 | delta = args[1]->IntegerValue(); 325 | 326 | v8::Local arg_initial; 327 | getParam(2, NameMap::INITIAL, &arg_initial); 328 | 329 | if (!arg_initial.IsEmpty()) { 330 | if (arg_initial->IsNumber()) { 331 | initial = arg_initial->IntegerValue(); 332 | create = true; 333 | } else { 334 | if (!arg_initial->IsUndefined()) { 335 | throw Couchnode::Exception("Initial value must be numeric", 336 | arg_initial); 337 | } 338 | create = false; 339 | } 340 | 341 | } else { 342 | create = false; 343 | } 344 | 345 | return true; 346 | } 347 | -------------------------------------------------------------------------------- /src/notify.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #include "couchbase_impl.h" 3 | #include 4 | #include 5 | 6 | using namespace Couchnode; 7 | using namespace v8; 8 | 9 | CouchbaseCookie::CouchbaseCookie(Handle cbo, 10 | Handle callback, 11 | Handle data, 12 | unsigned int numRemaining) 13 | : remaining(numRemaining), 14 | parent(Persistent::New(cbo)), 15 | ucookie(Persistent::New(data)), 16 | ucallback(Persistent::New(callback)) 17 | { 18 | if (ucookie.IsEmpty()) { 19 | ucookie = Persistent::New(v8::Undefined()); 20 | } 21 | } 22 | 23 | CouchbaseCookie::~CouchbaseCookie() 24 | { 25 | if (!parent.IsEmpty()) { 26 | parent.Dispose(); 27 | parent.Clear(); 28 | } 29 | 30 | if (!ucookie.IsEmpty()) { 31 | ucookie.Dispose(); 32 | ucookie.Clear(); 33 | } 34 | 35 | if (!ucallback.IsEmpty()) { 36 | ucallback.Dispose(); 37 | ucookie.Clear(); 38 | } 39 | } 40 | 41 | // (data, error, key, cas, flags, value) 42 | void CouchbaseCookie::result(lcb_error_t error, 43 | const void *key, lcb_size_t nkey, 44 | const void *bytes, 45 | lcb_size_t nbytes, 46 | lcb_uint32_t flags, 47 | lcb_cas_t cas) 48 | { 49 | using namespace v8; 50 | HandleScope scope; 51 | Persistent context = Context::New(); 52 | Local argv[6]; 53 | 54 | argv[0] = Local::New(ucookie); 55 | argv[2] = Local::New(String::New((const char *)key, nkey)); 56 | 57 | if (error != LCB_SUCCESS) { 58 | argv[1] = Local::New(Number::New(error)); 59 | argv[3] = Local::New(Undefined()); 60 | argv[4] = Local::New(Undefined()); 61 | argv[5] = Local::New(Undefined()); 62 | } else { 63 | argv[1] = Local::New(False()); 64 | argv[3] = Local::New(Cas::CreateCas(cas)); 65 | argv[4] = Local::New(Number::New(flags)); 66 | argv[5] = Local::New(String::New((const char *)bytes, nbytes)); 67 | } 68 | 69 | invoke(context, 6, argv); 70 | } 71 | 72 | // (data, error, key, cas) 73 | void CouchbaseCookie::result(lcb_error_t error, 74 | const void *key, lcb_size_t nkey, 75 | lcb_cas_t cas) 76 | { 77 | using namespace v8; 78 | HandleScope scope; 79 | Persistent context = Context::New(); 80 | Local argv[4]; 81 | 82 | argv[0] = Local::New(ucookie); 83 | argv[2] = Local::New(String::New((const char *)key, nkey)); 84 | 85 | if (error != LCB_SUCCESS) { 86 | argv[1] = Local::New(Number::New(error)); 87 | argv[3] = Local::New(Undefined()); 88 | } else { 89 | argv[1] = Local::New(False()); 90 | argv[3] = Local::New(Cas::CreateCas(cas)); 91 | } 92 | 93 | invoke(context, 4, argv); 94 | } 95 | 96 | // (data, error, key, cas, value) 97 | void CouchbaseCookie::result(lcb_error_t error, 98 | const void *key, lcb_size_t nkey, 99 | lcb_uint64_t value, 100 | lcb_cas_t cas) 101 | { 102 | using namespace v8; 103 | HandleScope scope; 104 | Persistent context = Context::New(); 105 | Local argv[5]; 106 | 107 | argv[0] = Local::New(ucookie); 108 | argv[2] = Local::New(String::New((const char *)key, nkey)); 109 | 110 | if (error != LCB_SUCCESS) { 111 | argv[1] = Local::New(Number::New(error)); 112 | argv[3] = Local::New(Undefined()); 113 | argv[4] = Local::New(Undefined()); 114 | } else { 115 | argv[1] = Local::New(False()); 116 | argv[3] = Local::New(Cas::CreateCas(cas)); 117 | argv[4] = Local::New(Number::New(value)); 118 | } 119 | 120 | invoke(context, 5, argv); 121 | } 122 | 123 | // (data, error, key) 124 | void CouchbaseCookie::result(lcb_error_t error, 125 | const void *key, lcb_size_t nkey) 126 | { 127 | using namespace v8; 128 | HandleScope scope; 129 | Persistent context = Context::New(); 130 | Local argv[3]; 131 | 132 | argv[0] = Local::New(ucookie); 133 | argv[2] = Local::New(String::New((const char *)key, nkey)); 134 | 135 | if (error != LCB_SUCCESS) { 136 | argv[1] = Local::New(Number::New(error)); 137 | } else { 138 | argv[1] = Local::New(False()); 139 | } 140 | 141 | invoke(context, 3, argv); 142 | } 143 | 144 | static inline CouchbaseCookie *getInstance(const void *c) 145 | { 146 | return reinterpret_cast(const_cast(c)); 147 | } 148 | 149 | // @todo we need to do this a better way in the future! 150 | static void unknownLibcouchbaseType(const std::string &type, int version) 151 | { 152 | std::stringstream ss; 153 | ss << "Received an unsupported object version for " 154 | << type.c_str() << ": " << version; 155 | Couchnode::Exception ex(ss.str().c_str()); 156 | throw ex; 157 | } 158 | 159 | 160 | extern "C" { 161 | // libcouchbase handlers keep a C linkage... 162 | static void error_callback(lcb_t instance, 163 | lcb_error_t err, const char *errinfo) 164 | { 165 | void *cookie = const_cast(lcb_get_cookie(instance)); 166 | CouchbaseImpl *me = reinterpret_cast(cookie); 167 | me->errorCallback(err, errinfo); 168 | } 169 | 170 | 171 | 172 | static void get_callback(lcb_t, 173 | const void *cookie, 174 | lcb_error_t error, 175 | const lcb_get_resp_t *resp) 176 | { 177 | if (resp->version != 0) { 178 | unknownLibcouchbaseType("get", resp->version); 179 | } 180 | 181 | getInstance(cookie)->result(error, resp->v.v0.key, resp->v.v0.nkey, 182 | resp->v.v0.bytes, resp->v.v0.nbytes, 183 | resp->v.v0.flags, resp->v.v0.cas); 184 | } 185 | 186 | static void store_callback(lcb_t, 187 | const void *cookie, 188 | lcb_storage_t, 189 | lcb_error_t error, 190 | const lcb_store_resp_t *resp) 191 | { 192 | if (resp->version != 0) { 193 | unknownLibcouchbaseType("store", resp->version); 194 | } 195 | 196 | getInstance(cookie)->result(error, resp->v.v0.key, 197 | resp->v.v0.nkey, resp->v.v0.cas); 198 | } 199 | 200 | static void arithmetic_callback(lcb_t, 201 | const void *cookie, 202 | lcb_error_t error, 203 | const lcb_arithmetic_resp_t *resp) 204 | { 205 | if (resp->version != 0) { 206 | unknownLibcouchbaseType("arithmetic", resp->version); 207 | } 208 | 209 | getInstance(cookie)->result(error, resp->v.v0.key, resp->v.v0.nkey, 210 | resp->v.v0.value, resp->v.v0.cas); 211 | } 212 | 213 | 214 | 215 | static void remove_callback(lcb_t, 216 | const void *cookie, 217 | lcb_error_t error, 218 | const lcb_remove_resp_t *resp) 219 | { 220 | if (resp->version != 0) { 221 | unknownLibcouchbaseType("remove", resp->version); 222 | } 223 | 224 | getInstance(cookie)->result(error, resp->v.v0.key, resp->v.v0.nkey); 225 | 226 | } 227 | 228 | static void touch_callback(lcb_t, 229 | const void *cookie, 230 | lcb_error_t error, 231 | const lcb_touch_resp_t *resp) 232 | { 233 | if (resp->version != 0) { 234 | unknownLibcouchbaseType("touch", resp->version); 235 | } 236 | 237 | getInstance(cookie)->result(error, resp->v.v0.key, resp->v.v0.nkey); 238 | 239 | } 240 | 241 | static void configuration_callback(lcb_t instance, 242 | lcb_configuration_t config) 243 | { 244 | void *cookie = const_cast(lcb_get_cookie(instance)); 245 | CouchbaseImpl *me = reinterpret_cast(cookie); 246 | me->onConnect(config); 247 | } 248 | } // extern "C" 249 | 250 | void CouchbaseImpl::setupLibcouchbaseCallbacks(void) 251 | { 252 | lcb_set_error_callback(instance, error_callback); 253 | lcb_set_get_callback(instance, get_callback); 254 | lcb_set_store_callback(instance, store_callback); 255 | lcb_set_arithmetic_callback(instance, arithmetic_callback); 256 | lcb_set_remove_callback(instance, remove_callback); 257 | lcb_set_touch_callback(instance, touch_callback); 258 | lcb_set_configuration_callback(instance, configuration_callback); 259 | } 260 | -------------------------------------------------------------------------------- /io/common.c: -------------------------------------------------------------------------------- 1 | #include "lcb_luv_internal.h" 2 | #include 3 | #include 4 | 5 | static void 6 | maybe_callout(lcb_luv_socket_t sock) 7 | { 8 | short which = 0; 9 | if (!sock->event) { 10 | /* !? */ 11 | return; 12 | } 13 | lcb_luv_flush(sock); 14 | 15 | #define check_if_ready(fld) \ 16 | if (EVSTATE_IS(&(sock->evstate[LCB_LUV_EV_ ## fld]), PENDING) && \ 17 | (sock->event->lcb_events & LCB_ ## fld ## _EVENT)) { \ 18 | which |= LCB_ ## fld ## _EVENT; \ 19 | } 20 | 21 | check_if_ready(READ); 22 | check_if_ready(WRITE); 23 | #undef check_if_ready 24 | 25 | log_loop_rant("Will determine if we need to call any functions.."); 26 | log_loop_rant("which=%x, wait for=%x", which, sock->event->lcb_events); 27 | if (which) { 28 | log_loop_debug(" ==== CB Invoking callback for %d =====", sock->idx); 29 | sock->event->lcb_cb(sock->idx, which, sock->event->lcb_arg); 30 | log_loop_debug("==== CB Done invoking callback for %d =====", sock->idx); 31 | lcb_luv_flush(sock); 32 | } 33 | } 34 | 35 | static void 36 | prepare_cb(uv_prepare_t *handle, int status) 37 | { 38 | lcb_luv_socket_t sock = (lcb_luv_socket_t)handle->data; 39 | log_loop_trace("prepcb start"); 40 | if (!sock) { 41 | fprintf(stderr, "We were called with prepare_t %p, with a missing socket\n", 42 | handle); 43 | return; 44 | } 45 | 46 | lcb_luv_socket_ref(sock); 47 | maybe_callout(sock); 48 | lcb_luv_socket_unref(sock); 49 | log_loop_trace("prepcb stop"); 50 | } 51 | 52 | void 53 | lcb_luv_schedule_enable(lcb_luv_socket_t sock) 54 | { 55 | if (sock->prep_active) { 56 | log_loop_trace("prep_active is true"); 57 | return; 58 | } 59 | 60 | log_loop_debug("Will try and schedule prepare callback for %d", sock->idx); 61 | lcb_luv_socket_ref(sock); 62 | uv_prepare_start(&sock->prep, prepare_cb); 63 | sock->prep_active = 1; 64 | } 65 | 66 | void 67 | lcb_luv_schedule_disable(lcb_luv_socket_t sock) 68 | { 69 | if (sock->prep_active == 0) { 70 | log_loop_trace("prep_active is false"); 71 | return; 72 | } 73 | log_loop_debug("Disabling prepare"); 74 | uv_prepare_stop(&sock->prep); 75 | lcb_luv_socket_unref(sock); 76 | sock->prep_active = 0; 77 | } 78 | 79 | static inline lcb_socket_t 80 | find_free_idx(struct lcb_luv_cookie_st *cookie) 81 | { 82 | lcb_socket_t ret = -1; 83 | unsigned int nchecked = 0; 84 | while (nchecked < cookie->fd_max && ret == -1) { 85 | if (cookie->socktable[cookie->fd_next] == NULL) { 86 | ret = cookie->fd_next; 87 | } 88 | cookie->fd_next = ((cookie->fd_next + 1) % cookie->fd_max); 89 | nchecked++; 90 | } 91 | return ret; 92 | } 93 | 94 | 95 | lcb_luv_socket_t 96 | lcb_luv_socket_new(struct lcb_io_opt_st *iops) 97 | { 98 | /* Find the next 'file descriptor' */ 99 | 100 | lcb_socket_t idx; 101 | lcb_luv_socket_t newsock; 102 | idx = find_free_idx(IOPS_COOKIE(iops)); 103 | if (idx == -1) { 104 | iops->v.v0.error = ENFILE; 105 | return NULL; 106 | } 107 | 108 | newsock = calloc(1, sizeof(struct lcb_luv_socket_st)); 109 | newsock->idx = idx; 110 | newsock->parent = IOPS_COOKIE(iops); 111 | 112 | uv_prepare_init(newsock->parent->loop, &newsock->prep); 113 | newsock->prep_active = 0; 114 | 115 | newsock->prep.data = newsock; 116 | newsock->u_req.req.data = newsock; 117 | newsock->refcount = 1; 118 | 119 | uv_tcp_init(IOPS_COOKIE(iops)->loop, &newsock->tcp); 120 | IOPS_COOKIE(iops)->socktable[idx] = newsock; 121 | iops->v.v0.error = 0; 122 | log_socket_debug("%p: Created new socket %p(%d)", iops, newsock, idx); 123 | return newsock; 124 | } 125 | 126 | void lcb_luv_socket_free(lcb_luv_socket_t sock) 127 | { 128 | assert(sock->event == NULL); 129 | assert(sock->idx == -1); 130 | assert(sock->refcount == 0); 131 | assert(sock->prep_active == 0); 132 | assert(sock->read.readhead_active == 0); 133 | free(sock); 134 | } 135 | 136 | static void 137 | sock_free_pass(lcb_luv_socket_t sock) 138 | { 139 | sock->handle_count--; 140 | if (!sock->handle_count) { 141 | free(sock); 142 | } 143 | } 144 | 145 | static void 146 | io_close_cb(uv_handle_t *handle) 147 | { 148 | lcb_luv_socket_t sock = (lcb_luv_socket_t)handle; 149 | sock_free_pass(sock); 150 | } 151 | 152 | static void 153 | prep_close_cb(uv_handle_t *handle) 154 | { 155 | lcb_luv_socket_t sock = (lcb_luv_socket_t) 156 | (((char *)handle) - offsetof(struct lcb_luv_socket_st, prep)); 157 | sock_free_pass(sock); 158 | } 159 | 160 | unsigned long 161 | lcb_luv_socket_unref(lcb_luv_socket_t sock) 162 | { 163 | unsigned long ret; 164 | assert(sock->refcount); 165 | sock->refcount--; 166 | ret = sock->refcount; 167 | 168 | if (ret == 0) { 169 | sock->handle_count = 2; 170 | uv_close((uv_handle_t *)&sock->tcp, io_close_cb); 171 | uv_close((uv_handle_t *)&sock->prep, prep_close_cb); 172 | } 173 | return ret; 174 | } 175 | 176 | void 177 | lcb_luv_socket_deinit(lcb_luv_socket_t sock) 178 | { 179 | if (sock->idx == -1) { 180 | return; 181 | } 182 | 183 | log_socket_info("%p: Deinitializing socket %d", sock->parent, sock->idx); 184 | 185 | lcb_luv_schedule_disable(sock); 186 | 187 | if (sock->event && sock->event->handle == sock) { 188 | sock->event->handle = NULL; 189 | sock->event = NULL; 190 | } 191 | 192 | lcb_luv_read_stop(sock); 193 | 194 | assert(sock->prep_active == 0); 195 | sock->parent->socktable[sock->idx] = 0; 196 | sock->idx = -1; 197 | 198 | if (sock->refcount > 1) { 199 | log_socket_warn("Socket %p still has a reference count of %d", 200 | sock, sock->refcount); 201 | int ii; 202 | for (ii = 0; ii < LCB_LUV_EV_MAX; ii++) { 203 | struct lcb_luv_evstate_st *evstate = sock->evstate + ii; 204 | log_socket_warn("Flags for evstate@%d: 0x%X", ii, evstate->flags); 205 | } 206 | 207 | log_socket_warn("Write buffer has %d bytes", sock->write.nb); 208 | log_socket_warn("Write position is at %d", sock->write.pos); 209 | log_socket_warn("Read buffer has %d bytes", sock->read.nb); 210 | } 211 | lcb_luv_socket_unref(sock); 212 | } 213 | 214 | 215 | lcb_luv_socket_t 216 | lcb_luv_sock_from_idx(struct lcb_io_opt_st *iops, lcb_socket_t idx) 217 | { 218 | if (idx < 0) { 219 | return NULL; 220 | } 221 | 222 | if (idx > IOPS_COOKIE(iops)->fd_max) { 223 | return NULL; 224 | } 225 | 226 | return IOPS_COOKIE(iops)->socktable[idx]; 227 | } 228 | 229 | void * 230 | lcb_luv_create_event(struct lcb_io_opt_st *iops) 231 | { 232 | struct lcb_luv_event_st *ev = calloc(1, sizeof(struct lcb_luv_event_st)); 233 | return ev; 234 | } 235 | 236 | void 237 | lcb_luv_delete_event(struct lcb_io_opt_st *iops, 238 | lcb_socket_t sock_i, 239 | void *event_opaque) 240 | { 241 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 242 | struct lcb_luv_event_st *ev = (struct lcb_luv_event_st *)event_opaque; 243 | int ii; 244 | 245 | if (sock == NULL && ev == NULL) { 246 | return; 247 | } 248 | 249 | if (sock) { 250 | lcb_luv_schedule_disable(sock); 251 | lcb_luv_read_stop(sock); 252 | 253 | /* clear all the state flags */ 254 | for (ii = 0; ii < LCB_LUV_EV_RDWR_MAX; ii++) { 255 | sock->evstate[ii].flags = 0; 256 | } 257 | sock->event = NULL; 258 | } 259 | 260 | if (ev && (ev->handle == sock || sock == NULL)) { 261 | ev->handle = NULL; 262 | ev->lcb_events = 0; 263 | } 264 | } 265 | 266 | void 267 | lcb_luv_destroy_event(struct lcb_io_opt_st *iops, 268 | void *event_opaque) 269 | { 270 | struct lcb_luv_event_st *ev = (struct lcb_luv_event_st *)event_opaque; 271 | if (ev->handle) { 272 | ev->handle->event = NULL; 273 | } 274 | free(ev); 275 | } 276 | 277 | int 278 | lcb_luv_update_event(struct lcb_io_opt_st *iops, 279 | lcb_socket_t sock_i, 280 | void *event_opaque, 281 | short flags, 282 | void *cb_data, 283 | lcb_luv_callback_t cb) 284 | { 285 | struct lcb_luv_event_st *event = (struct lcb_luv_event_st *)event_opaque; 286 | /* Check to see if our 'socket' is valid */ 287 | lcb_luv_socket_t sock = lcb_luv_sock_from_idx(iops, sock_i); 288 | if (sock == NULL) { 289 | log_event_error("Requested update on invalid socket: fd=%d", sock_i); 290 | return 0; 291 | } 292 | 293 | log_event_debug("Requested events %x", flags); 294 | 295 | if (sock->event) { 296 | assert(sock->event == event); 297 | assert(event->handle == sock); 298 | } else { 299 | sock->event = event; 300 | event->handle = sock; 301 | } 302 | 303 | event->lcb_cb = cb; 304 | event->lcb_arg = cb_data; 305 | event->lcb_events = flags; 306 | 307 | /* trigger a read-ahead */ 308 | if (flags & LCB_READ_EVENT) { 309 | lcb_luv_read_nudge(sock); 310 | } 311 | 312 | if (flags & LCB_WRITE_EVENT) { 313 | /* for writes, we either have data to be flushed, or we want to wait 314 | * until we're able to write data. In any event, we schedule for this 315 | */ 316 | if ((sock->evstate[LCB_LUV_EV_WRITE].flags & LCB_LUV_EVf_FLUSHING) == 0 && 317 | sock->write.nb == 0) { 318 | sock->evstate[LCB_LUV_EV_WRITE].flags |= LCB_LUV_EVf_PENDING; 319 | } 320 | 321 | lcb_luv_schedule_enable(sock); 322 | } else { 323 | sock->evstate[LCB_LUV_EV_WRITE].flags &= (~LCB_LUV_EVf_PENDING); 324 | } 325 | 326 | return 1; 327 | } 328 | 329 | int 330 | lcb_luv_errno_map(int uverr) 331 | { 332 | 333 | #ifndef UNKNOWN 334 | #define UNKNOWN -1 335 | #endif 336 | 337 | #ifndef EAIFAMNOSUPPORT 338 | #define EAIFAMNOSUPPORT EAI_FAMILY 339 | #endif 340 | 341 | #ifndef EAISERVICE 342 | #define EAISERVICE EAI_SERVICE 343 | #endif 344 | 345 | #ifndef EADDRINFO 346 | #define EADDRINFO EAI_SYSTEM 347 | #endif 348 | 349 | #ifndef EAISOCKTYPE 350 | #define EAISOCKTYPE EAI_SOCKTYPE 351 | #endif 352 | 353 | #ifndef ECHARSET 354 | #define ECHARSET 0 355 | #endif 356 | 357 | #ifndef ENONET 358 | #define ENONET ENETDOWN 359 | #endif 360 | 361 | #define OK 0 362 | 363 | int ret = 0; 364 | #define X(errnum,errname,errdesc) \ 365 | if (uverr == UV_##errname) { \ 366 | return errname; \ 367 | } 368 | UV_ERRNO_MAP(X); 369 | 370 | return ret; 371 | 372 | #undef X 373 | } 374 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/couchbase_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | #include 18 | #include 19 | #include 20 | 21 | #include "couchbase_impl.h" 22 | #include "io/libcouchbase-libuv.h" 23 | #include "cas.h" 24 | 25 | using namespace std; 26 | using namespace Couchnode; 27 | 28 | typedef lcb_io_opt_st *(*loop_factory_fn)(uv_loop_t *, uint16_t); 29 | 30 | // libcouchbase handlers keep a C linkage... 31 | extern "C" { 32 | // node.js will call the init method when the shared object 33 | // is successfully loaded. Time to register our class! 34 | static void init(v8::Handle target) 35 | { 36 | CouchbaseImpl::Init(target); 37 | } 38 | 39 | NODE_MODULE(couchbase_impl, init) 40 | } 41 | 42 | #ifdef COUCHNODE_DEBUG 43 | unsigned int CouchbaseImpl::objectCount; 44 | #endif 45 | 46 | v8::Handle Couchnode::ThrowException(const char *str) 47 | { 48 | return v8::ThrowException(v8::Exception::Error(v8::String::New(str))); 49 | } 50 | 51 | v8::Handle Couchnode::ThrowIllegalArgumentsException(void) 52 | { 53 | return Couchnode::ThrowException("Illegal Arguments"); 54 | } 55 | 56 | CouchbaseImpl::CouchbaseImpl(lcb_t inst) : 57 | ObjectWrap(), connected(false), useHashtableParams(false), 58 | instance(inst), lastError(LCB_SUCCESS) 59 | 60 | { 61 | lcb_set_cookie(instance, reinterpret_cast(this)); 62 | setupLibcouchbaseCallbacks(); 63 | #ifdef COUCHNODE_DEBUG 64 | ++objectCount; 65 | #endif 66 | } 67 | 68 | CouchbaseImpl::~CouchbaseImpl() 69 | { 70 | #ifdef COUCHNODE_DEBUG 71 | --objectCount; 72 | cerr << "Destroying handle.." << endl 73 | << "Still have " << objectCount << " handles remaining" << endl; 74 | #endif 75 | 76 | lcb_destroy(instance); 77 | 78 | EventMap::iterator iter = events.begin(); 79 | while (iter != events.end()) { 80 | if (!iter->second.IsEmpty()) { 81 | iter->second.Dispose(); 82 | iter->second.Clear(); 83 | } 84 | ++iter; 85 | } 86 | } 87 | 88 | void CouchbaseImpl::Init(v8::Handle target) 89 | { 90 | v8::HandleScope scope; 91 | 92 | v8::Local t = v8::FunctionTemplate::New(New); 93 | 94 | v8::Persistent s_ct; 95 | s_ct = v8::Persistent::New(t); 96 | s_ct->InstanceTemplate()->SetInternalFieldCount(1); 97 | s_ct->SetClassName(v8::String::NewSymbol("CouchbaseImpl")); 98 | 99 | NODE_SET_PROTOTYPE_METHOD(s_ct, "getVersion", GetVersion); 100 | NODE_SET_PROTOTYPE_METHOD(s_ct, "setTimeout", SetTimeout); 101 | NODE_SET_PROTOTYPE_METHOD(s_ct, "getTimeout", GetTimeout); 102 | NODE_SET_PROTOTYPE_METHOD(s_ct, "getRestUri", GetRestUri); 103 | NODE_SET_PROTOTYPE_METHOD(s_ct, "setSynchronous", SetSynchronous); 104 | NODE_SET_PROTOTYPE_METHOD(s_ct, "isSynchronous", IsSynchronous); 105 | NODE_SET_PROTOTYPE_METHOD(s_ct, "getLastError", GetLastError); 106 | NODE_SET_PROTOTYPE_METHOD(s_ct, "get", Get); 107 | NODE_SET_PROTOTYPE_METHOD(s_ct, "set", Set); 108 | NODE_SET_PROTOTYPE_METHOD(s_ct, "add", Add); 109 | NODE_SET_PROTOTYPE_METHOD(s_ct, "replace", Replace); 110 | NODE_SET_PROTOTYPE_METHOD(s_ct, "append", Append); 111 | NODE_SET_PROTOTYPE_METHOD(s_ct, "prepend", Prepend); 112 | NODE_SET_PROTOTYPE_METHOD(s_ct, "on", On); 113 | NODE_SET_PROTOTYPE_METHOD(s_ct, "arithmetic", Arithmetic); 114 | NODE_SET_PROTOTYPE_METHOD(s_ct, "delete", Remove); 115 | NODE_SET_PROTOTYPE_METHOD(s_ct, "touch", Touch); 116 | NODE_SET_PROTOTYPE_METHOD(s_ct, "_opCallStyle", OpCallStyle); 117 | 118 | target->Set(v8::String::NewSymbol("CouchbaseImpl"), s_ct->GetFunction()); 119 | 120 | NameMap::initialize(); 121 | Cas::initialize(); 122 | } 123 | 124 | v8::Handle CouchbaseImpl::On(const v8::Arguments &args) 125 | { 126 | if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsFunction()) { 127 | return ThrowException("Usage: cb.on('event', 'callback')"); 128 | } 129 | 130 | // @todo verify that the user specifies a valid monitor ;) 131 | v8::HandleScope scope; 132 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 133 | v8::Handle ret = me->on(args); 134 | return scope.Close(ret); 135 | } 136 | 137 | v8::Handle CouchbaseImpl::on(const v8::Arguments &args) 138 | { 139 | v8::HandleScope scope; 140 | v8::Local s = args[0]->ToString(); 141 | char *func = new char[s->Length() + 1]; 142 | memset(func, 0, s->Length() + 1); 143 | s->WriteAscii(func); 144 | 145 | string function(func); 146 | delete []func; 147 | 148 | EventMap::iterator iter = events.find(function); 149 | if (iter != events.end() && !iter->second.IsEmpty()) { 150 | iter->second.Dispose(); 151 | iter->second.Clear(); 152 | } 153 | 154 | events[function] = 155 | v8::Persistent::New( 156 | v8::Local::Cast(args[1])); 157 | 158 | return scope.Close(v8::True()); 159 | } 160 | 161 | v8::Handle CouchbaseImpl::OpCallStyle(const v8::Arguments &args) 162 | { 163 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 164 | 165 | v8::Handle rv = me->useHashtableParams ? 166 | NameMap::names[NameMap::OPSTYLE_HASHTABLE] : 167 | NameMap::names[NameMap::OPSTYLE_POSITIONAL]; 168 | 169 | if (!args.Length()) { 170 | return rv; 171 | } 172 | 173 | if (args.Length() != 1 || args[0]->IsString() == false) { 174 | return ThrowException("First (and only) argument must be a string"); 175 | } 176 | 177 | if (NameMap::names[NameMap::OPSTYLE_HASHTABLE]->Equals(args[0])) { 178 | me->useHashtableParams = true; 179 | } else if (NameMap::names[NameMap::OPSTYLE_POSITIONAL]->Equals(args[0])) { 180 | me->useHashtableParams = false; 181 | } else { 182 | return ThrowException("Unrecognized call style"); 183 | } 184 | 185 | return rv; 186 | } 187 | 188 | v8::Handle CouchbaseImpl::New(const v8::Arguments &args) 189 | { 190 | v8::HandleScope scope; 191 | 192 | if (args.Length() < 1) { 193 | return ThrowException("You need to specify the URI for the REST server"); 194 | } 195 | 196 | if (args.Length() > 4) { 197 | return ThrowException("Too many arguments"); 198 | } 199 | 200 | char *argv[4]; 201 | memset(argv, 0, sizeof(argv)); 202 | 203 | for (int ii = 0; ii < args.Length(); ++ii) { 204 | if (args[ii]->IsString()) { 205 | v8::Local s = args[ii]->ToString(); 206 | argv[ii] = new char[s->Length() + 1]; 207 | s->WriteAscii(argv[ii]); 208 | } else if (!args[ii]->IsNull()) { 209 | // @todo handle NULL 210 | return ThrowIllegalArgumentsException(); 211 | } 212 | } 213 | 214 | lcb_io_opt_st *iops = lcb_luv_create_io_opts(uv_default_loop(), 215 | 1024); 216 | if (iops == NULL) { 217 | return ThrowException("Failed to create a new IO ops structure"); 218 | } 219 | 220 | lcb_create_st createOptions(argv[0], argv[1], argv[2], 221 | argv[3], iops); 222 | lcb_t instance; 223 | lcb_error_t err = lcb_create(&instance, &createOptions); 224 | for (int ii = 0; ii < 4; ++ii) { 225 | delete[] argv[ii]; 226 | } 227 | 228 | if (err != LCB_SUCCESS) { 229 | return ThrowException("Failed to create libcouchbase instance"); 230 | } 231 | 232 | if (lcb_connect(instance) != LCB_SUCCESS) { 233 | return ThrowException("Failed to schedule connection"); 234 | } 235 | 236 | CouchbaseImpl *hw = new CouchbaseImpl(instance); 237 | hw->Wrap(args.This()); 238 | return args.This(); 239 | } 240 | 241 | v8::Handle CouchbaseImpl::GetVersion(const v8::Arguments &) 242 | { 243 | v8::HandleScope scope; 244 | 245 | stringstream ss; 246 | ss << "libcouchbase node.js v1.0.0 (v" << lcb_get_version(NULL) 247 | << ")"; 248 | 249 | v8::Local result = v8::String::New(ss.str().c_str()); 250 | return scope.Close(result); 251 | } 252 | 253 | v8::Handle CouchbaseImpl::SetTimeout(const v8::Arguments &args) 254 | { 255 | if (args.Length() != 1 || !args[0]->IsInt32()) { 256 | return ThrowIllegalArgumentsException(); 257 | } 258 | 259 | v8::HandleScope scope; 260 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 261 | uint32_t timeout = args[0]->Int32Value(); 262 | lcb_set_timeout(me->instance, timeout); 263 | 264 | return v8::True(); 265 | } 266 | 267 | v8::Handle CouchbaseImpl::GetTimeout(const v8::Arguments &args) 268 | { 269 | if (args.Length() != 0) { 270 | return ThrowIllegalArgumentsException(); 271 | } 272 | 273 | v8::HandleScope scope; 274 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 275 | return scope.Close(v8::Integer::New(lcb_get_timeout(me->instance))); 276 | } 277 | 278 | v8::Handle CouchbaseImpl::GetRestUri(const v8::Arguments &args) 279 | { 280 | if (args.Length() != 0) { 281 | return ThrowIllegalArgumentsException(); 282 | } 283 | 284 | v8::HandleScope scope; 285 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 286 | stringstream ss; 287 | ss << lcb_get_host(me->instance) << ":" << lcb_get_port( 288 | me->instance); 289 | 290 | return scope.Close(v8::String::New(ss.str().c_str())); 291 | } 292 | 293 | v8::Handle CouchbaseImpl::SetSynchronous(const v8::Arguments &args) 294 | { 295 | if (args.Length() != 1) { 296 | return ThrowIllegalArgumentsException(); 297 | } 298 | 299 | v8::HandleScope scope; 300 | 301 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 302 | 303 | lcb_syncmode_t mode; 304 | if (args[0]->BooleanValue()) { 305 | mode = LCB_SYNCHRONOUS; 306 | } else { 307 | mode = LCB_ASYNCHRONOUS; 308 | } 309 | 310 | lcb_behavior_set_syncmode(me->instance, mode); 311 | return v8::True(); 312 | } 313 | 314 | v8::Handle CouchbaseImpl::IsSynchronous(const v8::Arguments &args) 315 | { 316 | if (args.Length() != 0) { 317 | return ThrowIllegalArgumentsException(); 318 | } 319 | 320 | v8::HandleScope scope; 321 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 322 | if (lcb_behavior_get_syncmode(me->instance) 323 | == LCB_SYNCHRONOUS) { 324 | return v8::True(); 325 | } 326 | 327 | return v8::False(); 328 | } 329 | 330 | v8::Handle CouchbaseImpl::GetLastError(const v8::Arguments &args) 331 | { 332 | if (args.Length() != 0) { 333 | return ThrowIllegalArgumentsException(); 334 | } 335 | 336 | v8::HandleScope scope; 337 | CouchbaseImpl *me = ObjectWrap::Unwrap(args.This()); 338 | const char *msg = lcb_strerror(me->instance, me->lastError); 339 | return scope.Close(v8::String::New(msg)); 340 | } 341 | 342 | void CouchbaseImpl::errorCallback(lcb_error_t err, const char *errinfo) 343 | { 344 | 345 | if (!connected) { 346 | // Time to fail out all the commands.. 347 | connected = true; 348 | runScheduledCommands(); 349 | } 350 | 351 | if (err == LCB_ETIMEDOUT && onTimeout()) { 352 | return; 353 | } 354 | 355 | lastError = err; 356 | EventMap::iterator iter = events.find("error"); 357 | if (iter != events.end() && !iter->second.IsEmpty()) { 358 | using namespace v8; 359 | Local argv[1] = { Local::New(String::New(errinfo)) }; 360 | iter->second->Call(Context::GetEntered()->Global(), 1, argv); 361 | } 362 | } 363 | 364 | void CouchbaseImpl::onConnect(lcb_configuration_t config) 365 | { 366 | if (config == LCB_CONFIGURATION_NEW) { 367 | if (!connected) { 368 | connected = true; 369 | runScheduledCommands(); 370 | } 371 | } 372 | lcb_set_configuration_callback(instance, NULL); 373 | 374 | EventMap::iterator iter = events.find("connect"); 375 | if (iter != events.end() && !iter->second.IsEmpty()) { 376 | using namespace v8; 377 | Local argv[1]; 378 | iter->second->Call(Context::GetEntered()->Global(), 0, argv); 379 | } 380 | } 381 | 382 | bool CouchbaseImpl::onTimeout(void) 383 | { 384 | EventMap::iterator iter = events.find("timeout"); 385 | if (iter != events.end() && !iter->second.IsEmpty()) { 386 | using namespace v8; 387 | Local argv[1]; 388 | iter->second->Call(v8::Context::GetEntered()->Global(), 0, argv); 389 | return true; 390 | } 391 | 392 | return false; 393 | } 394 | -------------------------------------------------------------------------------- /io/util/lcb_luv_yolog.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This file was automatically generated from '/home/mnunberg/src/yolog/srcutil/genyolog.pl'. It contains the macro 4 | wrappers and function definitions. 5 | */ 6 | /** 7 | * Initial version August 2010 8 | * Second revision June 2012 9 | * Copyright 2010-2012 M. Nunberg 10 | * See the included LICENSE file for distribution terms 11 | * 12 | * 13 | * Yolog is a simple logging library. 14 | * 15 | * It has several goals 16 | * 17 | * 1) Make initial usage and learning curve as *easy* as possible. It should be 18 | * simple enough to generate loging output for most cases 19 | * 20 | * 2) While simplicity is good, flexibility is good too. From my own 21 | * experience, programs will tend to accumulate a great deal of logging 22 | * info. In common situations they are just commented out or removed in 23 | * production deployments. This is probably not the right way to go. 24 | * Logging statements at times can function as comments, and should be 25 | * enabled when required. 26 | * 27 | * Thus output control and performance should be flexible, but not at the 28 | * cost of reduced simplicity. 29 | * 30 | * 3) Reduction of boilerplate. Logging should be more about what's being 31 | * logged, and less about which particular output control or context is 32 | * being used. Through the use of preprocessor macros, this information 33 | * should be implicit using a simple identifier which is the logging 34 | * entry point. 35 | * 36 | * 37 | * As such, the architecture is designed as follows: 38 | * 39 | * Logging Context 40 | * This is the main component. A logging context represents a class or 41 | * subsystem within the application which emits messages. This logging 42 | * context is created or initialized by the application developer 43 | * appropriate to his or her application. 44 | * 45 | * For example, an HTTP client may contain the following systems 46 | * htparse: Subsystem which handles HTTP parsing 47 | * sockpool: Subsystem maintaining connection pools 48 | * srvconn: Subsystem which intializes new connections to the server 49 | * io: Reading/writing and I/O readiness notifications 50 | * 51 | * Logging Levels: 52 | * Each subsystem has various degrees of information it may wish to emit. 53 | * Some information is relevant only while debugging an application, while 54 | * other information may be relevant to its general maintenance. 55 | * 56 | * For example, an HTTP parser system may wish to output the tokens it 57 | * encounters during its processing of the TCP stream, but would also 58 | * like to notify about bad messages, or such which may exceed the maximum 59 | * acceptable header size (which may hint at an attack or security risk). 60 | * 61 | * Logging messages of various degrees concern various aspects of the 62 | * application's development and maintenance, and therefore need individual 63 | * output control. 64 | * 65 | * Configuration: 66 | * Because of the varying degrees of flexibility required in a logging 67 | * library, there are multiple configuration stages. 68 | * 69 | * 1) Project configuration. 70 | * As the project author, you are aware of the subsystems which your 71 | * application provides, and should be able to specify this as a 72 | * compile/build time parameter. This includes the types of subsystems 73 | * as well as the identifier/macro names which your application will 74 | * use to emit logging messages. The burden should not be placed 75 | * on the application developer to actually perform the boilerplate 76 | * of writing logging wrappers, as this can be generally abstracted 77 | * 78 | * 2) Runtime/Initialization configuration. 79 | * This is performed by users who are interested in different aspects 80 | * of your application's logging systems. Thus, a method of providing 81 | * configuration files and environment variables should be used in 82 | * order to allow users to modify logging output from outside the code. 83 | * 84 | * Additionally, you as the application developer may be required to 85 | * trigger this bootstrap process (usually a few function calls from 86 | * your program's main() function). 87 | * 88 | * 3) There is no logging configuration or setup! 89 | * Logging messages themselves should be entirely about the message 90 | * being emitted, with the metadata/context information being implicit 91 | * and only the information itself being explicit. 92 | */ 93 | 94 | #ifndef LCB_LUV_YOLOG_H_ 95 | #define LCB_LUV_YOLOG_H_ 96 | 97 | #include 98 | #include 99 | 100 | typedef enum { 101 | #define LCB_LUV_YOLOG_XLVL(X) \ 102 | X(DEFAULT, 0) \ 103 | /* really transient messages */ \ 104 | X(RANT, 1) \ 105 | /* function enter/leave events */ \ 106 | X(TRACE, 2) \ 107 | /* state change events */ \ 108 | X(STATE, 3) \ 109 | /* generic application debugging events */ \ 110 | X(DEBUG, 4) \ 111 | /* informational messages */ \ 112 | X(INFO, 5) \ 113 | /* warning messages */ \ 114 | X(WARN, 6) \ 115 | /* error messages */ \ 116 | X(ERROR, 7) \ 117 | /* critical messages */ \ 118 | X(CRIT, 8) 119 | 120 | #define X(lvl, i) \ 121 | LCB_LUV_YOLOG_##lvl = i, 122 | LCB_LUV_YOLOG_XLVL(X) 123 | #undef X 124 | LCB_LUV_YOLOG_LEVEL_MAX 125 | } lcb_luv_yolog_level_t; 126 | 127 | #define LCB_LUV_YOLOG_LEVEL_INVALID -1 128 | #define LCB_LUV_YOLOG_LEVEL_UNSET 0 129 | 130 | enum { 131 | /* screen output */ 132 | LCB_LUV_YOLOG_OUTPUT_SCREEN = 0, 133 | 134 | /* global file */ 135 | LCB_LUV_YOLOG_OUTPUT_GFILE, 136 | 137 | /* specific file */ 138 | LCB_LUV_YOLOG_OUTPUT_PFILE, 139 | 140 | LCB_LUV_YOLOG_OUTPUT_COUNT 141 | }; 142 | 143 | #define LCB_LUV_YOLOG_OUTPUT_ALL LCB_LUV_YOLOG_OUTPUT_COUNT 144 | 145 | #ifndef LCB_LUV_YOLOG_API 146 | #define LCB_LUV_YOLOG_API 147 | #endif 148 | 149 | struct lcb_luv_yolog_context; 150 | struct lcb_luv_yolog_fmt_st; 151 | 152 | /** 153 | * Callback to be invoked when a logging message arrives. 154 | * This is passed the logging context, the level, and message passed. 155 | */ 156 | typedef void 157 | (*lcb_luv_yolog_callback)( 158 | struct lcb_luv_yolog_context*, 159 | lcb_luv_yolog_level_t, 160 | va_list ap); 161 | 162 | 163 | enum { 164 | LCB_LUV_YOLOG_LINFO_DEFAULT_ONLY = 0, 165 | LCB_LUV_YOLOG_LINFO_DEFAULT_ALSO = 1 166 | }; 167 | 168 | typedef enum { 169 | 170 | #define LCB_LUV_YOLOG_XFLAGS(X) \ 171 | X(NOGLOG, 0x1) \ 172 | X(NOFLOG, 0x2) \ 173 | X(COLOR, 0x10) 174 | #define X(c,v) LCB_LUV_YOLOG_F_##c = v, 175 | LCB_LUV_YOLOG_XFLAGS(X) 176 | #undef X 177 | LCB_LUV_YOLOG_F_MAX = 0x200 178 | } lcb_luv_yolog_flags_t; 179 | 180 | /* maximum size of heading/trailing user data between format specifiers */ 181 | #define LCB_LUV_YOLOG_FMT_USTR_MAX 16 182 | 183 | /* Default format string */ 184 | #define LCB_LUV_YOLOG_FORMAT_DEFAULT \ 185 | "[%(prefix)] %(filename):%(line) %(color)(%(func)) " 186 | 187 | 188 | enum { 189 | LCB_LUV_YOLOG_FMT_LISTEND = 0, 190 | LCB_LUV_YOLOG_FMT_USTRING, 191 | LCB_LUV_YOLOG_FMT_EPOCH, 192 | LCB_LUV_YOLOG_FMT_PID, 193 | LCB_LUV_YOLOG_FMT_TID, 194 | LCB_LUV_YOLOG_FMT_LVL, 195 | LCB_LUV_YOLOG_FMT_TITLE, 196 | LCB_LUV_YOLOG_FMT_FILENAME, 197 | LCB_LUV_YOLOG_FMT_LINE, 198 | LCB_LUV_YOLOG_FMT_FUNC, 199 | LCB_LUV_YOLOG_FMT_COLOR 200 | }; 201 | 202 | 203 | /* structure representing a single compiled format specifier */ 204 | struct lcb_luv_yolog_fmt_st { 205 | /* format type, opaque, derived from format string */ 206 | int type; 207 | /* user string, heading or trailing padding, depending on the type */ 208 | char ustr[LCB_LUV_YOLOG_FMT_USTR_MAX]; 209 | }; 210 | 211 | struct lcb_luv_yolog_msginfo_st { 212 | const char *co_line; 213 | const char *co_title; 214 | const char *co_reset; 215 | 216 | const char *m_func; 217 | const char *m_file; 218 | const char *m_prefix; 219 | 220 | int m_level; 221 | int m_line; 222 | unsigned long m_time; 223 | }; 224 | 225 | struct lcb_luv_yolog_output_st { 226 | FILE *fp; 227 | struct lcb_luv_yolog_fmt_st *fmtv; 228 | int use_color; 229 | int level; 230 | }; 231 | 232 | struct lcb_luv_yolog_context; 233 | 234 | typedef struct lcb_luv_yolog_context_group { 235 | struct lcb_luv_yolog_context *contexts; 236 | int ncontexts; 237 | lcb_luv_yolog_callback cb; 238 | struct lcb_luv_yolog_output_st o_file; 239 | struct lcb_luv_yolog_output_st o_screen; 240 | } lcb_luv_yolog_context_group; 241 | 242 | typedef struct lcb_luv_yolog_context { 243 | /** 244 | * The minimum allowable logging level. 245 | * Performance number so we don't have to iterate over the entire 246 | * olevels array each time. This should be kept in sync with sync_levels 247 | * after any modification to olevels 248 | */ 249 | lcb_luv_yolog_level_t level; 250 | 251 | struct lcb_luv_yolog_context_group *parent; 252 | 253 | /** 254 | * Array of per-output-type levels 255 | */ 256 | lcb_luv_yolog_level_t olevels[LCB_LUV_YOLOG_OUTPUT_COUNT]; 257 | 258 | /** 259 | * This is a human-readable name for the context. This is the 'prefix'. 260 | */ 261 | const char *prefix; 262 | 263 | /** 264 | * If this subsystem logs to its own file, then it is set here 265 | */ 266 | struct lcb_luv_yolog_output_st *o_alt; 267 | } lcb_luv_yolog_context; 268 | 269 | 270 | /** 271 | * These two functions log an actual message. 272 | * 273 | * @param ctx - The context. Can be NULL to use the default/global context. 274 | * @param level - The severity of this message 275 | * @param file - The filename of this message (e.g. __FILE__) 276 | * @param line - The line of this message (e.g. __LINE__) 277 | * @param fn - The function name (e.g. __func__) 278 | * @param fmt - User-defined format (printf-style) 279 | * @param args .. extra arguments to format 280 | */ 281 | LCB_LUV_YOLOG_API 282 | void 283 | lcb_luv_yolog_logger(lcb_luv_yolog_context *ctx, 284 | lcb_luv_yolog_level_t level, 285 | const char *file, 286 | int line, 287 | const char *fn, 288 | const char *fmt, 289 | ...); 290 | 291 | /** 292 | * Same thing as lcb_luv_yolog_logger except it takes a va_list instead. 293 | */ 294 | LCB_LUV_YOLOG_API 295 | void 296 | lcb_luv_yolog_vlogger(lcb_luv_yolog_context *ctx, 297 | lcb_luv_yolog_level_t level, 298 | const char *file, 299 | int line, 300 | const char *fn, 301 | const char *fmt, 302 | va_list ap); 303 | 304 | /** 305 | * Initialize the default logging settings. This function *must* be called 306 | * some time before any logging messages are invoked, or disaster may ensue. 307 | * (Or not). 308 | */ 309 | LCB_LUV_YOLOG_API 310 | void 311 | lcb_luv_yolog_init_defaults(lcb_luv_yolog_context_group *grp, 312 | lcb_luv_yolog_level_t default_level, 313 | const char *color_env, 314 | const char *level_env); 315 | 316 | 317 | /** 318 | * Compile a format string into a format structure. 319 | * 320 | * 321 | * Format strings can have specifiers as such: %(spec). This was taken 322 | * from Python's logging module which is probably one of the easiest logging 323 | * libraries to use aside from Yolog, of course! 324 | * 325 | * The following specifiers are available: 326 | * 327 | * %(epoch) - time(NULL) result 328 | * 329 | * %(pid) - The process ID 330 | * 331 | * %(tid) - The thread ID. On Linux this is gettid(), on other POSIX systems 332 | * this does a byte-for-byte representation of the returned pthread_t from 333 | * pthread_self(). On non-POSIX systems this does nothing. 334 | * 335 | * %(level) - A level string, e.g. 'DEBUG', 'ERROR' 336 | * 337 | * %(filename) - The source file 338 | * 339 | * %(line) - The line number at which the logging function was invoked 340 | * 341 | * %(func) - The function from which the function was invoked 342 | * 343 | * %(color) - This is a special specifier and indicates that normal 344 | * severity color coding should begin here. 345 | * 346 | * 347 | * @param fmt the format string 348 | * @return a list of allocated format structures, or NULL on error. The 349 | * format structure list may be freed by free() 350 | */ 351 | LCB_LUV_YOLOG_API 352 | struct lcb_luv_yolog_fmt_st * 353 | lcb_luv_yolog_fmt_compile(const char *fmt); 354 | 355 | 356 | /** 357 | * Sets the format string of a context. 358 | * 359 | * Internally this calls fmt_compile and then sets the format's context, 360 | * freeing any existing context. 361 | * 362 | * @param ctx the context which should utilize the format 363 | * @param fmt a format string. 364 | * @param replace whether to replace the existing string (if any) 365 | * 366 | * @return 0 on success, -1 if there was an error setting the format. 367 | * 368 | * 369 | */ 370 | LCB_LUV_YOLOG_API 371 | int 372 | lcb_luv_yolog_set_fmtstr(struct lcb_luv_yolog_output_st *output, 373 | const char *fmt, 374 | int replace); 375 | 376 | 377 | LCB_LUV_YOLOG_API 378 | void 379 | lcb_luv_yolog_set_screen_format(lcb_luv_yolog_context_group *grp, 380 | const char *format); 381 | 382 | /** 383 | * Yolog maintains a global object for messages which have no context. 384 | * This function gets this object. 385 | */ 386 | LCB_LUV_YOLOG_API 387 | lcb_luv_yolog_context * 388 | lcb_luv_yolog_get_global(void); 389 | 390 | /** 391 | * This will read a file and apply debug settings from there.. 392 | * 393 | * @param contexts an aray of logging contexts 394 | * @param ncontext the count of contexts 395 | * @param filename the filename containing the settings 396 | * @param cb a callback to invoke when an unrecognized line is found 397 | * @param error a pointer to a string which shall contain an error 398 | * 399 | * @return true on success, false on failure. error will be set on failure as 400 | * well. Should be freed with 'free()' 401 | */ 402 | LCB_LUV_YOLOG_API 403 | int 404 | lcb_luv_yolog_parse_file(lcb_luv_yolog_context_group *grp, 405 | const char *filename); 406 | 407 | 408 | LCB_LUV_YOLOG_API 409 | void 410 | lcb_luv_yolog_parse_envstr(lcb_luv_yolog_context_group *grp, 411 | const char *envstr); 412 | 413 | /** 414 | * These functions are mainly private 415 | */ 416 | 417 | /** 418 | * This is a hack for C89 compilers which don't support variadic macros. 419 | * In this case we maintain a global context which is initialized and locked. 420 | * 421 | * This function tries to lock the context (it checks to see if this level can 422 | * be logged, locks the global structure, and returns true. If the level 423 | * cannot be logged, false is retured). 424 | * 425 | * The functions implicit_logger() and implicit_end() should only be called 426 | * if implicit_begin() returns true. 427 | */ 428 | int 429 | lcb_luv_yolog_implicit_begin(lcb_luv_yolog_context *ctx, 430 | int level, 431 | const char *file, 432 | int line, 433 | const char *fn); 434 | 435 | /** 436 | * printf-compatible wrapper which operates on the implicit structure 437 | * set in implicit_begin() 438 | */ 439 | void 440 | lcb_luv_yolog_implicit_logger(const char *fmt, ...); 441 | 442 | /** 443 | * Unlocks the implicit structure 444 | */ 445 | void 446 | lcb_luv_yolog_implicit_end(void); 447 | 448 | 449 | void 450 | lcb_luv_yolog_fmt_write(struct lcb_luv_yolog_fmt_st *fmts, 451 | FILE *fp, 452 | const struct lcb_luv_yolog_msginfo_st *minfo); 453 | 454 | 455 | void 456 | lcb_luv_yolog_sync_levels(lcb_luv_yolog_context *ctx); 457 | 458 | /** 459 | * These are the convenience macros. They are disabled by default because I've 460 | * made some effort to make lcb_luv_yolog more embed-friendly and not clobber a project 461 | * with generic functions. 462 | * 463 | * The default macros are C99 and employ __VA_ARGS__/variadic macros. 464 | * 465 | * 466 | * 467 | */ 468 | #ifdef LCB_LUV_YOLOG_ENABLE_MACROS 469 | 470 | #ifndef LCB_LUV_YOLOG_C89_MACROS 471 | 472 | /** 473 | * These macros are all invoked with double-parens, so 474 | * lcb_luv_yolog_debug(("foo")); 475 | * This way the 'variation' of the arguments is dispatched to the actual C 476 | * function instead of the macro.. 477 | */ 478 | 479 | #define lcb_luv_yolog_debug(...) \ 480 | lcb_luv_yolog_logger(\ 481 | NULL\ 482 | LCB_LUV_YOLOG_DEBUG, \ 483 | __FILE__, \ 484 | __LINE__, \ 485 | __func__, \ 486 | ## __VA_ARGS__) 487 | 488 | #define lcb_luv_yolog_info(...) \ 489 | lcb_luv_yolog_logger(\ 490 | NULL\ 491 | LCB_LUV_YOLOG_INFO, \ 492 | __FILE__, \ 493 | __LINE__, \ 494 | __func__, \ 495 | ## __VA_ARGS__) 496 | 497 | #define lcb_luv_yolog_warn(...) \ 498 | lcb_luv_yolog_logger(\ 499 | NULL\ 500 | LCB_LUV_YOLOG_WARN, \ 501 | __FILE__, \ 502 | __LINE__, \ 503 | __func__, \ 504 | ## __VA_ARGS__) 505 | 506 | #define lcb_luv_yolog_error(...) \ 507 | lcb_luv_yolog_logger(\ 508 | NULL\ 509 | LCB_LUV_YOLOG_ERROR, \ 510 | __FILE__, \ 511 | __LINE__, \ 512 | __func__, \ 513 | ## __VA_ARGS__) 514 | 515 | #define lcb_luv_yolog_crit(...) \ 516 | lcb_luv_yolog_logger(\ 517 | NULL\ 518 | LCB_LUV_YOLOG_CRIT, \ 519 | __FILE__, \ 520 | __LINE__, \ 521 | __func__, \ 522 | ## __VA_ARGS__) 523 | 524 | #else /* ifdef LCB_LUV_YOLOG_C89_MACROS */ 525 | 526 | 527 | #define lcb_luv_yolog_debug(args) \ 528 | if (lcb_luv_yolog_implicit_begin( \ 529 | NULL, \ 530 | LCB_LUV_YOLOG_DEBUG, \ 531 | __FILE__, \ 532 | __LINE__, \ 533 | __func__)) \ 534 | { \ 535 | lcb_luv_yolog_implicit_logger args; \ 536 | lcb_luv_yolog_implicit_end(); \ 537 | } 538 | 539 | #define lcb_luv_yolog_info(args) \ 540 | if (lcb_luv_yolog_implicit_begin( \ 541 | NULL, \ 542 | LCB_LUV_YOLOG_INFO, \ 543 | __FILE__, \ 544 | __LINE__, \ 545 | __func__)) \ 546 | { \ 547 | lcb_luv_yolog_implicit_logger args; \ 548 | lcb_luv_yolog_implicit_end(); \ 549 | } 550 | 551 | #define lcb_luv_yolog_warn(args) \ 552 | if (lcb_luv_yolog_implicit_begin( \ 553 | NULL, \ 554 | LCB_LUV_YOLOG_WARN, \ 555 | __FILE__, \ 556 | __LINE__, \ 557 | __func__)) \ 558 | { \ 559 | lcb_luv_yolog_implicit_logger args; \ 560 | lcb_luv_yolog_implicit_end(); \ 561 | } 562 | 563 | #define lcb_luv_yolog_error(args) \ 564 | if (lcb_luv_yolog_implicit_begin( \ 565 | NULL, \ 566 | LCB_LUV_YOLOG_ERROR, \ 567 | __FILE__, \ 568 | __LINE__, \ 569 | __func__)) \ 570 | { \ 571 | lcb_luv_yolog_implicit_logger args; \ 572 | lcb_luv_yolog_implicit_end(); \ 573 | } 574 | 575 | #define lcb_luv_yolog_crit(args) \ 576 | if (lcb_luv_yolog_implicit_begin( \ 577 | NULL, \ 578 | LCB_LUV_YOLOG_CRIT, \ 579 | __FILE__, \ 580 | __LINE__, \ 581 | __func__)) \ 582 | { \ 583 | lcb_luv_yolog_implicit_logger args; \ 584 | lcb_luv_yolog_implicit_end(); \ 585 | } 586 | 587 | #endif /* LCB_LUV_YOLOG_C89_MACROS */ 588 | 589 | #endif /* LCB_LUV_YOLOG_ENABLE_MACROS */ 590 | 591 | #endif /* LCB_LUV_YOLOG_H_ */ 592 | 593 | /** These macros define the subsystems for logging **/ 594 | 595 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_READ 0 596 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS 1 597 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT 2 598 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET 3 599 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE 4 600 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP 5 601 | #define LCB_LUV_YOLOG_LOGGING_SUBSYS__COUNT 6 602 | 603 | 604 | 605 | 606 | /** Array of context object for each of our subsystems **/ 607 | extern lcb_luv_yolog_context* lcb_luv_yolog_logging_contexts; 608 | extern lcb_luv_yolog_context_group lcb_luv_yolog_log_group; 609 | 610 | /** Function called to initialize the logging subsystem **/ 611 | 612 | void 613 | lcb_luv_yolog_init(const char *configfile); 614 | 615 | 616 | /** Macro to retrieve information about a specific subsystem **/ 617 | 618 | #define lcb_luv_yolog_subsys_ctx(subsys) \ 619 | (lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_ ## subsys) 620 | 621 | #define lcb_luv_yolog_subsys_count() (LCB_LUV_YOLOG_LOGGING_SUBSYS__COUNT) 622 | 623 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 624 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 625 | #define log_rant(...) 626 | #else 627 | #define log_rant(...) \ 628 | lcb_luv_yolog_logger(\ 629 | NULL,\ 630 | LCB_LUV_YOLOG_RANT, \ 631 | __FILE__, \ 632 | __LINE__, \ 633 | __func__, \ 634 | ## __VA_ARGS__) 635 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 636 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 637 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 638 | #define log_trace(...) 639 | #else 640 | #define log_trace(...) \ 641 | lcb_luv_yolog_logger(\ 642 | NULL,\ 643 | LCB_LUV_YOLOG_TRACE, \ 644 | __FILE__, \ 645 | __LINE__, \ 646 | __func__, \ 647 | ## __VA_ARGS__) 648 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 649 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 650 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 651 | #define log_state(...) 652 | #else 653 | #define log_state(...) \ 654 | lcb_luv_yolog_logger(\ 655 | NULL,\ 656 | LCB_LUV_YOLOG_STATE, \ 657 | __FILE__, \ 658 | __LINE__, \ 659 | __func__, \ 660 | ## __VA_ARGS__) 661 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 662 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 663 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 664 | #define log_debug(...) 665 | #else 666 | #define log_debug(...) \ 667 | lcb_luv_yolog_logger(\ 668 | NULL,\ 669 | LCB_LUV_YOLOG_DEBUG, \ 670 | __FILE__, \ 671 | __LINE__, \ 672 | __func__, \ 673 | ## __VA_ARGS__) 674 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 675 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 676 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 677 | #define log_info(...) 678 | #else 679 | #define log_info(...) \ 680 | lcb_luv_yolog_logger(\ 681 | NULL,\ 682 | LCB_LUV_YOLOG_INFO, \ 683 | __FILE__, \ 684 | __LINE__, \ 685 | __func__, \ 686 | ## __VA_ARGS__) 687 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 688 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 689 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 690 | #define log_warn(...) 691 | #else 692 | #define log_warn(...) \ 693 | lcb_luv_yolog_logger(\ 694 | NULL,\ 695 | LCB_LUV_YOLOG_WARN, \ 696 | __FILE__, \ 697 | __LINE__, \ 698 | __func__, \ 699 | ## __VA_ARGS__) 700 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 701 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 702 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 703 | #define log_error(...) 704 | #else 705 | #define log_error(...) \ 706 | lcb_luv_yolog_logger(\ 707 | NULL,\ 708 | LCB_LUV_YOLOG_ERROR, \ 709 | __FILE__, \ 710 | __LINE__, \ 711 | __func__, \ 712 | ## __VA_ARGS__) 713 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 714 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 715 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 716 | #define log_crit(...) 717 | #else 718 | #define log_crit(...) \ 719 | lcb_luv_yolog_logger(\ 720 | NULL,\ 721 | LCB_LUV_YOLOG_CRIT, \ 722 | __FILE__, \ 723 | __LINE__, \ 724 | __func__, \ 725 | ## __VA_ARGS__) 726 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 727 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 728 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 729 | #define log_read_rant(...) 730 | #else 731 | #define log_read_rant(...) \ 732 | lcb_luv_yolog_logger(\ 733 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 734 | LCB_LUV_YOLOG_RANT, \ 735 | __FILE__, \ 736 | __LINE__, \ 737 | __func__, \ 738 | ## __VA_ARGS__) 739 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 740 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 741 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 742 | #define log_read_trace(...) 743 | #else 744 | #define log_read_trace(...) \ 745 | lcb_luv_yolog_logger(\ 746 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 747 | LCB_LUV_YOLOG_TRACE, \ 748 | __FILE__, \ 749 | __LINE__, \ 750 | __func__, \ 751 | ## __VA_ARGS__) 752 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 753 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 754 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 755 | #define log_read_state(...) 756 | #else 757 | #define log_read_state(...) \ 758 | lcb_luv_yolog_logger(\ 759 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 760 | LCB_LUV_YOLOG_STATE, \ 761 | __FILE__, \ 762 | __LINE__, \ 763 | __func__, \ 764 | ## __VA_ARGS__) 765 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 766 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 767 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 768 | #define log_read_debug(...) 769 | #else 770 | #define log_read_debug(...) \ 771 | lcb_luv_yolog_logger(\ 772 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 773 | LCB_LUV_YOLOG_DEBUG, \ 774 | __FILE__, \ 775 | __LINE__, \ 776 | __func__, \ 777 | ## __VA_ARGS__) 778 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 779 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 780 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 781 | #define log_read_info(...) 782 | #else 783 | #define log_read_info(...) \ 784 | lcb_luv_yolog_logger(\ 785 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 786 | LCB_LUV_YOLOG_INFO, \ 787 | __FILE__, \ 788 | __LINE__, \ 789 | __func__, \ 790 | ## __VA_ARGS__) 791 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 792 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 793 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 794 | #define log_read_warn(...) 795 | #else 796 | #define log_read_warn(...) \ 797 | lcb_luv_yolog_logger(\ 798 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 799 | LCB_LUV_YOLOG_WARN, \ 800 | __FILE__, \ 801 | __LINE__, \ 802 | __func__, \ 803 | ## __VA_ARGS__) 804 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 805 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 806 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 807 | #define log_read_error(...) 808 | #else 809 | #define log_read_error(...) \ 810 | lcb_luv_yolog_logger(\ 811 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 812 | LCB_LUV_YOLOG_ERROR, \ 813 | __FILE__, \ 814 | __LINE__, \ 815 | __func__, \ 816 | ## __VA_ARGS__) 817 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 818 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 819 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 820 | #define log_read_crit(...) 821 | #else 822 | #define log_read_crit(...) \ 823 | lcb_luv_yolog_logger(\ 824 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_READ,\ 825 | LCB_LUV_YOLOG_CRIT, \ 826 | __FILE__, \ 827 | __LINE__, \ 828 | __func__, \ 829 | ## __VA_ARGS__) 830 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 831 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 832 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 833 | #define log_iops_rant(...) 834 | #else 835 | #define log_iops_rant(...) \ 836 | lcb_luv_yolog_logger(\ 837 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 838 | LCB_LUV_YOLOG_RANT, \ 839 | __FILE__, \ 840 | __LINE__, \ 841 | __func__, \ 842 | ## __VA_ARGS__) 843 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 844 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 845 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 846 | #define log_iops_trace(...) 847 | #else 848 | #define log_iops_trace(...) \ 849 | lcb_luv_yolog_logger(\ 850 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 851 | LCB_LUV_YOLOG_TRACE, \ 852 | __FILE__, \ 853 | __LINE__, \ 854 | __func__, \ 855 | ## __VA_ARGS__) 856 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 857 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 858 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 859 | #define log_iops_state(...) 860 | #else 861 | #define log_iops_state(...) \ 862 | lcb_luv_yolog_logger(\ 863 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 864 | LCB_LUV_YOLOG_STATE, \ 865 | __FILE__, \ 866 | __LINE__, \ 867 | __func__, \ 868 | ## __VA_ARGS__) 869 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 870 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 871 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 872 | #define log_iops_debug(...) 873 | #else 874 | #define log_iops_debug(...) \ 875 | lcb_luv_yolog_logger(\ 876 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 877 | LCB_LUV_YOLOG_DEBUG, \ 878 | __FILE__, \ 879 | __LINE__, \ 880 | __func__, \ 881 | ## __VA_ARGS__) 882 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 883 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 884 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 885 | #define log_iops_info(...) 886 | #else 887 | #define log_iops_info(...) \ 888 | lcb_luv_yolog_logger(\ 889 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 890 | LCB_LUV_YOLOG_INFO, \ 891 | __FILE__, \ 892 | __LINE__, \ 893 | __func__, \ 894 | ## __VA_ARGS__) 895 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 896 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 897 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 898 | #define log_iops_warn(...) 899 | #else 900 | #define log_iops_warn(...) \ 901 | lcb_luv_yolog_logger(\ 902 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 903 | LCB_LUV_YOLOG_WARN, \ 904 | __FILE__, \ 905 | __LINE__, \ 906 | __func__, \ 907 | ## __VA_ARGS__) 908 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 909 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 910 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 911 | #define log_iops_error(...) 912 | #else 913 | #define log_iops_error(...) \ 914 | lcb_luv_yolog_logger(\ 915 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 916 | LCB_LUV_YOLOG_ERROR, \ 917 | __FILE__, \ 918 | __LINE__, \ 919 | __func__, \ 920 | ## __VA_ARGS__) 921 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 922 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 923 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 924 | #define log_iops_crit(...) 925 | #else 926 | #define log_iops_crit(...) \ 927 | lcb_luv_yolog_logger(\ 928 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_IOPS,\ 929 | LCB_LUV_YOLOG_CRIT, \ 930 | __FILE__, \ 931 | __LINE__, \ 932 | __func__, \ 933 | ## __VA_ARGS__) 934 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 935 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 936 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 937 | #define log_event_rant(...) 938 | #else 939 | #define log_event_rant(...) \ 940 | lcb_luv_yolog_logger(\ 941 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 942 | LCB_LUV_YOLOG_RANT, \ 943 | __FILE__, \ 944 | __LINE__, \ 945 | __func__, \ 946 | ## __VA_ARGS__) 947 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 948 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 949 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 950 | #define log_event_trace(...) 951 | #else 952 | #define log_event_trace(...) \ 953 | lcb_luv_yolog_logger(\ 954 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 955 | LCB_LUV_YOLOG_TRACE, \ 956 | __FILE__, \ 957 | __LINE__, \ 958 | __func__, \ 959 | ## __VA_ARGS__) 960 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 961 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 962 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 963 | #define log_event_state(...) 964 | #else 965 | #define log_event_state(...) \ 966 | lcb_luv_yolog_logger(\ 967 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 968 | LCB_LUV_YOLOG_STATE, \ 969 | __FILE__, \ 970 | __LINE__, \ 971 | __func__, \ 972 | ## __VA_ARGS__) 973 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 974 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 975 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 976 | #define log_event_debug(...) 977 | #else 978 | #define log_event_debug(...) \ 979 | lcb_luv_yolog_logger(\ 980 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 981 | LCB_LUV_YOLOG_DEBUG, \ 982 | __FILE__, \ 983 | __LINE__, \ 984 | __func__, \ 985 | ## __VA_ARGS__) 986 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 987 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 988 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 989 | #define log_event_info(...) 990 | #else 991 | #define log_event_info(...) \ 992 | lcb_luv_yolog_logger(\ 993 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 994 | LCB_LUV_YOLOG_INFO, \ 995 | __FILE__, \ 996 | __LINE__, \ 997 | __func__, \ 998 | ## __VA_ARGS__) 999 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1000 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1001 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 1002 | #define log_event_warn(...) 1003 | #else 1004 | #define log_event_warn(...) \ 1005 | lcb_luv_yolog_logger(\ 1006 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 1007 | LCB_LUV_YOLOG_WARN, \ 1008 | __FILE__, \ 1009 | __LINE__, \ 1010 | __func__, \ 1011 | ## __VA_ARGS__) 1012 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1013 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1014 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 1015 | #define log_event_error(...) 1016 | #else 1017 | #define log_event_error(...) \ 1018 | lcb_luv_yolog_logger(\ 1019 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 1020 | LCB_LUV_YOLOG_ERROR, \ 1021 | __FILE__, \ 1022 | __LINE__, \ 1023 | __func__, \ 1024 | ## __VA_ARGS__) 1025 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1026 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1027 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 1028 | #define log_event_crit(...) 1029 | #else 1030 | #define log_event_crit(...) \ 1031 | lcb_luv_yolog_logger(\ 1032 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_EVENT,\ 1033 | LCB_LUV_YOLOG_CRIT, \ 1034 | __FILE__, \ 1035 | __LINE__, \ 1036 | __func__, \ 1037 | ## __VA_ARGS__) 1038 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1039 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1040 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 1041 | #define log_socket_rant(...) 1042 | #else 1043 | #define log_socket_rant(...) \ 1044 | lcb_luv_yolog_logger(\ 1045 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1046 | LCB_LUV_YOLOG_RANT, \ 1047 | __FILE__, \ 1048 | __LINE__, \ 1049 | __func__, \ 1050 | ## __VA_ARGS__) 1051 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1052 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1053 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 1054 | #define log_socket_trace(...) 1055 | #else 1056 | #define log_socket_trace(...) \ 1057 | lcb_luv_yolog_logger(\ 1058 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1059 | LCB_LUV_YOLOG_TRACE, \ 1060 | __FILE__, \ 1061 | __LINE__, \ 1062 | __func__, \ 1063 | ## __VA_ARGS__) 1064 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1065 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1066 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 1067 | #define log_socket_state(...) 1068 | #else 1069 | #define log_socket_state(...) \ 1070 | lcb_luv_yolog_logger(\ 1071 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1072 | LCB_LUV_YOLOG_STATE, \ 1073 | __FILE__, \ 1074 | __LINE__, \ 1075 | __func__, \ 1076 | ## __VA_ARGS__) 1077 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1078 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1079 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 1080 | #define log_socket_debug(...) 1081 | #else 1082 | #define log_socket_debug(...) \ 1083 | lcb_luv_yolog_logger(\ 1084 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1085 | LCB_LUV_YOLOG_DEBUG, \ 1086 | __FILE__, \ 1087 | __LINE__, \ 1088 | __func__, \ 1089 | ## __VA_ARGS__) 1090 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1091 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1092 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 1093 | #define log_socket_info(...) 1094 | #else 1095 | #define log_socket_info(...) \ 1096 | lcb_luv_yolog_logger(\ 1097 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1098 | LCB_LUV_YOLOG_INFO, \ 1099 | __FILE__, \ 1100 | __LINE__, \ 1101 | __func__, \ 1102 | ## __VA_ARGS__) 1103 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1104 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1105 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 1106 | #define log_socket_warn(...) 1107 | #else 1108 | #define log_socket_warn(...) \ 1109 | lcb_luv_yolog_logger(\ 1110 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1111 | LCB_LUV_YOLOG_WARN, \ 1112 | __FILE__, \ 1113 | __LINE__, \ 1114 | __func__, \ 1115 | ## __VA_ARGS__) 1116 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1117 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1118 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 1119 | #define log_socket_error(...) 1120 | #else 1121 | #define log_socket_error(...) \ 1122 | lcb_luv_yolog_logger(\ 1123 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1124 | LCB_LUV_YOLOG_ERROR, \ 1125 | __FILE__, \ 1126 | __LINE__, \ 1127 | __func__, \ 1128 | ## __VA_ARGS__) 1129 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1130 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1131 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 1132 | #define log_socket_crit(...) 1133 | #else 1134 | #define log_socket_crit(...) \ 1135 | lcb_luv_yolog_logger(\ 1136 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_SOCKET,\ 1137 | LCB_LUV_YOLOG_CRIT, \ 1138 | __FILE__, \ 1139 | __LINE__, \ 1140 | __func__, \ 1141 | ## __VA_ARGS__) 1142 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1143 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1144 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 1145 | #define log_write_rant(...) 1146 | #else 1147 | #define log_write_rant(...) \ 1148 | lcb_luv_yolog_logger(\ 1149 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1150 | LCB_LUV_YOLOG_RANT, \ 1151 | __FILE__, \ 1152 | __LINE__, \ 1153 | __func__, \ 1154 | ## __VA_ARGS__) 1155 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1156 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1157 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 1158 | #define log_write_trace(...) 1159 | #else 1160 | #define log_write_trace(...) \ 1161 | lcb_luv_yolog_logger(\ 1162 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1163 | LCB_LUV_YOLOG_TRACE, \ 1164 | __FILE__, \ 1165 | __LINE__, \ 1166 | __func__, \ 1167 | ## __VA_ARGS__) 1168 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1169 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1170 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 1171 | #define log_write_state(...) 1172 | #else 1173 | #define log_write_state(...) \ 1174 | lcb_luv_yolog_logger(\ 1175 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1176 | LCB_LUV_YOLOG_STATE, \ 1177 | __FILE__, \ 1178 | __LINE__, \ 1179 | __func__, \ 1180 | ## __VA_ARGS__) 1181 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1182 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1183 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 1184 | #define log_write_debug(...) 1185 | #else 1186 | #define log_write_debug(...) \ 1187 | lcb_luv_yolog_logger(\ 1188 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1189 | LCB_LUV_YOLOG_DEBUG, \ 1190 | __FILE__, \ 1191 | __LINE__, \ 1192 | __func__, \ 1193 | ## __VA_ARGS__) 1194 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1195 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1196 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 1197 | #define log_write_info(...) 1198 | #else 1199 | #define log_write_info(...) \ 1200 | lcb_luv_yolog_logger(\ 1201 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1202 | LCB_LUV_YOLOG_INFO, \ 1203 | __FILE__, \ 1204 | __LINE__, \ 1205 | __func__, \ 1206 | ## __VA_ARGS__) 1207 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1208 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1209 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 1210 | #define log_write_warn(...) 1211 | #else 1212 | #define log_write_warn(...) \ 1213 | lcb_luv_yolog_logger(\ 1214 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1215 | LCB_LUV_YOLOG_WARN, \ 1216 | __FILE__, \ 1217 | __LINE__, \ 1218 | __func__, \ 1219 | ## __VA_ARGS__) 1220 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1221 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1222 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 1223 | #define log_write_error(...) 1224 | #else 1225 | #define log_write_error(...) \ 1226 | lcb_luv_yolog_logger(\ 1227 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1228 | LCB_LUV_YOLOG_ERROR, \ 1229 | __FILE__, \ 1230 | __LINE__, \ 1231 | __func__, \ 1232 | ## __VA_ARGS__) 1233 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1234 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1235 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 1236 | #define log_write_crit(...) 1237 | #else 1238 | #define log_write_crit(...) \ 1239 | lcb_luv_yolog_logger(\ 1240 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_WRITE,\ 1241 | LCB_LUV_YOLOG_CRIT, \ 1242 | __FILE__, \ 1243 | __LINE__, \ 1244 | __func__, \ 1245 | ## __VA_ARGS__) 1246 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1247 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1248 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 0)) 1249 | #define log_loop_rant(...) 1250 | #else 1251 | #define log_loop_rant(...) \ 1252 | lcb_luv_yolog_logger(\ 1253 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1254 | LCB_LUV_YOLOG_RANT, \ 1255 | __FILE__, \ 1256 | __LINE__, \ 1257 | __func__, \ 1258 | ## __VA_ARGS__) 1259 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1260 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1261 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 1)) 1262 | #define log_loop_trace(...) 1263 | #else 1264 | #define log_loop_trace(...) \ 1265 | lcb_luv_yolog_logger(\ 1266 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1267 | LCB_LUV_YOLOG_TRACE, \ 1268 | __FILE__, \ 1269 | __LINE__, \ 1270 | __func__, \ 1271 | ## __VA_ARGS__) 1272 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1273 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1274 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 2)) 1275 | #define log_loop_state(...) 1276 | #else 1277 | #define log_loop_state(...) \ 1278 | lcb_luv_yolog_logger(\ 1279 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1280 | LCB_LUV_YOLOG_STATE, \ 1281 | __FILE__, \ 1282 | __LINE__, \ 1283 | __func__, \ 1284 | ## __VA_ARGS__) 1285 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1286 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1287 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 3)) 1288 | #define log_loop_debug(...) 1289 | #else 1290 | #define log_loop_debug(...) \ 1291 | lcb_luv_yolog_logger(\ 1292 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1293 | LCB_LUV_YOLOG_DEBUG, \ 1294 | __FILE__, \ 1295 | __LINE__, \ 1296 | __func__, \ 1297 | ## __VA_ARGS__) 1298 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1299 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1300 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 4)) 1301 | #define log_loop_info(...) 1302 | #else 1303 | #define log_loop_info(...) \ 1304 | lcb_luv_yolog_logger(\ 1305 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1306 | LCB_LUV_YOLOG_INFO, \ 1307 | __FILE__, \ 1308 | __LINE__, \ 1309 | __func__, \ 1310 | ## __VA_ARGS__) 1311 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1312 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1313 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 5)) 1314 | #define log_loop_warn(...) 1315 | #else 1316 | #define log_loop_warn(...) \ 1317 | lcb_luv_yolog_logger(\ 1318 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1319 | LCB_LUV_YOLOG_WARN, \ 1320 | __FILE__, \ 1321 | __LINE__, \ 1322 | __func__, \ 1323 | ## __VA_ARGS__) 1324 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1325 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1326 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 6)) 1327 | #define log_loop_error(...) 1328 | #else 1329 | #define log_loop_error(...) \ 1330 | lcb_luv_yolog_logger(\ 1331 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1332 | LCB_LUV_YOLOG_ERROR, \ 1333 | __FILE__, \ 1334 | __LINE__, \ 1335 | __func__, \ 1336 | ## __VA_ARGS__) 1337 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1338 | #if (defined LCB_LUV_YOLOG_DEBUG_LEVEL \ 1339 | && (LCB_LUV_YOLOG_DEBUG_LEVEL > 7)) 1340 | #define log_loop_crit(...) 1341 | #else 1342 | #define log_loop_crit(...) \ 1343 | lcb_luv_yolog_logger(\ 1344 | lcb_luv_yolog_logging_contexts + LCB_LUV_YOLOG_LOGGING_SUBSYS_LOOP,\ 1345 | LCB_LUV_YOLOG_CRIT, \ 1346 | __FILE__, \ 1347 | __LINE__, \ 1348 | __func__, \ 1349 | ## __VA_ARGS__) 1350 | #endif /* LCB_LUV_YOLOG_NDEBUG_LEVEL */ 1351 | --------------------------------------------------------------------------------