Permalink
Browse files

Tree (Re)organization; Added type aliases for common C types; StructT…

…ype is gone, replaced now by Struct() generator; Most of node-ffi understands passing a struct type as a type in a list of types; errno support
  • Loading branch information...
1 parent f5a45e7 commit ab0b75a5aa79ff1e2df4d841ca924b044c3cddbe @rbranson rbranson committed Nov 15, 2010
Showing with 2,343 additions and 8 deletions.
  1. +71 −0 README.md
  2. +59 −0 example/sqlite.js
  3. +27 −0 lib/callback.js
  4. +34 −0 lib/cif.js
  5. +69 −0 lib/dynamic_library.js
  6. +25 −0 lib/errno.js
  7. +128 −0 lib/ffi.js
  8. +109 −0 lib/foreign_function.js
  9. +21 −0 lib/library.js
  10. +94 −0 lib/pointer.js
  11. +117 −0 lib/struct.js
  12. +5 −3 package.json
  13. +146 −0 src/callback_info.cc
  14. +229 −0 src/ffi.cc
  15. +143 −0 src/ffi.h
  16. +573 −0 src/pointer.cc
  17. +33 −0 src/threaded_callback_invokation.cc
  18. +455 −0 test/test.js
  19. +5 −5 wscript
View
@@ -0,0 +1,71 @@
+# node-ffi
+
+Rick Branson rick [at] diodeware [dot] com
+http://github.com/rbranson/node-ffi
+
+# DESCRIPTION
+
+node-ffi is a Node.js addon for loading and calling dynamic libraries using pure JavaScript. It can be used to create bindings to native libraries without writing any C++ code.
+
+WARNING: node-ffi assumes you know what you're doing. You can pretty easily create situations where you will segfault the interpreter and unless you've got C debugger skills, you probably won't know what's going on.
+
+# EXAMPLE
+
+ var FFI = require("node-ffi");
+
+ var libm = new FFI.Library("libm", { "ceil": [ "double", [ "double" ] ] });
+ libm.ceil(1.5); // 2
+
+ // You can also access just functions in the current process by passing a null
+ var current = new FFI.Library(null, { "atoi": [ "int32", [ "string" ] ] });
+ current.atoi("1234"); // 1234
+
+# REQUIREMENTS
+
+* Linux, OS X, or Solaris.
+* You will need node.js 0.2.0+
+
+# NPM INSTALL
+
+ $ npm install node-ffi
+
+# SOURCE INSTALL
+
+ $ git clone git://github.com/rbranson/node-ffi.git
+ $ cd node-ffi
+ $ node-waf configure build
+ $ node test.js
+ $ node-waf install
+
+# TYPES
+
+ int8 Signed 8-bit Integer
+ uint8 Unsigned 8-bit Integer
+ int16 Signed 16-bit Integer
+ uint16 Unsigned 16-bit Integer
+ int32 Signed 32-bit Integer
+ uint32 Unsigned 32-bit Integer
+ int64 Signed 64-bit Integer
+ uint64 Unsigned 64-bit Integer
+ float Single Precision Floating Point Number (float)
+ double Double Precision Floating Point Number (double)
+ pointer Pointer Type
+ string Null-Terminated String (char *)
+
+In addition to the basic types, there are type aliases for common C types.
+
+ byte unsigned char
+ char char
+ uchar unsigned char
+ short short
+ ushort unsigned short
+ int int
+ uint unsigned int
+ long long
+ ulong unsigned long
+ longlong long long
+ ulonglong unsigned long long
+
+# LICENSE
+
+See LICENSE file.
View
@@ -0,0 +1,59 @@
+var FFI = require("node-ffi"),
+ util = require("util");
+
+var SQLite3 = new FFI.Library("libsqlite3", {
+ "sqlite3_open": [ "int32", [ "string", "pointer" ] ],
+ "sqlite3_close": [ "int32", [ "pointer" ] ],
+ "sqlite3_changes": [ "int32", [ "pointer" ]],
+ "sqlite3_exec": [ "int32", [ "pointer", "string", "pointer", "pointer", "pointer" ] ],
+});
+
+var SQLite3Async = new FFI.Library("libsqlite3", {
+ "sqlite3_exec": [ "int32", [ "pointer", "string", "pointer", "pointer", "pointer" ], {"async": true} ]
+});
+
+// create a storage area for the db pointer SQLite3 gives us
+var db = new FFI.Pointer(FFI.Bindings.POINTER_SIZE);
+
+util.log("Opening test.sqlite3...");
+SQLite3.sqlite3_open("test.sqlite3", db);
+var dbh = db.getPointer(); // we have to extract the pointer as it's an output param
+
+util.log("Creating and/or clearing foo table...");
+
+SQLite3.sqlite3_exec(dbh, "CREATE TABLE foo (bar VARCHAR);", null, null, null);
+SQLite3.sqlite3_exec(dbh, "DELETE FROM foo;", null, null, null);
+
+util.log("Inserting bar 5 times...");
+
+for (var i = 0; i < 5; i++) {
+ SQLite3.sqlite3_exec(dbh, "INSERT INTO foo VALUES('baz');", null, null, null);
+}
+
+var rowCount = 0;
+var callback = new FFI.Callback(["int32", ["pointer", "int32", "pointer", "pointer"]], function(tmp, cols, argv, colv) {
+ var obj = {};
+
+ for (var i = 0; i < cols; i++) {
+ var colName = colv.getPointer().getCString();
+ var colData = argv.getPointer().getCString();
+ obj[colName] = colData;
+ }
+
+ util.log("Row: " + JSON.stringify(obj));
+ rowCount++;
+
+ return 0;
+});
+
+var fin = false;
+
+SQLite3Async.sqlite3_exec(dbh, "SELECT * FROM foo;", callback.getPointer(), null, null).on("success", function(ret) {
+ util.log("Total Rows: " + rowCount);
+ util.log("Changes: " + SQLite3.sqlite3_changes(dbh));
+ util.log("Closing...");
+ fin = true;
+ SQLite3.sqlite3_close(dbh);
+});
+
+setTimeout(2000, function() { });
View
@@ -0,0 +1,27 @@
+var FFI = require("./ffi");
+
+var Callback = module.exports = function(typedata, func) {
+ var retType = typedata[0],
+ types = typedata[1];
+
+ this._cif = new FFI.CIF(retType, types);
+ this._info = new FFI.CallbackInfo(this._cif.getPointer(), function (retval, params) {
+ var pptr = params.seek(0);
+ var args = [];
+
+ for (var i = 0, len = types.length; i < len; i++) {
+ args.push(FFI.derefValuePtr(types[i], pptr.getPointer(true)));
+ }
+
+ var methodResult = func.apply(this, args);
+
+ if (retType != "void")
+ retval["put" + FFI.TYPE_TO_POINTER_METHOD_MAP[retType]](methodResult);
+ });
+
+ this._pointer = this._info.pointer;
+};
+
+Callback.prototype.getPointer = function() {
+ return this._pointer;
+};
View
@@ -0,0 +1,34 @@
+var FFI = require("./ffi"),
+ util = require("util");
+
+// CIF proves a JS interface for the libffi "callback info" (CIF) structure
+var CIF = module.exports = function(rtype, types) {
+ this._returnType = rtype;
+ this._types = types;
+
+ if (!FFI.isValidReturnType(this._returnType)) {
+ throw new Error("Invalid Return Type");
+ }
+
+ this._argtypesptr = new FFI.Pointer(types.length * FFI.Bindings.FFI_TYPE_SIZE);
+ this._rtypeptr = FFI.Bindings.FFI_TYPES[this._returnType];
+
+ var tptr = this._argtypesptr.seek(0);
+
+ for (var i = 0, len = types.length; i < len; i++) {
+ var typeName = types[i];
+
+ if (FFI.isValidParamType(typeName)) {
+ tptr.putPointer(FFI.ffiTypeFor(typeName), true);
+ }
+ else {
+ throw new Error("Invalid Type: " + types[i]);
+ }
+ }
+
+ this._cifptr = FFI.Bindings.prepCif(types.length, this._rtypeptr, this._argtypesptr);
+};
+
+CIF.prototype.getArgTypesPointer = function() { return this._argtypesptr; }
+CIF.prototype.getReturnTypePointer = function() { return this._rtypeptr; }
+CIF.prototype.getPointer = function() { return this._cifptr; }
View
@@ -0,0 +1,69 @@
+var FFI = require("./ffi");
+
+// DynamicLibrary loads and fetches function pointers for dynamic libraries (.so, .dylib, etc)
+var DynamicLibrary = module.exports = function(path, mode) {
+ if (path == null && process.platform == "darwin") {
+ this._handle = FFI.DARWIN_RTLD_DEFAULT;
+ this.close = function() { }; // neuter close
+ }
+ else {
+ this._handle = this._dlopen(path, mode);
+
+ if (this._handle.isNull()) {
+ throw new Error("Dynamic Linking Error: " + this._dlerror());
+ }
+ }
+};
+
+DynamicLibrary.FLAGS = {
+ "RTLD_LAZY": 0x1,
+ "RTLD_NOW": 0x2,
+ "RTLD_LOCAL": 0x4,
+ "RTLD_GLOBAL": 0x8
+};
+
+// Close this library, returns the result of the dlclose() system function
+DynamicLibrary.prototype.close = function() {
+ return this._dlclose(this._handle);
+};
+
+// Get a symbol from this library, returns a Pointer for (memory address of) the symbol
+DynamicLibrary.prototype.get = function(symbol) {
+ var ptr;
+
+ if ((ptr = this._dlsym(this._handle, symbol)).isNull()) {
+ throw new Error("Dynamic Symbol Retrieval Error: " + this.error());
+ }
+
+ return ptr;
+};
+
+// Returns the result of the dlerror() system function
+DynamicLibrary.prototype.error = function() {
+ return this._dlerror();
+};
+
+
+DynamicLibrary.prototype._dlopen = FFI.ForeignFunction.build(
+ FFI.Bindings.StaticFunctions.dlopen,
+ "pointer",
+ [ "string", "int32" ]
+);
+
+DynamicLibrary.prototype._dlclose = FFI.ForeignFunction.build(
+ FFI.Bindings.StaticFunctions.dlclose,
+ "int32",
+ [ "pointer" ]
+);
+
+DynamicLibrary.prototype._dlsym = FFI.ForeignFunction.build(
+ FFI.Bindings.StaticFunctions.dlsym,
+ "pointer",
+ [ "pointer", "string" ]
+);
+
+DynamicLibrary.prototype._dlerror = FFI.ForeignFunction.build(
+ FFI.Bindings.StaticFunctions.dlerror,
+ "string",
+ [ ]
+);
View
@@ -0,0 +1,25 @@
+// Implementation of errno. This is a #define :/. On Linux, it's a global variable with the symbol
+// "errno", but on OS X it's a method execution called __error.
+
+var FFI = require("./ffi");
+
+if (process.platform == "darwin") {
+ var darwinErrorMethod = FFI.ForeignFunction.build(
+ new FFI.DynamicLibrary().get("__error"),
+ "pointer",
+ []
+ );
+}
+else {
+ var errnoGlobal = new FFI.DynamicLibrary().get("errno");
+}
+
+var errno = module.exports = function() {
+ if (process.platform == "darwin") {
+ return FFI.derefValuePtr("int32", darwinErrorMethod());
+ }
+ else {
+ return errnoGlobal.getInt32();
+ }
+};
+
Oops, something went wrong.

0 comments on commit ab0b75a

Please sign in to comment.