From 419ff16619cdbed3c2d1ea0be04c07b8d01b4155 Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Tue, 3 Apr 2012 03:25:59 -0600 Subject: [PATCH 01/11] Working version of Windows Registry Handler as a node module using node-ffi. Support for types REG_SZ, REG_DWORD and REG_BINARY with simple test driver. --- gpii/index.js | 5 + .../registrySettingsHandler/README.md | 9 + .../registrySettingsHandler/index.js | 5 + .../registrySettingsHandler/package.json | 19 + .../src/RegistrySettingsHandler.js | 374 +++++++++ .../test/testPayload.json | 18 + node_modules/node-ffi/.npmignore | 2 + node_modules/node-ffi/.travis.yml | 3 + node_modules/node-ffi/LICENSE | 19 + node_modules/node-ffi/README.md | 90 +++ node_modules/node-ffi/bindings.gyp | 43 ++ .../compiled/darwin/x64/ffi_bindings.node | Bin 0 -> 131756 bytes .../compiled/linux/ia32/ffi_bindings.node | Bin 0 -> 152172 bytes .../compiled/linux/x64/ffi_bindings.node | Bin 0 -> 159079 bytes .../compiled/sunos/ia32/ffi_bindings.node | Bin 0 -> 150768 bytes .../compiled/win32/ia32/ffi_bindings.node | Bin 0 -> 92160 bytes node_modules/node-ffi/deps/libffi/.npmignore | 18 + node_modules/node-ffi/doc/benchmark.js | 45 ++ node_modules/node-ffi/doc/benchmark.txt | 10 + .../node-ffi/example/factorial/README | 7 + .../node-ffi/example/factorial/factorial.c | 11 + .../node-ffi/example/factorial/factorial.js | 14 + .../node-ffi/example/factorial/wscript | 24 + node_modules/node-ffi/example/sqlite.js | 57 ++ .../node-ffi/example/structs/struct.c | 25 + .../node-ffi/example/structs/struct.js | 31 + node_modules/node-ffi/example/structs/wscript | 24 + node_modules/node-ffi/lib/bindings.js | 37 + node_modules/node-ffi/lib/callback.js | 41 + node_modules/node-ffi/lib/cif.js | 37 + node_modules/node-ffi/lib/dynamic_library.js | 93 +++ node_modules/node-ffi/lib/errno.js | 25 + node_modules/node-ffi/lib/ffi.js | 185 +++++ node_modules/node-ffi/lib/foreign_function.js | 109 +++ node_modules/node-ffi/lib/library.js | 33 + node_modules/node-ffi/lib/pointer.js | 121 +++ node_modules/node-ffi/lib/struct.js | 182 +++++ node_modules/node-ffi/package.json | 25 + node_modules/node-ffi/src/callback_info.cc | 156 ++++ node_modules/node-ffi/src/ffi.cc | 184 +++++ node_modules/node-ffi/src/ffi.h | 197 +++++ node_modules/node-ffi/src/foreign_caller.cc | 135 ++++ node_modules/node-ffi/src/node_async_shim.h | 42 ++ node_modules/node-ffi/src/pointer.cc | 705 +++++++++++++++++ .../src/threaded_callback_invokation.cc | 29 + node_modules/node-ffi/test.old/test.js | 348 +++++++++ node_modules/node-ffi/test.old/test.old.js | 711 ++++++++++++++++++ node_modules/node-ffi/test/buffer.js | 50 ++ node_modules/node-ffi/test/callback.js | 28 + node_modules/node-ffi/test/errno.js | 25 + .../node-ffi/test/foreign_function.js | 39 + node_modules/node-ffi/test/library.js | 114 +++ node_modules/node-ffi/test/objc.js | 42 ++ node_modules/node-ffi/test/pointer.js | 340 +++++++++ node_modules/node-ffi/test/struct.js | 212 ++++++ package.json | 19 + 56 files changed, 5117 insertions(+) create mode 100755 gpii/index.js create mode 100644 gpii/node_modules/registrySettingsHandler/README.md create mode 100755 gpii/node_modules/registrySettingsHandler/index.js create mode 100755 gpii/node_modules/registrySettingsHandler/package.json create mode 100644 gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js create mode 100644 gpii/node_modules/registrySettingsHandler/test/testPayload.json create mode 100644 node_modules/node-ffi/.npmignore create mode 100644 node_modules/node-ffi/.travis.yml create mode 100644 node_modules/node-ffi/LICENSE create mode 100644 node_modules/node-ffi/README.md create mode 100644 node_modules/node-ffi/bindings.gyp create mode 100644 node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node create mode 100644 node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node create mode 100644 node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node create mode 100644 node_modules/node-ffi/compiled/sunos/ia32/ffi_bindings.node create mode 100644 node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node create mode 100644 node_modules/node-ffi/deps/libffi/.npmignore create mode 100644 node_modules/node-ffi/doc/benchmark.js create mode 100644 node_modules/node-ffi/doc/benchmark.txt create mode 100644 node_modules/node-ffi/example/factorial/README create mode 100644 node_modules/node-ffi/example/factorial/factorial.c create mode 100644 node_modules/node-ffi/example/factorial/factorial.js create mode 100644 node_modules/node-ffi/example/factorial/wscript create mode 100644 node_modules/node-ffi/example/sqlite.js create mode 100644 node_modules/node-ffi/example/structs/struct.c create mode 100644 node_modules/node-ffi/example/structs/struct.js create mode 100644 node_modules/node-ffi/example/structs/wscript create mode 100644 node_modules/node-ffi/lib/bindings.js create mode 100644 node_modules/node-ffi/lib/callback.js create mode 100644 node_modules/node-ffi/lib/cif.js create mode 100644 node_modules/node-ffi/lib/dynamic_library.js create mode 100644 node_modules/node-ffi/lib/errno.js create mode 100644 node_modules/node-ffi/lib/ffi.js create mode 100644 node_modules/node-ffi/lib/foreign_function.js create mode 100644 node_modules/node-ffi/lib/library.js create mode 100644 node_modules/node-ffi/lib/pointer.js create mode 100644 node_modules/node-ffi/lib/struct.js create mode 100644 node_modules/node-ffi/package.json create mode 100644 node_modules/node-ffi/src/callback_info.cc create mode 100644 node_modules/node-ffi/src/ffi.cc create mode 100644 node_modules/node-ffi/src/ffi.h create mode 100644 node_modules/node-ffi/src/foreign_caller.cc create mode 100644 node_modules/node-ffi/src/node_async_shim.h create mode 100644 node_modules/node-ffi/src/pointer.cc create mode 100644 node_modules/node-ffi/src/threaded_callback_invokation.cc create mode 100644 node_modules/node-ffi/test.old/test.js create mode 100644 node_modules/node-ffi/test.old/test.old.js create mode 100644 node_modules/node-ffi/test/buffer.js create mode 100644 node_modules/node-ffi/test/callback.js create mode 100644 node_modules/node-ffi/test/errno.js create mode 100644 node_modules/node-ffi/test/foreign_function.js create mode 100644 node_modules/node-ffi/test/library.js create mode 100644 node_modules/node-ffi/test/objc.js create mode 100644 node_modules/node-ffi/test/pointer.js create mode 100644 node_modules/node-ffi/test/struct.js create mode 100644 package.json diff --git a/gpii/index.js b/gpii/index.js new file mode 100755 index 000000000..d4203ff74 --- /dev/null +++ b/gpii/index.js @@ -0,0 +1,5 @@ +var fluid = require("universal"); + +fluid.require("registrySettingsHandler", require); + +module.exports = fluid; \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/README.md b/gpii/node_modules/registrySettingsHandler/README.md new file mode 100644 index 000000000..6287b32c6 --- /dev/null +++ b/gpii/node_modules/registrySettingsHandler/README.md @@ -0,0 +1,9 @@ +The GPII personalisation infrastructure's "settings handler" which reads and writes to the Windows Registry. + +Run basic self-test with + +node node_modules\registrySettingsHandler + +Run a settingsHandler payload with, for example, + +node node_modules\registrySettingsHandler ../test/testPayload.json \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/index.js b/gpii/node_modules/registrySettingsHandler/index.js new file mode 100755 index 000000000..b3de34880 --- /dev/null +++ b/gpii/node_modules/registrySettingsHandler/index.js @@ -0,0 +1,5 @@ +var fluid = require("universal"); + +var loader = fluid.getLoader(__dirname); + +loader.require("./src/RegistrySettingsHandler.js"); \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/package.json b/gpii/node_modules/registrySettingsHandler/package.json new file mode 100755 index 000000000..d8ab2498b --- /dev/null +++ b/gpii/node_modules/registrySettingsHandler/package.json @@ -0,0 +1,19 @@ +{ + "name": "registrySettingsHandler", + "description": "A \"settings handler\" for the GPII architecture which reads and writes to the Windows ™ registry", + "version": "0.1", + "author": "GPII", + "bugs": "http://wiki.gpii.net/index.php/Main_Page", + "homepage": "http://gpii.net/", + "dependencies": {}, + "licenses": [ + { + "type": "BSD-3-Clause", + "url": "http://www.opensource.org/licenses/BSD-3-Clause" + } + ], + "keywords": ["gpii", "accessibility", "settings", "fluid", "IoC", "Inversion of Control", "configuration", "evented"], + "repository": "git://github.com/GPII/windows.git", + "main": "./index.js", + "engines": { "node" : ">=0.1.9" } +} \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js b/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js new file mode 100644 index 000000000..b4ac53728 --- /dev/null +++ b/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js @@ -0,0 +1,374 @@ +/*! +Windows Registry Settings Handler + +Copyright 2012 Raising the Floor - International +Copyright 2012 Antranig Basman +Copyright 2012 Astea Solutions AD + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/gpii/universal/LICENSE.txt +*/ + +"use strict"; + +var ffi = require("node-ffi"); +var fluid = require("universal"); + +var gpii = fluid.registerNamespace("gpii"); +var windows = fluid.registerNamespace("gpii.windows"); + +var NULL = new ffi.Pointer(0); + +var user32 = new ffi.Library('user32', { + 'MessageBoxW': [ + 'int32', [ 'int32', "pointer", 'pointer', 'int32' ] + ] +}); + +// Guide to node_ffi types and conversions: +// https://github.com/rbranson/node-ffi/wiki/Node-FFI-Tutorial + +var advapi32 = new ffi.Library("advapi32", { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724897(v=vs.85).aspx + // HKEY, LPCWSTR, DWORD, REGSAM, PHKEY + // Note abuse of type of argument 1 - node_ffi doesn't allow us to forge pointers from integers directly, + // as required by base keys + RegOpenKeyExW: [ + "int32", ["uint32", "pointer", "uint32", "uint32", "pointer"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724837(v=vs.85).aspx + // HKEY + RegCloseKey: [ + "long", ["pointer"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx + // HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD + RegQueryValueExW: [ + "int32", ["pointer", "pointer", "pointer", "pointer", "pointer", "pointer"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724844(v=vs.85).aspx + // HKEY, LPCWSTR, DWORD, LPWSTR, DWORD, REGSAM, LPSECURITY_ATTRIBUTES, PHKEY, LPDWORD + // Similar abuse to RegOpenKeyExW + RegCreateKeyExW: [ + "long", ["uint32", "pointer", "uint32", "pointer", "uint32", "uint32", "pointer", "pointer", "pointer"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724923(v=vs.85).aspx + // HKEY, LPCWSTR, DWORD, DWORD, const BYTE*, DWORD + RegSetValueExW: [ + "long", ["pointer", "pointer", "uint32", "uint32", "pointer", "uint32"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724847(v=vs.85).aspx + // HKEY, LPCWSTR, REGSAM, DWORD + RegDeleteKeyExW: [ + "long", ["uint32", "pointer", "uint32", "uint32"] + ] +}); + +var kernel32 = new ffi.Library("kernel32", { + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd319072(v=vs.85).aspx + // UINT, DWORD, LPCSTR, INT, LPWSTR, INT + "MultiByteToWideChar": [ + "int", ["uint", "uint32", "string", "int", "pointer", "int"] + ], + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130(v=vs.85).aspx + // UINT, DWORD, LPCWSTR, int, LPSTR, int, LPCSTR, LPBOOL + "WideCharToMultiByte": [ + "int", ["uint", "uint32", "pointer", "int", "pointer", "int", "pointer", "pointer"] + ] +}); + +// Windows API constants delved from the unfathomable deeps of windows.h +windows.API_constants = { + HKEY_CLASSES_ROOT: 0x80000000, + HKEY_CURRENT_USER: 0x80000001, + HKEY_LOCAL_MACHINE: 0x80000002, + HKEY_USERS: 0x80000003, + HKEY_CURRENT_CONFIG: 0x80000005, + CP_UTF8: 65001, + KEY_QUERY_VALUE: 1, + KEY_SET_VALUE: 2, + returnCodes: { + 0: "ERROR_SUCCESS", + 1: "ERROR_INVALID_FUNCTION", + 2: "FILE_NOT_FOUND", + 3: "PATH_NOT_FOUND", + 6: "ERROR_INVALID_HANDLE" + } +}; + +windows.registryTypes = {}; + +windows.registryTypes.REG_SZ = { + code: 1, + readConvert: function (pointer) { + return windows.fromWideChar(pointer); + }, + writeConvert: function (value) { + return windows.toWideChar(value); + } +}; + +windows.registryTypes.REG_DWORD = { + code: 4, + readConvert: function (pointer) { + return pointer.getUInt32(); + }, + writeConvert: function (value) { + var pointer = new ffi.Pointer(4); + pointer.putUInt32(value); + return { + pointer: pointer, + length: 4 + }; + } +}; + +windows.registryTypes.REG_BINARY = { + code: 3, + readConvert: function (pointer, count) { + return gpii.pointerToHex(pointer, count); + }, + writeConvert: function (value) { + return gpii.hexToPointer(value); + } +}; + +gpii.pointerToHex = function (pointer, count) { + var togo = ""; + for (var i = 0; i < count; ++ i) { + var nbyte = pointer.getUInt8(true); + togo += (nbyte < 16? "0" : "") + nbyte.toString(16); + if (i !== count - 1) { + togo += " "; + } + } + return togo; +}; + +// Slow but short method to parse very forgiving hex streams - any non-hex characters will be ignored. Digits +// forming a single byte must be adjacent +gpii.hexToPointer = function (hexString) { + var buffer = []; + for (var i = 0; i < hexString.length; ++ i) { + var c = hexString.charAt(i); + var digit = parseInt(c, 16); + if (!isNaN(digit)) { + buffer.push(digit); + } + } + if ((buffer.length % 2) === 1) { + fluid.fail("Odd number of hex digits in buffer " + hexString); + } + var togo = new ffi.Pointer(buffer.length / 2); + var writer = togo.seek(0); + for (var i = 0; i < buffer.length; i += 2) { + writer.putUInt8(buffer[i]*16 + buffer[i + 1], true); + } + return { + pointer: togo, + length: buffer.length / 2 + }; +}; + +var c = windows.API_constants; + +windows.convertReturnCode = function (code) { + return "Return code " + code + ": " + c.returnCodes[code]; +}; + +windows.checkReturnCode = function (code) { + if (code !== 0) { + fluid.fail(windows.convertReturnCode(code)); + } +}; + +windows.getBaseKey = function (baseKey) { + var key = c[baseKey]; + if (key === undefined) { + fluid.fail("Unknown registry base key " + baseKey); + } + return key; +}; + +var cr = windows.checkReturnCode; + +windows.toWideChar = function (string) { + var chars = kernel32.MultiByteToWideChar(c.CP_UTF8, 0, string, -1, NULL, 0); + + var buffer = new ffi.Pointer(chars * 2); + var chars2 = kernel32.MultiByteToWideChar(c.CP_UTF8, 0, string, -1, buffer, chars); + if (chars2 != chars) { + fluid.fail("Expected to write " + chars + " characters, only wrote " + chars2); + } + + return { + pointer: buffer, + length: chars * 2 + }; +}; + +windows.fromWideChar = function (buffer) { + var bytes = kernel32.WideCharToMultiByte(c.CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL); + var convertBuffer = new ffi.Pointer(bytes); + kernel32.WideCharToMultiByte(c.CP_UTF8, 0, buffer, -1, convertBuffer, bytes, NULL, NULL); + return convertBuffer.getCString(); +}; + +windows.readRegistryKey = function (baseKey, path, subKey, type) { + var converter = windows.registryTypes[type]; + if (!converter) { + fluid.fail("Unsupported registry type " + type); + } + var togo = { + statusCode: 200 + }; + + var pathW = windows.toWideChar(path).pointer; + var keyHolder = new ffi.Pointer(4); + var base = windows.getBaseKey(baseKey); + var code = advapi32.RegOpenKeyExW(base, pathW, 0, c.KEY_QUERY_VALUE, keyHolder); + if (code === 2) { // FILE_NOT_FOUND + togo.statusCode = 404; + return togo; + } + cr(code); + try { + var subKeyW = windows.toWideChar(subKey).pointer; + var dataLength = new ffi.Pointer(4); + var code2 = advapi32.RegQueryValueExW(keyHolder.getPointer(), subKeyW, NULL, NULL, NULL, dataLength); + if (code2 === 2) { // FILE_NOT_FOUND + togo.statusCode = 404; + return togo; + } + cr(code2); + togo.bytes = dataLength.getInt32(); + + var valueHolder = new ffi.Pointer(togo.bytes); + var code3 = advapi32.RegQueryValueExW(keyHolder.getPointer(), subKeyW, NULL, NULL, valueHolder, dataLength); + cr(code3); + + togo.value = converter.readConvert(valueHolder, togo.bytes); + } + finally { + var code4 = advapi32.RegCloseKey(keyHolder.getPointer()); + cr(code4); + } + + return togo; +}; + +windows.writeRegistryKey = function (baseKey, path, subKey, value, type) { + var converter = windows.registryTypes[type]; + if (!converter) { + fluid.fail("Unsupported registry type " + type); + } + var togo = { + statusCode: 200 + }; + var pathW = windows.toWideChar(path).pointer; + var keyHolder = new ffi.Pointer(4); + var base = windows.getBaseKey(baseKey); + // "int32", ["pointer", "pointer", "uint32", "pointer", "uint32", "uint32", "pointer", "pointer", "pointer"] + var code = advapi32.RegCreateKeyExW(base, pathW, 0, NULL, 0, c.KEY_SET_VALUE, NULL, keyHolder, NULL); + cr(code); + try { + var subKeyW = windows.toWideChar(subKey).pointer; + var converted = converter.writeConvert(value); + var code2 = advapi32.RegSetValueExW(keyHolder.getPointer(), subKeyW, 0, converter.code, converted.pointer, converted.length); + // TODO: consider plausible errors which might result from this process + cr(code2); + } + finally { + advapi32.RegCloseKey(keyHolder.getPointer()); + } + return togo; +}; + +windows.deleteRegistryKey = function (baseKey, path) { + var pathW = windows.toWideChar(path).pointer; + var base = windows.getBaseKey(baseKey); + var code = advapi32.RegDeleteKeyExW(base, pathW, 0, 0); + cr(code); +}; + +windows.registrySettingsHandler = function (payload) { + var key = payload.options.hKey; + var path = payload.options.path; + var results = fluid.transform(payload.settings, function(value) { + var oldValue = windows.readRegistryKey(key, path, value.path, value.dataType); + var writeStatus = windows.writeRegistryKey(key, path, value.path, value.value, value.dataType); + var newValue = windows.readRegistryKey(key, path, value.path, value.dataType); + return { + oldValue: oldValue.value, + statusCode: writeStatus.statusCode, + newValue: newValue.value + }; + }); + return { + results: results + }; + +}; + +function expect(expected, actual) { + if (actual !== expected) { + console.log("FAIL: retrieved value " + actual + " where " + expected + " was required"); + } +} + +function testSelf() { + // A self-test driver that reads and writes some values from the live registry + // Don't worry, this is perfectly safe! + + var value = windows.readRegistryKey("HKEY_LOCAL_MACHINE", "HARDWARE\\DEVICEMAP\\SERIALCOMM", "\\Device\\Serial0", "REG_SZ"); + console.log("Retrieved value " + value.value); + + var value2 = windows.readRegistryKey("HKEY_LOCAL_MACHINE", "HARDWARE\\DEVICEMAP\\VIDEO", "MaxObjectNumber", "REG_DWORD"); + console.log("Retrieved value " + value2.value); + + var value3 = windows.readRegistryKey("HKEY_LOCAL_MACHINE", "HARDWARE\\DEVICEMAP\\VIDEO", "ObjectNumberList", "REG_BINARY"); + console.log("Retrieved value " + value3.value); + + var dwordValue = 100; + windows.writeRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "Magnification", dwordValue, "REG_DWORD"); + var value4 = windows.readRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "Magnification", "REG_DWORD"); + console.log("Retrieved value " + value4.value); + expect(dwordValue, value4.value); + + var unicodeString = "I am Agent Γ"; + windows.writeRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "MagnificationAnnotation", unicodeString, "REG_SZ"); + var value5 = windows.readRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "MagnificationAnnotation", "REG_SZ"); + console.log("Retrieved value " + value5.value); // note that this may well display in the console incorrectly + expect(unicodeString, value5.value); + + windows.writeRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "MagnificationBinary", "0a, b0, cAfebAbe", "REG_BINARY"); + var value6 = windows.readRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier", "MagnificationBinary", "REG_BINARY"); + console.log("Retrieved value " + value6.value); + expect("0a b0 ca fe ba be", value6.value); + + // clean up after ourselves + windows.deleteRegistryKey("HKEY_CURRENT_USER", "Software\\GPIIMagnifier"); +} + +if (process.argv[1].indexOf("egistrySettingsHandler") !== -1) { + if (process.argv.length === 2) { + testSelf(); + } + else { + var filename = __dirname + "/" + process.argv[2]; + // Note that this is now possible! + // http://blog.gvm-it.eu/post/8175813806/node-js-0-5-2-load-json-files-with-require + var testFile = require(filename); + console.log("Executing payload: ", testFile); + var results = windows.registrySettingsHandler(testFile); + console.log("Returned results: ", results); + windows.deleteRegistryKey(testFile.options.hKey, testFile.options.path); + } +} + +//var OK_or_Cancel = user32.MessageBoxW( +// 0, windows.toWideChar(value), windows.toWideChar(path + "|" + subKey), 1 +//); \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/test/testPayload.json b/gpii/node_modules/registrySettingsHandler/test/testPayload.json new file mode 100644 index 000000000..880bb8091 --- /dev/null +++ b/gpii/node_modules/registrySettingsHandler/test/testPayload.json @@ -0,0 +1,18 @@ +{ + "options": { + "hKey": "HKEY_CURRENT_USER", + "path": "Software\\GPIIMagnifierTest" + }, + "settings": { + "magnification": { + "path": "Magnification", + "value": 100, + "dataType": "REG_DWORD" + }, + "magnificationString": { + "path": "MagnificationString", + "value": "Very Big", + "dataType": "REG_SZ" + } + } +} \ No newline at end of file diff --git a/node_modules/node-ffi/.npmignore b/node_modules/node-ffi/.npmignore new file mode 100644 index 000000000..59dd0416b --- /dev/null +++ b/node_modules/node-ffi/.npmignore @@ -0,0 +1,2 @@ +deps +out diff --git a/node_modules/node-ffi/.travis.yml b/node_modules/node-ffi/.travis.yml new file mode 100644 index 000000000..2d26206d5 --- /dev/null +++ b/node_modules/node-ffi/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - 0.6 diff --git a/node_modules/node-ffi/LICENSE b/node_modules/node-ffi/LICENSE new file mode 100644 index 000000000..10f8553c7 --- /dev/null +++ b/node_modules/node-ffi/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2011 Richard "Rick" W. Branson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/node_modules/node-ffi/README.md b/node_modules/node-ffi/README.md new file mode 100644 index 000000000..299647e2b --- /dev/null +++ b/node_modules/node-ffi/README.md @@ -0,0 +1,90 @@ +node-ffi +======== +### Node.js Foreign Function Interface + +`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. + +It also simplifies the augmentation of node.js with C code as it takes care of handling the translation of types across JavaScript and C, which can add reams of boilerplate code to your otherwise simple C. See the `example/factorial` for an example of this use case. + +**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 + +``` js +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, Windows, or Solaris. + * `libffi` comes bundled with node-ffi, it does not need to be installed on your system. + * The current version is tested to run on node 0.6.x. + * If you need node 0.4.x support, use the `0.4` branch of node-ffi. + +# NPM INSTALL + +``` bash +$ npm install node-ffi +``` + +Most popular operating systems have a pre-compiled binary that comes along with node-ffi, so most of the time you will not need to compile anything! (Unless of course you want to, then see below). + +# SOURCE INSTALL + +``` bash +$ git clone git://github.com/rbranson/node-ffi.git +$ cd node-ffi +$ "$NODE_SOURCE/tools/gyp_addon" -f make +$ make BUILDTYPE=Release +``` + +Note that the `NODE_SOURCE` variable needs to be set to the root dir of the node source tree. + +# 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 + ulonglong unsigned long long + size_t platform-dependent, usually pointer size + +# V8 and 64-bit Types + +Internally, V8 stores integers that will fit into a 32-bit space in a 32-bit integer, and those that fall outside of this get put into double-precision floating point numbers. This is problematic because FP numbers are imprecise. To get around this, the methods in node-ffi that deal with 64-bit integers return strings and can accept strings as parameters. + +# Call Overhead + +There is non-trivial overhead associated with FFI calls. Comparing a hard-coded binding version of `strtoul()` to an FFI version of `strtoul()` shows that the native hard-coded binding is 5x faster. So don't just use the C version of a function just because it's faster. There's a significant cost in FFI calls, so make them worth it. + +# LICENSE + +See LICENSE file. diff --git a/node_modules/node-ffi/bindings.gyp b/node_modules/node-ffi/bindings.gyp new file mode 100644 index 000000000..1bf4f3b9b --- /dev/null +++ b/node_modules/node-ffi/bindings.gyp @@ -0,0 +1,43 @@ +{ + 'targets': [ + { + 'target_name': 'ffi_bindings', + 'sources': [ + 'src/ffi.cc' + , 'src/callback_info.cc' + , 'src/pointer.cc' + , 'src/threaded_callback_invokation.cc' + , 'src/foreign_caller.cc' + ], + 'include_dirs': [ + 'deps/libffi/include' + ], + 'conditions': [ + ['OS=="win"', { + 'libraries': [ + 'deps/libffi/.libs/libffi.lib' + ], + 'dependencies': [ + 'deps/dlfcn-win32/dlfcn.gyp:dlfcn' + , 'deps/pthreads-win32/pthread.gyp:pthread' + ] + }, { + 'libraries': [ + 'deps/libffi/.libs/libffi.a' + ], + }], + ['OS=="mac"', { + 'xcode_settings': { + 'MACOSX_DEPLOYMENT_TARGET': '10.5', + 'OTHER_CFLAGS': [ + '-ObjC++' + ] + }, + 'libraries': [ + '-lobjc' + ], + }] + ] + } + ] +} diff --git a/node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node b/node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node new file mode 100644 index 0000000000000000000000000000000000000000..cd51159e01960adf5f918920103f567aac7d92b5 GIT binary patch literal 131756 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 W^MMVsIp09M1!|;uAC%vn`hNhFe0mcA literal 0 HcmV?d00001 diff --git a/node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node b/node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node new file mode 100644 index 0000000000000000000000000000000000000000..a5c20e566e271b036cc4f6e29de06697a1c6b76f GIT binary patch literal 152172 zcmeFa4O~>!)(3nBF{!8_qoSfBn+l7%pr}|@C=6e{!Jb(!!Mm6es{`~UB~&dix%P;ZYt@8j=%r^f%;A7|~g z*IIk+wfEU`nC={$WwY4~^V7lTU@*0Cf?=cr_Z$_Wic*aPqmz+nIE>R(xmlmM;wqc^ zc4Q*PQ00x|Y=-%5jWY~KykQ(B<)pa#W}E-piZs70kCbmLm@vN%RAj{%qC`#8Q&NZymcr{wx+;4^altl(X%p7S#fyS30UP9+ zunylwd|$zLBfi$pt4O>i8JmG`SPI{?TyMqozbxr(xPD*K9|1p>>ra56;k!f9{{}Y8 zwGa3WzTe`@aqu0!=H~}o?2^Qfz@PEmEos89a{U`{k6b(d`E$kS3Gdzhx1R<*Kjrbm z=4Xv>oWFHQ_O-DEpE+u(=gobt?z^KmZul{E%|Fi_cx3tXL3<{gbzj-2o{pxkZI3Nj zea2V!7M!)a{<%k16+e0DNj*0-NP^Ox=oT?c%)>bMQ(oY1;{q+`n~Bko^QI&#jV z@2xD_ICrzBq;bCIore}JKW1^C;p0AYycM5x?U#pjzIN-6Yd?#Ox+-yk^TsQ8|Js;S zcIV+cN1piRjwz=lzq{>*E8LXd`rIl z{*5m$-0`0Q`L}H9F*af3RgcX$B5~-r&nvU1%=$<6J85%XjLqnC>Wv!?n{i71m=2R( zes)0b%q;ga-`;V{yqqhi&YeDaLH)*GPCx#~yZ$)g#q?!|cYA!t&KbooJ#$>bbG5$-sYvbPdK)N$7N1YT%v-~YOL;0}$!;t#N9c7j(_iua`QoilP zKsoP^J|#FE#YW`A%a)_flwq)beF(j8nJFLEza-jB>GrGXz~#g8VR{NvIZRIoq1TrV zTs|!49AJJDaB-%Y05aXdA|J9c{&4&wAEuXE`b&COonV$~{%&KYe0X_d2tTieq~9|u z16hA%NcrU!d9nO^PIU5Nx=Tp^U*g0jAC`Y^(J#|KnT0^6KM1jxgpl?|9~ZcR{U3E~ zAkF(XhNL4y^p_hV|AG*CCWPoSHl)3oA@cYwB>iqkdyOIepKs9@tIi0K*9#%^?hfJi z(IW%><^AJA=+6kbe;RIvoRpsd)U%GFEc~z@2wE}&sg-u`e%pqKh;uB`nxRtjp=tn_&dQ;PI^l${+DTl z=Kc@wZ?=>(eS1jxa!a|Qcf9|0<^PWfk>}D7d%88m9xe~D|JaiQ^w|EaKvK!`35&h3 z+|sc`O(npf>~bVh~&=%l*0FKV}i; zA=owVPs4af#rlq)&c@+JFYxmQg3F6l`bgt3)StK*v8uG!1M@>Qg7M?z{`Y|CU+8$2 z^sWNEh4<;WpZx5C{HyEr+CY`V-WFmpP$u_BV?G#-1;H^wzaH|bUlZuxvq(3<38YAQ zU-UQO4%m(G7X^Qnyu&bVkn-NBpS>D=gxuNQVX%jpGq65H{bP(cqbuyS0rEIY>K}#v zEW{$?T}hWipXn&?E%bNe#+4R%PJ{eg*CP&+`bR=ut*wSZx5@Te;h&P{=`{<}4#|c5ebWe_e%Sp!hVdG1NN4N{_n8VKOg?s0ezwy;O_)u7uq$dU@y{s6ZAXJqOXyV z&qVYO;#Kn9jq$Mp_Irz@Z%6-Ef*wCH#&>&{vK-_2GNjqRd9c@3pJAMbH1#t8{Ipv5 zsY82K``d+d4D4%+)V~7tqb&NPT{ldGKau|Sz<6xEAA2TJ|19t`dZ-Y+2ievs$(S%!gbs{RfF z|C2$V_I*11VJi69KG>%A4_M!g`6qE$pnM3*A01*do|X3Fz~5#Ie|Mw2*&UE?7tqh% z37Lz3xCM%Ac-k;tm;PM``d0e_yGB~JzP~fP^dQ`q{%^x5koI=MURNMJT+(TZ3i~z3_u07LI^J(( ze1h@UA3%FO0se0^{6VA8{|bh`;xWWU(qH1}3d49XLbv}alNc#N&d(DA;tyqWgC-e&-f2%$=fo}bef%zsO3-H_+*oPa&2f`0WQh~uSvG0M06UGL4Y{aexA_IZZ!JlbK}$jciyp{z8o!j)g{ z%F8qIii%5%ky$V~pO^V1#Wxljd6!n@4J#a1T;VD#&nU^Ss3@#3@~#@%Ju7QquN+so ztE{rbIc(6-ZdGZ$`<9QZoKRTms&KLfa#B(TuDa5U;;dr+AQbc%kzZ0- zm^QrZvN6{c7Pxc)@_XuZ_mrHHvPu05UD;*DrD!*+yllciUVx{p;=+=#POuq53A_}S zjtex=Q)l!ZSzhca%yku|IkO9jvx`G+Oi2da?%fnS`rcmBW=e9PeUwz_u1d#xk(+Ua z>*JE6Oh}VS7|YYF2q+VR%ijI3*eXWS19C%qT7jUyI_`GV6{` zIkN{9C|R+6=j4LIiLTMd>6ZO;aNveakB-F0xig={B-Ff8xMW zNUXE~!q0(?YiV`sF|f4QRjf=r$Cd9YF374ZEnpWboV46P_mcBWE&2OrS}Da;W#qfc zl%PzmQc~EcSdnl{ielP1g|7DGk<&d7;!Nq4U5?>W?wZoq1)CmI=_+&@`C}@Ku_eeZ z1r*SffE80F;NL>3g#8LAus1l%nKEHaS&3hb$_CN`o$gm$Iu_&Q0D7C!(-h6{!U+>g z@?C}6KC*(Xz^_#}gPc5zFL0`ErR336dgox2kCP^9T%Eim7P_^(G%QMprxE1d8U5? zxs)D5^2;H(v4t2?W5HHCWW)Bb`>kKQifQ@mV|3sETFCa6&YwU$JOJ8%EHRRf!W=9VoEK{A6q!q99T+7n7GPH zQEO}=>J7xe%E{-yaIPlpVE!_?wQ(wGen;zcW>@8;swpz~u9R*xrF>Vx_~B(E5J%D=l5w+K zk;>BW&7^c2mp36FQ?PMAyG@&5*G}+Y+^v+#RxS*EMH~8A}Tp?(-@$wl)Oy(B`B+6e=bbi78E=r zkEC>D4s)KUS5*HKPpB4BYEg^Wbuu__(|gl9w24E^f?xx~U{N!Ue+iVF=l6GTy?!rh zt_ZqyQ&P#3crVWsHCCj0S}-g6Ljp5{teV&6q+q6d!NSymd12j;Q(oG85TP^n;JGLCNqY}=4H;ck|_Z(U|VN7AU3O|%LG5eX)4Xc5^ z3^|~_v44WNNvIcB6ffYk5F}vY5RIn_0td+3I(XX-y zF;Mtw%H(?L8hTw-a2>~Y3g%cRLmspT;=MSc3n#l&w31O-j)*F-Yho7mlu%DGNs-Eu z6a5ZxL2YMSbG}YV!P?Pui%!nZ)}?-%`*cPc1wriv%cq@C5si|B#-JB_Q|&ai4nh@wXOL&r+=jm^FmICjzJ{sE1-o?sBQd2X zEKnAhLkeBv%f^N?M5~K4r-`7sgLZr%P6jKs;7!j1k8ZmmKu|mOL-QwK(hD;9z{=Jt z(UhKMh!O|__52gqxKmC&m{{A$8c|SjHun9j#8Yz$3$G6^D-5)@G*sZ~D&wY}8HM{} z`7|@9JNnpe*UaELH4|$^umrB#i-t`jNa+bHH~{VX>!%FC;zVxHs{ymf^c4GA6;Zpv zv861#%oSfjS?O365Ht9e$a-G{RRkGy^VmYTp|U9<;d*GUYKM<7@=N?RRo*15+Ni~p zkWIt1EQIM5W(ksc!8y zqV|UnAcSEsmFvv#*2cNgAFl_;J5?03Uf;7HVd%CS(_k|!nC~z*`!{8RYnqXL2mu&} zUfl-Q4(9+u?(EjRNbm5frAA@7Wlh&sm8PAB&`_H>6tMYV1>Q@z^i5Y{C^6ThW#ukB zaT|--+$hq{-@raV!Ne(qib9t$;d*Q)Pr#D26n9>4fI?wum4SVT2?%~ladV+du27_} z^2&|z`4zY>o9Hs~)jpn41?Qf7enr{&J(-j-$#gQ)1-c}!a4dWi9_o#$sL;=0jXdtA zkea#Kem-~DjsAlN_RF~9ioEXUr|RpxZs&JDznlL$*?(oN5vZHuFHbqYJ03XWgU7%| z2la1+5vj1F`d1bDuObaTrspR@2@^YSsNbdt7p9kWzp z1j^)hK0w}~`DCfS#-nWcXwI293}?^+yf6n3=oKYi+y4LmlmxUSuEJRno=-`@*VT0{ z#Ce#p9C`fMAjM)SPe!`@#A2NEh%}y)yqMzRqbS!6{`wt_DN@G!U8?@!#+#BCtLjG? zA4>WdmF{GGDd~#SNWY`;t)w;m2tR#KnLZ=*so)O5`zv&QW~IgfRT}RW+#%R)lFqLd zd_%A!&brW!o)%1+qSH$Re-?D!sPpFtwhCt5r1KXDelIv+s?L8vaF^hv({%nq!Jh;N z->mZ=65K5~6hE#ae-8`(DwtiZ^B)o1BRFh^&R;6{hv4vAbpAgDdEkU}3k6>mJh4XS zR|vi?cy_JMpCS06V2@jM{v1J{V8(4a|9-)r1uvhe^B)uJ=+Wss!RG{zy-hvnKd93;Ez~%4k;WGu(s=e_jU67- z*s@IH#gA*eWQE4Or!-!>QsaX-P{wvTyrA*UMvWD(XuRunjXwxxy`j_h3C?{}r!Ux| z@e{#Y-qPu_-q!f4VChz!?);9%Rf1Q&tJ8l7KJ=bWXZ=g#*MhUR>2$aEHNGc!;|Dr@ z>W3QF2^M{%(?@))@o~XnpXl^Yf`9*1r!U^F@pHjjKhx=N1v`A9({lt9ztZU+n>C*6 z)A$L_dQq;aEgB!%sj=tx8vh|U>L;DPSt zL66{rg6jmE1UC!5Ex29qE5XEHG(V|=Lj}hQP7_@5tFHID;ManG`%ULxAh>joPQN4g zso?Q{==`IOv?)0b6dWe_bhOTYP4IzOou1Z3<76Csru@^+(YWedjT;5u6FfCZ=Z_Ga zA~;v@K0(`gx}Hn$9>EoY&kCM@M6#xLtl%kv*9u-QSS5Iqpj+^6!TEw834Sa1yI?vFuv6~C1V;;& z3--eSTb2(IoF;gu;1_~>1f#o)90aojuMpfQI4@P#dsFZy!OlH&{)vKP1@9I7NU&>9 zU2m1(dcijYw+enH__JWgUiv<}V4PsG;3a~C1xE^w61-0EE5W^j_B2iRbisjwR|~!- zcn=PQQl4)M{vvqX1v)=oaGc43U(LlBbY6Co8ThBlQZ@GT?KmzW(p1$93_}9 zSR#14;Jtzi1s@lDS@3ni-vy(by4~XiPZs=};3a}Zf{zO}2!1K}gJ5Kqrgxd(bivyM z=L|y!!Ht65`fEBD3icPACfF!=8O~JfVQYy3!XyI?}K&QB6d7EBc!EjU)NL~x?uWWlL|HG(q*>jmct zE)ZNOxKwbNAXp9iSup`?qxo1b+^g}<`!pU?id89M-xY$V0d4=pIeWoO!K(#vh*OnM z7Q97pmf+oj3k4q&d{OWn!DhibupVN2edlTHFE~uFKyaquY{5qbR|&o>_`2X$!EJ*5 z?h^WfqXmlve--459LP^c!LEX-f)@!661-e+gy0y#2L&G&TrK#L;0D1r1tad!?H()G zOVBAeNbm;8E8=+kK3DLOJ9PSyxfzXN|$W6tLqixKxSe*?-R`d~h&(l~Om#+Pr@nC#a00qf!W z(rk^d;9jQF?$qdoT{2ySbqw+1%^C-xJ*J;Qd&JuiM-x5YXsr4{hMZMt6nAd$8_e{!eup@2J;UMShU~GUWwaj&VbrK)C^J3p73{c=`R3 z|A59-g59w$WBof9YjiKsxcCu`IZHME@TkVESg*1E`sErQd_v>PPip+)X^l@jqp@tY zM)wOEpI)Q!ix)MX@v_E(291l?YTUX`V>;G%Y%c-pJL1+RjU6^={M)M)Oxa)hJw%-68j^D{sKfp6w8m|{T zmiB`D2=W0uk@^RA`j5sTf(r$I5bQy^_J@k*dJ{i)u5lCf%Jh>O4_~SAUFsG2 zcTumvCDbeM-8C9JzNFDfy&}JsdIfHvUV%SSufXf5SKy`8EAy#W;F;7bFqe7-&Z0bl zCvuztr%;c;U#LgmHPj#QJjxU3_*Ua6L6=}I$|43C#^{GN{vvoT#vb#JfG;7|uwFdg zr^9mK0;bQvxFl}CdmE@H;OEp6@EgJ1f}c`f$WP|D2VNq0so<4@&y!v{{2Fl#eveN% z0zYPY9Dct{{R3mEci=~YE{=1gucF?8dj#iG|40v^-ho{fYIJdYBmL46jqkD@q&sr_ z0ee$lz$w%h@VN$!KT%&upSxb;&PI)!sW0T8xlv;-^@X(W4UNBkq4BF78e6{7_*#p` zci4Z__k5>u1pFV<&FpuvVH``p2mC;=iT)4i>^U00q8yMuah}GTDG#Lc>6d})I6i=5 zDHq^jlnZc);7G~`>GLQb;B?9d_~T-YnoF^@8-jzSmep{UCiY z?cQk^R|rlNjG&&7KR~dWc8l~1!Mo~odLi|P{K7jl{wP>DSEpzET_a;a*4s_}0MC0! zqe1;3y+iQaB|4ohIQkKt&Y<2mSwWm4ef#SCnrTT=1Aq7gNv3-zZo^ zzlijF>K(XSaO@L0y^wZ*{P9m}tPyLVAVZM!~_fH_}<7aVhPN_tEZv3A8uhXpVE>y|g>vHrgHVi47X( zG--U5c8C05H))(lyF+@xYZ})JUO_uV{%?Yh(+-iY|59VZzct3})Oa!dGs?WPRMDRDk%ctu6e+d3Am@`f1 zKPtFaF!yGi|Cpd{x=xQ2TrSw5TIXLW_=I4`89M)J!6yYf-Jhx8DD+Q1C==|}5>jdL(*ZEa~ zTLjPX>iinPPXte#rSr1|&z+;w6Cc!Ay-;JrLmHD7Ym9nKW9u@F865wxpG=N>;8>1x zU;)Q9@Zo1QM!lf1^LmZ%H)?$D6)FEh<9T0c{JmM@KR7->XH5k#0q^c3U%-EnFJQ)E zji-?>q&v|+0k0xoz^lm@@FwyFypenXUm#z=)5sU`3yqmhWBb67Y#;b8+XwDt z``r=uuwCF6i!?s!0uDpJW@|j@E{&((t#Oaw>-Xt&tKd@JkNf_!$q z#=BcImXQyXzrgl>!n%X_gJHyTUW0GiOoVN3B|YFa(gV&SJ>Z$}Beu&7;~ZebJMiy< zuL{P|pCG?TaHZfNx6WT8xL5E>jxW@ITJR{e!Sp!6wSp(#s`D!a-xTc1@rC+s!H)%d zaeN{F4#5^dC&w4^9~AsWFo)v{`O5`6d38Ep@CCt5^*aCVJ2gHjSi=7I!tdk-qu6hx zM+iPF_`TqH^tUMgRPYM=S)^YVJemF#>0-gB1>@;okzX#jQSemySLC|{UlmNCe?@+k z;AX)t^s~sHEck|?gMJqIHwtbMJgZvgPZfMyFp+*1^=}q@NAR3mbbhtqdx8$?9rf=J zY!P%)|Hyw(@E5@x>L2;b1v^piNaqW_Ab1@0j{GvgO@e1o@5rAf_%Fc}>K*xB!7l|b zq5hG7pWqLIL#Th`|3lC=OQ+KW%LI?%xJ11ZKG*o>_Zla`E^Jwbv4L1_7{^Wm;+s!A z9dQXU1?x511L~Z2uf}f#A9!7-cMA@BL#H1Qod2dyXKc~)9DM}*Z8TR`vaXm`$LVd36_1N)5m_S@mayE zKhf#Ef{Q=Z>Hga_wg}GoOs97VCi`^yi?21Nw`jbC@Zw2W%7^PdOqTP5A*=2o9$lk=`x1fO14SopJ>35S&RlBArC} z0k;UcC`Y8@C_msD!O@f>(vg%OaH-&6$`R>S!FiM;(rJ_*@FT%$$`NS?4W^-jb1WVG4>+3juEIWz;FWji zv`_HLxjJ3>ca1*^c5T(^`vj-qe%ofOvv42rW5FS~pXp-UNAwAvg8P`R6MUNOXCiK4 zyTD`FF4KY!vR$N4o3Am4?IOKia2@X-i#VP41Lq39BiMy>m@oJ^=^=eS=>Z?{X&gg( zNWU#uI2&om>>i-)L&G=^^oTDCCcxI1{+8u?AaBF4in6q1_|M`nx7p%ikZ^>C23vr%>=?`&! zg;u*JjW;g%JzY`uzjEp?btpyjFZ`K;9+d967QKrnwZS=yNKg~Hq3>| ztOuOI{G%~Wi2uO8IB`1s3Cbdh45LJFmf+pQ>kQ))plvz$V0t|E;1B=S=@ThOO361@p z)Hvp8jl-VN_{S=ZMbBxxjPnuhoBpE46)$OQeOY4)=OdI)Sf_E(dX2RkG!`~#d~l=2 zH#cc4dQIae@MXMz+?N_%-)QWQu@`Y7;s@Rb?8p0n?{mJ($GrT2#$z9Zd~kg#Fv14E zM7$j1;%kiqneLDGHZl#I!?XkEnS2@#qh68D6YR7Q>8{W>{SEMCrd@`ylkx>FSgJAg zpBiJAX^eVY<4(#K^`f8Bn7C47N6HoX8&+%lRPZS1nfcek4kB_5!=PTT!TV}I*Z3Iq zn~e8lvVGv=)Gu)8qk_~g(mknP;PGr9coOvsyny-zo=^P(KOi5#e$+4UJn9#C4fPA0 zP5lDjcv<7o)GyM5s9&I)`UQ@oeu4d|U*I(A3AlWd#!IPRq+g$fxlCZz`2wo@C)`c(l8b?A9$i2 z{tGyseizu8ewRqU20T?Ti*o6LeTnBaj$!&d{QU>wWV|Q#S&g|YN4hWZEbKQl0Qqq-VpRhaM2c>cD|+2 zCs_BkPN!_u_^#lTcXaxccQvjREPPL=5C50O<${-Q)9D`t?|WaTFZw{^XM(jK>U7tS zG`=o)!^b*(+$S2J6TIeAoo3(IpCy6=Khxnv0uji0{;+vi2X%6 zi~R+DEjXL~MY93L7wIF|PvGN%!`NS>e-ivV`-}9&>@Vk%NMFQ$0zVV1Wq*vkv;+aw4rY=5U0Y=;@WmU&ZncF zHu!*l5NqIH&`;Y_hzE&xU_7CpHZS5Z^wail#7D&2F%O}iwq1CBhkn|?YZGy{VO)=X z+L~~F<$d5%j7#D??3<#WHt_Nr@e#!H=&$Wp#QC___OoI9oA`IEPti|Xonib9{j_7^h%3-f+f2j(pQE1-V|@S~Bf1%QPzyW(bcj)i3yJ-( zKSMkYsqA3!W`0Ty7s2ioefKR|o|aRKuuB91~m8+=UUcN#Ax zLblOAXtblRHqgHPN7Ubqc7FmMgL#kG7jYr+B&_cR&w>mh(jY&<{>1aKF2lVM(ReSu z;0uDgh!LEo;6H(eO=GR#X_$nWUoH58V1wY|4!XRoqsI3HkLje-*9-n5N~gyiq45pD zp3yp8FW4_er<(-xkJ0IMg1-w!bk_MF9ILS}&X<$VkK;6sKT+e+Cuyt_Tq*dR;Cm3bCZ7Z|Y)`z$!G zN{kzZ{MC34LY!q72}F$XG{LChIz3a+!Tc}bCj_IJkMu0TWack3j8x(@j5p>l1fN96 zw3_%5_PdDp!u?FgJIRklyh@D4{vNRc_f^}BPRNfWBAw&j83k8_2Rt*@Kik=6c-_19 z?%iA4;Of|X9&(!#;hUNle~y5!G3x5|tQh$0eL50}y^EtHYwzM{T#aYSw;fdWE{E*arjBHm9~9Zvp(G6@$xR*EF@BmlvP=B1 z!WK`aBeJO@#H&6{9pn60@&2m>|CPhMid0vcy%I+s?Q1 zzeRYDBR^;*9;_t0TWo88iSS-T7S?{z$@ZG(yRK`IV|%}L)AXZTvfb+h{@O zncg^eLuBt(&q1-8*BdU)ao^R9@qC(-_d3qrGk#8QC56JIuqq1*y8H_x!PU zZ{3}{@o(4n*8UOU{>Ik3wIV^w$=zV{sA_xXxmiQs)4R3&t5@74Z*w=sTz%~|>(PEr zGv-V*f4yox)_bSHR^N6V=XLMa?H^gE^3bcjTilJYS6{Q<8}AVQF+OJOL?B{#7dUp2 zEtjKDp|T85je{+vGC z9EXm*wif@+Xr%OLW9uS~dhd-#{@eu781|ZYu;}f-w|a7?+R7`?S?}}?=89GeBcp2H zw$Jv0H}A;Ex~Uj?nU1(187jra`4#876cSeh%H*{T_7Rg%cgA}t_0SyyF{508Zk2B2>&`_<4gm;G_?EP>)ip(8Gj%P|_A zO7~24j70}0dP*GQ{q5Zp)xmW=I@AX&E8`&RS7h7NpyM#qV(x<~Y3K#JZP9odSnz8c zHBGXf-Dr4}7Y(Dqv0gOjZY)8cit1ryrZ6}jaI?ccdmP6E%`cL~&{VR&juKF^U$pib z#DZ#Y!;buEoy`^F?We5GUW*@|AhQE{nE+qN zc|mp7ug>)*U*M63NTG2afv5vw(PKm1=t-h@67+Sv!?QtMd0C^x5lyma&_HuB$a8=o zE~8W(SbPma1EcmsSCqGdC*H9)n}P{a(`2a0H=IUZWq7YU^5+dCX0K^Qj@KzYnuRRy zgbuzBSP5lQ+OHU&~9cPx+19bB3gxA(~ zobtBs5z?l;!PBx^jP?PoB?c^~L;G$$jtK+_UnM|4dlEgB5Qf!}YK)j&TDo-x&GwK6E` zM!|t7JMx>_>)_Tunsv1V3~;j%6h(ocayu52^Uu8PF%YKlkcQv>-)WeF2=4I*!TL$Y zOcDioB?b3d2AEQ9B&R*Uc&DOjFvZV9Dbi4aWEez^!rKS6Zx4`~I4cXK#1OuIZ=VvE zK7Ka>HysaKvry`zhk|nC{%p<;!5u&B@-W8JXCKC+#YvC85313B3DqP29967V_Qh-G zzl7>Be~xO9nYPu|iGK;zxIaggu4^AQknopKJ?+m?-H*MV{g+TZ>(5c$kG-Dvmrzam zb5!?Zuc?0t)$V@@)r`M{YTBQpx}ULi$zMYC;y*`qKQ=Jn&r#iv1cv@Os{7$J_b;J( z&0j)w>_MQ)m?=0OxB~k?7N@R4D@(-MUciA!#^J?I6QW|oos_bbiX)_v>%+0G0t<6- z6dbXV`hign8_paQ%3(vAgF-oMSaVP)hYdmw3gxgt(?OveHW)c5l*0y32ZeIjpyZ%X z4jV)r6v|GzY%q0DD2EMF4hrS4LDfN_95z@vD3rqnR|kc1*eUyk zsT)IYYiCc%-8eX~gNWyu*t>Rkxy|QtBw|-91{*o5b#QfHDD^>uFU zZA;5D6JFwh0xSCXbVju$?M1rNqio)ER)`EPLmCSEQ5G8xkQvnAe@jv6#pSAu7UNz%a0d<;4qt8ecJ zKGF#HSjqCd2WMLR`?e;JU@FP2$lwHu;mwjKN6DZzioM6P8$Oj${eftk>@|EeVtt~6 zeY{bgw>;~c(p3kV(v_f_)N99@($yh>rgU{e0MEClI+6`n9BxVW48|i4+5RI590Ex6 zWW++*NuEJ5o|~ev37u$ii3*8OBOmi9>Im=oq=yU&XZ9LC0B`T{PBWwTAg%P_z1|u} zHSV&Mn39zeJZh7ts(db_9&w@FBzw&!Ja{o5b=52e4ayMQCE?Pr&*AgXHqU#8V^bI% z_boyp?H4+OMAk(Hp7ndw6Fqd4Tlaw#iNDt{rJp*fmkbLj4aHTp25n8^G<*V4U;15r?q+*jl0V6lv9P!>v z#{jS4&E6wV;9ZyFk)+uxKilcic~@@n?AsGl{wzQ$4L$U&|-Dl%IN@ z`+_{rbw%NM?c*vxy+g|q3yXpA8m-5xzxj+<>j?Zy#xsTr@XU zy(4)JXQ!KjnWM zkGkL_)z^`|x3~tvu`Ff{dLGVTbRuv={)x+M*+Qmr4R}w$Jvg%@vn%K>;!X*=pc|z= z>ars}>KF^A+PZok!cs@+Jcj!ZnHPV<&H z1}LJv*V}7w$eGjAaDd9==>SxY!i|w0|DgkG|Gmd0pznbp14K4 zQ%PTu@JpY^G(1Wii`iM<(Tt0{M^RUvpS!-fc5g&ona$l40oLQ|k}m1pTy<5~-59xV zHFtfZuFowvLDu^h`)%7%z4j-2?X$e?BHj$Pp1>skMgc z9_+U3793YZkB?ouJ%YB2VHD|+lRk^ABs@IyX?S{2ioStp3Em`r?N<>{NfZK=$hydT zy0)68>q{haKiO*%fR;)oWgj1NE1_#Yg0r8ng2C!BxXY0ZRgQ+rheL)}n3jU!E@xoW zZX!mH_FdK}fX-y9gU37=7J-5eeVARMoP|CBBcUOx$^N7Np*xqGiY@Jy93HhEpq}Zu zs3zsPaNXvp8_TpAp;?pZz`X2fg^dnI$FjlUaIkX)jyz(P*8TU#2j-359;8PJJ_!V; zacXb|;xlB$f?6EVV3s;10x6J193dU?aVjGY*Pd24TB1lbqBZ$4e?=PN0{c}dBvmp=Q{&zQv$iqvuQPp#Dhh;t>m@Mo$9Wz zxf?pTcSqU#t@HGcq=`zKI2+gXgSWn}OG0z^8@+$@bgGE+wA2R;XU>s0gWBRLi}mIj zb+>S!d-})JW$p2djP~?*czF>8GfwsNkFT4_V)y6M>muttmnC@m$GIEh{R=qo6lJch zqOrCz065wZIxY8sgG>sKB5(|Vh$!JDBEZg%os-J#{F zqWY8%Tb7X_|9HX>X+Pf155to;Jv!xW@Df`$7$zLeup{Qv5=YF(SXczHx4-CHpQBds z_haUH$|#mQt`K3^Y_TX;jemf9NibyS$X+?BS7Ux zg4ZwVJgSAD>j^8tmgUfYL(A-a=|2-uls7xlTySM$0yN#5Ilf>3wGG0tE8E%t{2b#t zl@)O8mIW8$LZ$CDroMT8)=IB(g_T~*{lShT&KKsiGZW7jQSQK?NyDJAR7*p46eu}Vj4&9!)=~_+Kv61p zkp{zeF#ODP42SU5^o>$KA#8Ygk@hRgg}@*%8KNi=gc9IR;>Z?bB2eP*Nrtms-LYsz z4NE741Nsr^-s~vIneD0~9wHa9V^_n|Y}waP{>EN&KSC_=IriEo&C>{66#r^*ecBLk1_7?b~0ON@23Ig@0iMu{P`7F zx8b_inN$fuST_4z(rER*! zHfsaANf|KyVF_DJRKFvry#%F@gvGL0R?}dwITI!P-NMZ`NjaM~BVegxQ+H$c|K`4^ z#8#l5K8i_;EnED7YPi8>%Zm1Gpu#x*QhxHy!*z%lS+iw%m`vrJtnq<+CwCh%;ZKIc zpY-8Qr&_v~z?WDfu>Mwk4-2pFtY{86Z;Wd=IuTe&g?N%ajO@@Jw*>~^SqnEjp)bEa zSBpL?heMZ&U{n?A9N-#NWslA5I*qs#}>AMaWBCp}Nna z1M2Y=523}iJZ}{au4R1-4>*=QFNcl||Mv0p<@Oc266@X_;mQv-f%uT& z6=qfI*Mq$#oAzmrtr|VHd>4uw`8;ZOMCF6hh2S9&3&qqb^T+S9)bJqF0mY%*UXu?> zP)(HY3hD!lnxe3bN$Nz2!xtg#!roWY_Cq?xdM)>b)fcy*IA88dUymP%zM!Vqn=jQJ z_Alzta147hE1;{lth$OZ^%VO)hL_TiO)Dsd3gQnEto_};f9 zX#Fe{c7yc;j$OAZ{bc*~)22sbKt&CmD{&$E(Y*}t$DdWdgM#(jTl9Nhuzsmr z{uZN}8g{>JxPLW7&)nH)qwDp5PS+McX&Ha^T2s#%`_S{SgR1At_oL_2P`*Ds^S7}| z&)-}0T)i(nn?t(ocyILtDSiA1a%Edm??qUI;v#U!ad|e;nU%0Q#jb zfPN!Ug7}@U#196u6{cT7eF&<)r&L436|E2b^Y#;VuGfPKu&bX+zw)H+H-BzFJ-1n1X3X&-a^>gIhgig zzF#2JPTG(6zM=No?(kx>%n}dCyB31xhbb~Yybc1EXyG8w4=J1tsdSd~^)vym0Tn=7qNNJLnv`~eKB*#t0>Xs#CEHCZv8br3dYR;Qh3uQ{BIPcypMYp81!rNjGirGWPe zd9Z^kyGLNFCBB|K;q_5`(~7sV=PkX#Wv{_sDm7%jsLX6nSFUrYC;RMl-6#5YgwuZB zR4nc~40?(R-7jO@J<${4mwcvUA{;@O7iw7Ts|Nb@uS?8%rwp0&KjG}`9+q)kLMa9B zJ09rTMDICIH2cIAq<%`Lw6Av+)c!Z-ogLigw(Z*N#9_pj6K zv-$Q2-`!-gDsR!%6Mb7^x?}NZ(Lg zK+k;u?d%K3w!Gx`AAe%M{E(5?WqdsOe_USnXJcF?_GjNdI5y^ww*&Sy3VuqSJD@Er zz2pIE$o_S}z`D0nLkuiJ8JMj9VP~oPva@H+iRv%2H!Z)H|9|3_V?2N zj{H_>`jnc1TO}7&RD3^J*&O`oKI~5sZV55KgE&8^F*PC@ z!dul@4c<0qDdPUy3Bvl^u;4yf!s>aXx83^TV3|$#@O_{M>v7;2jYS zaWV2{D5QgzosmkKh;CiEDnK&NY8=3vukdc9SQXLQR#|^P9D@1-XcB&1Z8pjGofb$%T z8$HV$O96QH+NV&?YtM3U;gYfmAswZX;91~Uz?v%^3kho+4-?FXv#1bq4C_l2hYMoX z<6sqj3^CaAvv)wGHzuWlyNl29SO8jy{oyg_B&Gn*V}Q}=$fb@DO{)bZ)8>Ik^eMr8 zBNlZv^N|bn@##M+Lk4@{c>HSgvdHq}2FoIc$L8le6Q>yFslNK(iOJspcy)L2tGR_t z>F(nE8O1cCkrLzqWwlO{^G^=6Yo56r#IsMwB7@J}14ohQQ}}3tynFjm&s{@GIdi!) z@W}RD-mKSaANu%nG4zmUyiMaouaa8yOAJM?CL99#9smCY{i=g#e}R4v^n6;-d6Iq3 zw;vlWt|txqvIHDjPD|KsqQhL17k?KOdLkMVK1 z8WQeB1+6DO-WFzPkYbCTF%G`G-v3{e*ZW#tIND&5*ZX1Q6|P3u^;cMVk)mH-gLzH~ zyIjM0ZVBfHc=|M*XO{fCV8LexZhG1EQEKz`HZ0L8dnJ?rY8Kh z0Qd8?u{E5MOwX+6E}l=x4%&aq24`VTI0g3KTr>T9B3nR@dm=ofB>Nsqn5DKg^{-un zR_%c|2Um{Q=apmRyt2Jk?c=F4%j6nAi{%e}=--2mr_se>O}q2 zCye$eNAtb8u37TV+{$RaFZU*NQuX^ks=spHqU|#}*X>H?XN%0KqmcDv5oPa$?60BR zf)uZgLAAWfITSN1cS56@^jX`WleV7h7*BSz{4AX(Z1KxT^Zlp%Nk8}LB~BEl1novc zv?`1dRNA)AQ)sW2U(+q3}a+6VKq`v3*t3| z&zdlN{$Jyn?Vu~UL=A-&&Y*`(ng)rz$2^+c(?p1?j2 zAN=wz|1G2NEI7C!?kxI?xjUv`7pb;|WcO&aW#=f_y!OAdHO$kNp!XN8_oz3m`QF`$ zO=^6=VkBPl;eUsmN@IJTLzCbI_`P zKUeQ6$Kr4GZ9^^f+CJRPx4!b3EOy9L|IBw8xQ_PRtlneL@`BoL2c^(6wn4w`#|Ad4 zC`np(RheZuTn8Rns@XmKet9sTkEne-%f37J+P(aZcdBMbzi3Sw58AH{G)T?zPjzx1 zj?cXP)%c1+zP>9C5{WW9$k%;Bc~B7Q2R4}*&Y<6m9SwU5_k5?xJA@|^?Wk^AN=-v0{|+UG z8|?@zs@JE@u{sWD1y%S+(r=@BRK~!X=tfKzW3toUgCG1}D_8vnO`Q`CFySqW7BjHv zQx011%FKh*?-?KC8E<_g!`5L4CIjA(f$KTUHy%AgyMe!RO8J|A=QII-|`*fTN4GZFl?<=kqxmg zQ{cX7nD4h<=BS4Mz;lwpaK%Zclk{FgXE+Ka%Bz~j9D`rCm~^#s#c^^x(>%iKe~lgl zGzwL4w|@x4;V!>D;0Blo56?$=bJR%SY-*h{@XHynk_IBkmRVp>3{;f>uSUYr%E{v; zQ1~^?C4}}d+f>25*D$pVJ@Gaz-%;oZ-zhTDYydP|L2nGR*Sy`1kJ(T?E#9!#+=OC` z48AE++t&J~be{V%@LU-d?2Lf{d!4bgX=fZ>sSD})ElxX^K6s{lnv4#jn9}Ckx8i|*cZGB^8?a`9Dzei|%D~7oX+CHWFCDko zLn^8zf*&%X-usD${Kah7!ntX5X)wAd(_P^O!VnOj!)gIt5h;JC7HtMSw__M+66gC5 zEj}0Tw~+CImuPx+Ti(+Jp@Ix043nm~(GiN?9O7*cn0Y$2JXP<1E^n_%rJvM)&lc~? z{*TW=KgehFCtDn%JU{ii)Vn8=&4GxHC72z<+s-?Nf8)EW7z&binfiy_0ILqoVFw*r z#~Mc>`;&<$@ByitjWb&USiTHLpU=#SggGTxm7=XI!Sc>~{vm8C9Gv^d1=@+#@4TOa zqbvS*-baFxMcWoCwNHYI|Ml;2__OBnSB`74K>*~{lXsBBA5MOlRrwnID0ZiPl8;+S z##>09sY&vcVSKB62m9|lSkHe5BgXv0#Wk-XKjlC5kfA>U*Dt*GA2WsB znw;u)@W=agaB~#jD{r6O(`164Qi%!gub-I_ANqw?Ec5&`e{zCgXlxFW&br3dr&?-=7NM{;&LfiodiySW@Ww^P#H9aN(FaMe6s3 zC#&~#VGRmD#`m+OnUgQnLF7tKK9DE*pZjspY<={iWzOq(pRR~-64?dF5rI0H#C9n>jI^Q^6u7_FOvw~0l^ z;@AR;RP)T!X?e>c%ENAz48w4&&-HwXzYOD^7G>DyHP}}*;2n*fy-vJ#mcLn*9M=)6 z*{JU@{EIUAHMcXWNEx)Q?Q!mMPut}uVz0Recj(*R#Ff{%3x8OHzwkC`o}scRJD$Kr z!Cks}voZDdA25P9zGL>s-S%hGBkj*N;IHKzzNIePhGspOE$|X(p>10yS*k8xDeH{l zEk(W8+52rlqR5jS>2~glY_Zj=JJ4ipA(|!)-H56mt_kp6*-3S#rQ>S!#@Mu8%dN?| zi%IviXahgwo)#TZb;N2OEcHExe@HktS~HoO;K@exgN!(^p)TTarVkgg9%EK|G$mAd zqQ0hRu7!%AJ8An$RS@NIwyKLrk8@|sxn87{WGmy;C2u=B=(aBI2|I0WXKSQ;+D@bL zP2QWk3)>pFU}gRpA+tXWfnhE>T8q`u8tO4<AE?jUAQ?6aky)#2R%x>%N4R&6ZgMt*qS55sNvLx!C1pf> zN%(cX>Q-&TWU#UHqFH;V{!%GM{4=UP-vc#P_rgDn=1d_#uI_2AhP{?A7E@~Wy^1TZ zvzfy#0hfWA+SqHns(crI57SN+R~c*##h!JmGtu00N07>Uo~9ojvEQ@KKDWX1R^nRE zM*B+~cc-EyvjwQqywrA=QO_($q|UjM-K-1Lg&})*=DH=aUfFh7B4`(X*J3nI9%|Et zoTEB!fkr&eJ?gGKUgJV%^rREK&OIDbd$7u~Z9z^|M;w)PbxB^rLSxdZwKb+~u` zfmhq^#_qFw_wTNYTL!TY?6tOlzKZ6B&Rvt@X;aO%Gn~876!y?|DK>PilT`!M*RI8s zT^O&GCL*ppyxF51MI|-ptzHTFy0`OBPk^zgj=a;fLF$8VCunl+P{Irvc)U3sQ;fal zJrwd*ZC9;|>I#x^?oiju)OEAEA=;bU?0Ksztl!<72v6plqi$}c#lWMv8=~CX`ImOk zKb`z}8#2WI+O|M$EoY*Wur0+iPHjtKPgmzo|FsR7yq{uA*5Z^K|Rl3hS9r-efJL8!6m%aaXeofC~R(1=nfR_3@ubr3CNv#2$H+_ z#sSL2Iyj^2vQJZ&KW{r75`a*5V1(LljiqX2Edk^U$hdhL-39yt)!VmGSKK3e(Ht{~ zYH4;)Yu;F00!J;9?uN(?&Rr4?D;>mK=xnY!oR_c)H8pvwm9?zvkW1U~)HB6$|3x(| ztM|Gp6|R`Mo_gk@UG&8|S0mg{(4NS>eUHbv9n;I&X8awC($qTtH@8)!hRdB6k?!_7cL#}Xcd+PohZS9`h_2NtI!MwxWT)q~u3J4H=-9{<(t*lQ z1n6!|xH>RvL|d(diC`<)gNechm+!Xp>;+x=r8se7f9Tf9(SQ4RN0$CNssod>u{H@iEg78D$lu;7r75B(Jd8; zAr<*}+Oa1Sf(cgq4HC=$mEksEsV>b7^UZuf2k zJS)AqtCj3mdvjO9hzrw+ z>_vUvJrmh#-LPHdz!Uj%-Y^-e@iT;ZPQ3;fLrMoE?rDvNy(UGuzo@$UBz1w##^Z1M z(5N?i@jvFzhRUA2LSHbfk%Q3VT9W4|X7#Yz&5mr*W)1~5`gu6r3pu7EtDzF?G%EST zw1BL>jP-L?4bHfZG`mslCovs;_2xFJ@qyOS=`HSQTMSo{S#^uQDoh_$w0#rD8g9#{134nNJtf0 z$DgjPhM+Es&~?lKrrwjJN|57mZUb*>fNT@*_mS86k;gfUEJJSFf`qRkP%I|G6dkU| zTuUNu#{(GMV=R#Gl%WN6e54%5EWe08Qf>%UXQ_*5FWg-2EVY`&#YeDg-w|pAKg?-W zSjMQ>;hwg`s64#MSq}voEf>z1wu5iptYXe-%;{=>@tPf;Ynl;j)Wt)^2tPV`l`F&o zY3kvp-04MQx%HSRaN%D+97gqH|rh$lNI% z+KLW+fjPl>uh%(Gg{*rnRHAZShm1M~Jom0`K218;`c@(8A!>zlJ3?Kck(LcwTNgT8 z{q_kpBD6A@o!1_-EhS>EXd0T?WfGd_C!{A4jL2R7tnLhmrQf?$zd;{n0AcJhFOWld z$Y09!o6G{vq&usxzE(vIBXVZ!qz$N6;X*OhE?^{zG?y2p-{Du0&V^q54=F5EYeh_@ zP0popxM{Tyljyn5r5&6wuVt97tdleo)=65=QQq8T^jj;@4BRLt4z6Gqv|tKesb;6u zYHC`8dzD{b>djrl`i*cu@UBhHnP?Ww#&B8EW(VS@pE#$h85soEs5!j>X9>Kwt2Gbs z9T&K4R(4N|waMa;+5ZoF?*d*`as3U?-sdEo zB!?uM1c-oeas!mRAfliIAzTzRA}Af`&=OO_C4?S|NhS>y3btJnl)?ItXZ?>vUeiz zH3B?_RX7C$514e~F@*9<%oAh_ODPw(q$vFcvntX7Kyc6-@q5+dy!Mmcs@j0-Z-wH%Ja5VgtVv z*)_=00{otC07sl=0DHG*05MVhFR*^;(%OG*{m`2BOkZK+e_;3Oso(S#{y%Q_?l?dz zXN+E`W#PfX16=nVEUZU1c1{`?9P9dLz2j_lTP*%PgYDd?;pQ7$<=E{`g@vQ*oj~~x z9sesv8vM0zMTM+s&cUECi_XJ(|LN0rvN77|9B}9E%748la3pq5;N(Tz6KD#pY6=J8 zcQ=Vqgqu+h=F#C&bz0SRGTdQu-1Rv-=s#Vbqer=8w03|f5{w~SIa;}Dy)7Lvg7BD) zk!<*%HjpDzbcdYU?A-@7Xt!!k;jBy}lemj6pJpO`iCeGLOTWWUB#wVG5`XewrVt+5&VC5~)7SMz`1evyu2i8&p84NsbL zH^`U__9eE%xO?wT>TmoN>{9Oq5o7m&*`YAqe5NKi!U@(%)l&{wuU*(!0cC_&KexRW zOqLeFa{=$`3y(2`7U>pZritN|V+uC=7y>jawsU?%KJ`gcgGQ=99cjd;ddL=F+2>y_ ziypSU-*nJ~Ht?6i;0Bq90zMfwK(NCj&bHqC;Mx&>SBshR;6XuDAR=1>t&(bmN7Ag{ zzCWmV$}1aF(QS-_g`1E`i5ls_N8BRazx8++d)%0LDG1KMoNBrLMnpR~dt==uCSu(t zPa#~Q16+M^^^cW4I$6iD7o4{Cn?hUE6b;%3w+IEWX1H^;01n6!5e?5oFU#3g#2v@k zmX9nS?<@<123bKja`L1o8@Pf-6db}mV4k>vpagx1uEDT^#?ot>$a!1!bodT&E9eGU z1MjHDssxSS7FxAUR?x^!mkpU{wq8N+jLC(&DW!K-W50z9+9t_C!AMSI1-&EU7-NmJ zL;Fp};iZb6wt_}`+!gd;EYP@uZr}>~FxvtpMNaGrn&@#$6?cCmb_E?>)56+dbV5!I zTtP$eAs<;m>u7WZy$zhjuAsL?nt>HG#EHR-(U02E=p&(3M`BjcMKS2zkU?ING!ZHb?$;>hR zrbC${bIcaKhM>y|T{s~|u|D4A+C{Z%j&-?q$(pl$m#h91munX)a~GVuq6<#Adb?!7 z*$~n223c^zC4^-3qHP^wrXQ*X_Ikdv@{EKOK1&qU?53D*Ng>Uop6*g(!)7HHQ^5RsWS`ybd^2e1JyihY5i)g8XqF z!tDlg9DVMqkG;vyWcDA6&Z-K*|79*QeOvE=3Yi8G0iB3je{iL4(tL$sMb< z%8rl8jBNUBT|avpEQ)OUzyNpP5kcIVM`(Z@GX^Co!w@EZ?NT!M0)JuJ_~-LNFR;p9 z@K^=0y&>U`3<=qvJ1AQ((hwo1hX}e^&kZA`VdXum1-M>X4V$mq8+w7QR>2Yt73;Ut zZ1nAa>BC++JG9?h?X5A)uG*=BnV4cm3cVn_ww?NLz%-*G)$9)nyxITeqda0b&Ob9& z>E0s3fMxAr+GuyR{RiHZz#E@g_HcB>A3^^RB@G?eRuU9cYz-Ubw{F5LC$ z;oQPq*g8quH+%Q`*{}wBo5YoV;odmWH_vn%PG3P}2HU*P$gDG>MlWK8CvnU%*w0FF zi>JeVHTjuwy;|{5=mp)*s(3Kk(sb3ovE9F2loDMINk_K()2h}=_8PP0Z}@=E^55+Go6`ReHvY8{NLzuK9oj~N zz!e;Qd7-VHdl%r*^t)@7OE`A_T+;ndJf;9SC2{?esb< zD>Mv82F!Bz%Jh+e$@!n&E~9HE+hvGg-mY`E%Mgh zfU9~gTO*TJy*p{~P=1Fj=ysr#J7w)^##UQw2JPR-!d>Oi)do3eh?L=^1yu%edNcen zgfJXCLaTPD3NA{-Q0hlPh6F zF$AzyWS*R4_=NUhmv#^CsaoKt$ zj75JWw;K3$XxCn>#N3rIa!@dm6IluGi7-D-x`P zAx>;8WQp+6XuX{6?{;t8au%pfvfjgT7Ij5e!eAT{6dM*jOFkMAR;JW)R93=AP0^T@ zFkG^<6&G+mk>e}9F|^anB+T+aF))Wr*=4=vnZ7eRdz_S+(cMbYC9p1(t5}UveixSc z+>63h4`~t9jQ74E5BVF7)L}5*f()UjmHDghSA%uF;|m$pB%knFt*@yPI3^zrWtauSG-Gd5JCGO zR%o~nS6jLBE}oZM&7KijmHy)L&XN(cop!uG24Kwn$G2oThGzZi`;ULt(=^|af_sGj z&2CwvY{2MaF6{ZEJFwX=d;Yq~*rbUlK@Ybach=ks@2B2p!8nI4I^-G+_x88Gd5&^b zkBcYA12vqGGvfhdpuNebt$2y6U_;G)4_>Y6wN9yx$cCUhf+9pz{okQH#@oY z=>@Y~5;h*@0`|ah`I~2jjUx*Wr`@?lR1CJK(Y^G9febTw02@YWY3t4wb^zu+HYRkl zd1$48KJ*Yuhh1OL*BCHdvbh`4U>+$;OHy#9LE~e90UrheckAFVR8MKLUW>&?hr`?- z-ywWKt#E_$P}(0M6s{FHPW5^N!ya*Y+nht;f+ znp;5v6x=r*Fr56p6<}OAIfgaV@lF^?$W2?(;b5Vkx`y&{D;b~N4t6_OxQ}_wo;OQi zIrd>|W!WbP*BAQt*1?JChxJ@x`igGy;z`wh46T@vdi+O7pGluF^k5^=C!sn_lN@|x z`WkR#j>^)i%2E7aVI8VRAOjLJgN)ny=+M9S&31=A@6E*Z4ewxxB#vi`0XSrKf&A-n z(L1fm7UgHdJ)C{~Jb{y{pjw#B>&@NH6KN~3!2-q)TaCPma9E$xakxOgT{KbAdTm(x z;XH_^8am0r+m8t|cza2lryNIlP=}5YeZ*Mf*lAuAGpJ$#Kr@31cSGX`m8mBM)#^c5 zFN%$+s-F6Pw0cT?oc`lJ00vM3n;4d4}FuK(jrKe&(Y z|In&B8I^TZvtkS`8LSwl2bgg+GSEH_)41_6F|;a61<%I#P!*W^vW`D3%7YDuBL@o` zshQ1}3edq@1xy~>1cc)oMRSiZ1KI)~HgqC+IS$4Ac!h+ix(>0X9m=1nZ8=mU9jXCj zT-|0YehxtY__1SIkBO`Kii2?n(q?nCc@WMYQ3LL|YC5nf(R3^|3q4qP)B+e|y5eWi z$*(C!l9s5hnN!hyxC_Va$vrYyn#>hQ{XULh8j+EOM}sqEB7oMQznb`h zSF|J0!Ds9knR=Vf;S!?B)C!*#b5oNA>xV9DuBP45{18$Tk-2U?Scq4BOu@1Nd@ZkV zaknrk{h|huzJEL4qS3|q0iDPzHXQqXV3Tn?&Nt-J?wTO8g-=`*U%YzCZmfHA5Ep%_ zsyp*Y9h+vdvDwUfb9dvCYpBUXi@P86Bt_Rp-X^r<1}(hR1TX$5tz3)TAf6>!SR;{5 z*ozohxCy89F6lTg7E6rAM_6ZbzwNCEfEsbx;9zd3p1w(3&P}q7;WyJd7(|^$pfw_D zW_VEN^s1Ns;PFhaiE&zLs1maMeEctv#r9*YN1C1%T7_XBT6MQdtNOF(XjS#}yY;3n z>WJIS-8hVL(OGX}fA@p+C%C_x)S3+;ZNY&scXPMkI0Or0v(K(KwWHG636EKXvI|QB ziR?zs$im%G>FoFy(uuB}Af27En1QNCV8ahY+gC3GXN}v_ z2jbcy>y=*Z!;*{+-~x6%7A&F(@#_^#NtiO$L|Ry{&miT3ab%-wji?GXx-fyl&D!$_ zdm8O}gj_@9Q9bw*4cbxU!t|^%X8Y!~9SEtQmP4^x?n7}Ie?_dHY#eY$IMO>t$ zQgOx`^^~xAC$q!6L%%nazGOoiN2WVsE)p0CgX>;q4 z)*kYHh~uxs1Ws``?w|pT6%Nr9hj!_8XWj4OJZ2Q7~FO+E=+g^eD4%J6`C?jc@%5m8pQxPY-)E z(al71k43-US^h})+U0($2|ldK3D>6%~v?l^e$*oQjED1+TW*3^KyD?9}SRMZ(rXO==#cq`w z_n0@~tYY3o1WUD=AkI4UT#8wSdF$5}0oQQ4B~stnyp*(=AX-1csh+-twY&lW>RJjh z7vjuMK4zzXp(ZR6dV#;HVl=AY?&FzJg10w!t6n>U)wF}8>y=7~q7h}A@pTsj;-`Ri zn-M~`BF(|kEYqj#a&E#zB%i{eB)GYm;D-j9Ybo#^YAQzX@|6|?+$vnm!usvT>drLw zBEHkMI2#`!toafn(5U}`*=Q3s&PWt50Bv$*o8AmZFp`RFb7}#mRq?inX%9u}wkNHs zM%0*j;?!gbH|gf)RB(u?ew$k$2q|miZL;aP&8b<-0a6N+l@8Vv-N&pNUa$PY=W>CWj=TI}F z?lO!Rw!UXEO1g;5ebPLb~k|Z}%dC#xWtv_l4Q645xzeYIR=tJ~~DpDTq zFxE?lqUB@Ge~hL2H*8s~4T&|9$BV^hF_sL&J?;g>f7eoKK3u2gWf|@)D+{f|aKYWt zYBn(S) zs3i4GgDWtywIvi^U$+HgSPtrVVXYKJ(M;7m#(hJ_WXgrL{_VY_qn*GcWlp!yy@Q}Ht!N5G#|`FHEf9wOwpNgBzzR_B zb7QBSpMGJQX96&;-K2@F?d@0sB0G8JQ(qP64>{}U1?tTKd`CxbxbZ~^2*kX}(Z>3p z!i7~+6uK3Q;5sEo-j86w$y>+bxOblaI!#qAGS#L8Q;+iS3bFkjioa;Z$&8)-|rsL>T>4?utY zg6AFFwuabGLMdqSv1QGuRtT&RPKt8iv^VZtP;YR@pIDgQ#V(7S17D9eyJ9q1Cm|!P zieVm83L5j=##|d}8HQ|Za3d2&MNZB5xtzEE_T>8dPjM`UV*#jtEB2~37NyDgH1hEI zkHRLN6`E1dbaP$$IS250J{)?ay>!g`Qj^yV0DOaul$(pHr@&l5QQJ;G!ffS5>duGo z%^euU&W9Nm;j?j_k06FG!1>ai>HIrH?=He|ob>!zpjf@?QuDG{1LA0cAs1s2C?5#4=$ znXFo1F6JXF-<*6ffg%4Hf)yzujpIK;R*mFwz$vh_adR68(hh9gd|1aKA31}r8Y!+~ zb>BgC8`lbwVGIbf3E9>jWNh`?BOpjPrq#%S2+Clukq|Qd5kBp?er+pKp%?gz&)$gt zBw5XgHS%|~VROx2zBg|un-0jT?tBF8T;FKbS&dv-&shV3@N0xYxDxGv_?a$BCEU23 zP#ly!!cZshH7Dm_0@M7*FIw?vobgD1w@1_0ZcP|r-n{Uev^eb*5+3mpz_u_xtThJARdFx+gG8I%y&amGA%*Fi{_y}e7640(9gU$1w|qASw8v99tV&%x7>E7U_?93 zFs~TS58_pH($yISIZg{}D3e@7wKTgw1Pv(_IJ1-zj-}(nV=**hS<;h%%TS&#kC zRlYFK!8s4XQptE^^J7Rw*g?r=tQ>HvhF2ebIQ@mm&|gDZpDyx*Bp;z%ffzb4%>BgC z2%&}ENn5)fP1#<6F2Q#M{U(B~w&u)Zw?Bo>A|#p%8#{}v!-EHBJ-{G*eSQcLzL^#^ z#$$x@i1BnKZNAkBwXlNvOQAf6?Z~T#ZO88*BV?7`?DAJJ0XahI;?^VwZL9e6VV}! zc&ktBF8*o)4<&;g5jkds>OK6_s!mLMM&S#P>pjiWb^IB|Cn5f<+Je|UsL#q=(+k;bK32T~jOvYK& ziR<5SzfFh1fiJ*{UcmH(UiErr9}O)&WQ7iT`Kg<{~dq$hXarGmla*tBd!7x@1L)*R|ZShwRu!5g+_K)QD*ab2oDxyVtZXR zqq`W#u>yx2mfRb8rUSI}X1Wsq!E~t_fnkAmLL!{|o9E-RU$Gy6<=fIEz%^13H+eNN zI3jUak|8Kaiaik4HDcWV2i3} z`|-KqOql3&m|1bX&Bs5X@Q{=q_?%m2yY9;fxj}T4>MxT8v>0>fDXg{YY^BGrh7rVZ zg;h`+0w}-nuVu*q{{r22w0a`MdDQUA&!}Qi(}uvHa;%5n1RV@18GN`y5NgSkftG28 zS#mWTRE$rKvQ?m#S-lkc04Sv0)I8`6V{3X-wawwxy1p0G*)zK}^cQqqlXvakD}Ek2 zkm;(lXf(Qof-PkW3R~gVXI)8ik6k?|*9kIgK0ui)-;-;7RqX}Kq8BH4Sr(pZ!mH}I zI<~|q8()8tDc@I8#;)jLy5JWN|5jW8hbHM#U zLyH*>EhOj;2TAE|?rj<&9E3Rn1x!qYL!`@++ER8Psg zhPSgSI)*;8)7FO2CGQ??t~@phk=yZ9;JF6RY&^wyCgZsj&sBIP;31sP&E|2-e9bfL z-teDb$eRbIy;z6OTRl_PoCm9o!{I4ezd5VzQIv6|c7wVGRhdrospwN;G@-voVp#yoievRve&7b@RKdtZLr}?*w zk2U{Et{k>B|1RcR|BT8B_pv7e_=3jE9}a>^3AMr{ShOTKLVkw~mtg355)2=vD&{Sm zyG&J-FDzRYlsr{YTDD|?{*~DL(z3ZhRa{naV<}iD1F4c_WML7o$`{>UazjupFI!X` zVc=>NVdI7=V~SMyvU!6blY#T*iD+(Iw4@kC%heTS!9dB9vWf)@qtY9w#?CD*y>afm znAZDNv20<*gChLD3!eAO2oh5e$^g58PN*v7{K? zdd=L@ijqK?&YU`T$%2xBD!;U}fhmS>l9x6|U3c zpN`WHcJWhV;-u{2cqC3tuTWNMPVhMOWIX-x| z=9z@h>h2l24{Hj3*QT^3gI&?6i!=%m!F zv%}$A5uSwk-+^#cPB?rN;mYCRa9)N|M-YxfcoN|Zgp)4_hpP~lBHTha!hHx=UWBbT zY~1x2iFy$3LO2cKs8OKLG{UV2Pa3CL^pu*nn^+!dZpka09{?geMVh9Ul(&?+W}0pog#p;Rb|Tag? zhg%S?yg3}s>W+GFfHV$a3-+&;B5Wvw9GHJgI9!kL$TG+Qp&!RV0sMKWhKg`_GD80f z$QR)#gd3Q?4e~%ZYeP8fN57XM3?N*Ia2Uc-8zC2j{qIG45$?JV?PY!~+RHS;OnkrJ ze?R&K;V6VN5UxacE5dyUw;*i!2J{SJk4@olE5gYSpx<%Q=l?eNN4OHcO$$WVI#s?gslj}2-EYGN@_-Vgc%5n z5cWa16ye18Ukbt!yF;acwQ~j5vr3AgVs?ps> zo_k(iP64v{?Db)NjWTLXs&B15Ce?qJXH07PI`5d&%uvGk)K<%LiItq1iM*Eqd=*#+ z$E12+8OL3AH!4BF&w^-xb5+YiaZ1C)42pX z%lAe5iqgX2krY__<>zuh^$s`u5GdCW|i*8q-f z8TrTdZAIQz>I?xrFfBRN2O+#XA!V+PD8=&iz^wzW+n)jC@vlcTAn!)R-SUI& zS#B}uJ8p<(<%PqwXO_zv%d?`r{u|H!>yKv~$_9If!<$V#>)3xG54&z*a;kr9st;Y~ zeRTry19*bKKMH()_$tcRnh0OS*#exRKH>14aX2gEaP|RbA8>Ap!Qs^P%h7dy#&yhYG{P)!qk9$S)?A0kBPu30?q-06JO6# z`?+|sB8QbI(}FVmgp(_WfPJ&+;E5^JyKTU^^&I^1LbL;VQ*fE9;}fCdKQKBzG35pm zo0M{ORPV8+qnc3uM89zObHq`8jvgbIueI;;*uT*uMEcO@RjRQ+_F;$zU5n|%m(hpF zkv^0@9|4pRz~43i`#A=Gtqt=KJMN+5cJ=AQfo%$+Y_Sy%C$UYSe45R`SoZwGI=wK^ zsq1@C{s8JuL!AEtc)A3ImE97Sz@DBrj*Xj5R zV(3qF={Ev@25v64gTE*SU;43tG!sD|_+@AA3ylAb9RC;@(1ED^78*yRAPFU}X9n z*WTrU9a-n~JP<_{y3>F=Y$EoOVSC&@c5&$mNYjiQ)&Mu_@^JVZ%|m=2uQ0fV-(A4% zG5O!aJq+BylyG=Rdvw2U=o)?S;@EH475^UYVBk)?G8{hd-=I4SxU+D!({9^tH9Q#o zy&Jf5u7ZEk9^F+27gnS%M7jsK!KvZ!o5UTTy4#w$)?Rv-hx%ZDW>9Gmh}#6*zSI6a zTtALHk4(phy)+N;9xViwM~ zaISd9@);TJxU-k0kf$P)pIC--JH+{qdBw;ZhdjUJz@I>PEAk4E_dN6H58h=@40$K6 z^U!aF+eZyUnHzQ716;BB}|t2`$aQ2&M&?UQ}%4ft*KrW zKy(K3Yfx?)^UZu5>obw|O5ijAXK*aed_7|mz7_BzfL};BTwgfM6GEOFS+(%C5Ac(K zUmORYFL3g61n?fgaJVrqrtZIz%^HYML0ab&|ieMk?t_$=OMrEndi)< zo*%=|ou*{t#it9ReC7(ANzp#G&(|D#Q#&e#@gG2uJt)8F_A~qS#5oL{Cg9wJxGVp` zcGNefUY-O_|F4I`I69Vp0sN-pO2Y``J;)sRn_4L++-2LVMK7iOz}W+wx6drsrJg=) z|EWv9@iIn&EdSY;VW^?}V_)CRdRO6mG(z*zjtQP&)+<<#6224gqSZKWWC@WO;C2JS zq6_tatIBZrl34id0v7l&zUlCE%QnDELpTGEgBJ*#HYXFxQBxHT7skS`4|zQK*2}T&90j~5 z*23n*@v%6<$1K3V1bj>^e3gL3)~^J-*E*cN!w-q{>C}*SoySw!;pH)}x^+?hJAu1m zV>tYYHwO1>g8Qa72AA?b1l)rA!{IApaXpuLS+m$bz?VFLwLE;~NWCROD?Q;9S^?Zw zS@mEzJT_Jqp3x#J(kcLa+g7x}7elL5XideEDXtBrz&)`8Iu^^<48iq#lU?L7DQv?Q z;C`_a>(+>k;CQ&)!!{7U7w~g;!FI;M`$yoshjY9{D=7T zM63*^h7v@hXNeRdI?Z(lB-;M*+CWJA0e|zKz&8Q@ zJm8#zBJwMW*1revrvRTD3%6%S>OTZ{E8w!1qUGBF{}AwVW6RIi@Bs2MQE?mK z`(xqP!8h1JoP#365x}|sRpWcPb!ZU?d2JYFTpJ%mI8B;EA#IPtxPe{yPVb9%gLQ z2G-RO4tI~my#{Sk(KZEuw+DFV{5-nG7oLEphnV(_4g=?hz}X7?hz{9Xgfa4U0`LL9 z!2Oz7xXag^C|_)29`3f?0^Cq6u3Z_C!8pJV0sc@N{M!+DG2m0*z*->|zCg<;fIQx* zn+^E0vG7@F&o_zlGOzZ@zf8}eB1#zdse+BC^sRwSLwEqN#Rwzi z#;l_?Rojrc8;aSACyam0b3?}2J=fvB8pA~(* zoT++*)Im-|5U2e662oiP`NDe{X|*D~d#F;MBfOW){}=yGpdIeNb>QIvJZJhBScCha z26*>XmRhqxsc#^3|52v==cZ2$@4wVikK_G1{%ag#sV8INPshafnKp=Uic_+NgNcRVZfa zx5lE3! z;&&B(Ebp)IYpaCcGiU$%_)W?3zRDvM|J)lFDeplxk4y|ELcK5yCb*B~{Tb2hT%V7l z!u#41N%?)$ckk;OGu`)f3?*-aPGy28VkLRsL5YM;L~Kl9F|W6kft3hxtB&5(EbrOY zTgXZ4f}ZpClsLFZ$Ax>aZ*kkt&h@9yuUMkI0n3lBvvbq&VknTat&IZBRL(j?h}wzO zC!yLP5*bz^&@-8zNz6=1G6n<&N#17scK$wzMC)jpEDKmW?x=E10 z4U{f>@nhx7D4@>NwE+|VZbw~R@j?w?BA;57wKTX)S$#xywWaDTltam4DalebpJ;QM z13S`{?X`-fxwW8!9h)vos6zZ`Sc*&1b2`TL?Zkk_wuM0_5=*c&MQ?yXyJq*H!f z<9=OZx~}nMU1KI{T+SL_ml|0c$9fZLkKD&(&=4+&x(tfjTc zN~C$PmR&?>u1chNu*wO5c}S#ru!1_3m`L+rRcsMh#5{EHK7@=8-pTm&(mYtp8I2!= z|Ax-nKJVpdk?r@6N8q~!Kc2x<4=ce_GtE*-J3!TUBeRE@?5)W5?PkvTCTAyel9@By z1T3>JNyS-tE*m+Pk5Nmf|=&rFS#ocX%GS zgmTv%?O{iI4zUwVM;j9!-x&$sL~ki@61}(Mw?hfh|Fuo>{MC<&Mmz{?Pjd#M8~Zbw z4L-g1eiu=n&s#%FoWxfobQB7ulF1+qUWzD(@|?u*^VYnK9M2$Za(kc1XS6XL(H}CJ z=p{#Yo9>W&+j9869--Z5hRVo_(>ehD2XR}dm!ei2c5(rjCS-Uj#+>(bqXA+ z#IdY9^>t869CtBtmA^Bxvl2(_M2IVtV$E?KREv)%6ayVX?7>uEnI6-ogA*aNN6q!n9`%VH? zB+^xRTGIfVM4r;UDVOaAdFL4rQO;-39Lu5Yww<-`Mm^3jIxWrw5KC~bAxY~Q0&PfgF71XooZrLSOmUvUko7x%#vBddpqZX=RS^u?(ge#0_W#A z9|GrG=Vs99=lldT`#Z0p6$6|dh!1qSb;nCt&M@E)c1|E?h_ep-oaej*Izyf7QEHe& zr~G{9Yw+ubJ70%nE^x*nXN0o=m=`(^0`np#6Sa(VcoZ?pnF>t&qSY5W%aC)4a}PKl z?M#QZ)G4PoDtk#ezek()E9X)$^RjZTKyW}g^FjU<k(T4azA%FaKOQg=oz$5P+pOl=EFsX;e;sF#D!*N)h~0Igf+N zuRshfJEWW{1aB#)6L|SGY6qKdE9XN5hn4dlSo)1}MxizDfC}pSt#bN8?nji<1EP9Y zIZ#yfJLP-~(f?jKd%#PRa;5|J9*U#2e^5>ibYRWvIPbIcJ0G7UeXe_@9-t04@59a(1HlUzPI+inl6fA6oPg zBms^7n{vKH@sE|0g5qt;Nd?VMl=Ch`{&xgW!B3Tw2x2FcGaA8X%DDq=_#A@+q`y$k zNHp`La=JnU|4`0%KiG$XbQx-#&Yff<{g%^6{5S-a&7`j$Z}SoSE?-ME%36| zaxMhQI?HJQmAfqGYRK$v%kdz!-g2Ht>K@DK3+AgWXA87>gXLtShK-ikzvZk)xo=obB^tKLavH(JX3I%J@POrT`{A3GGXxcFv78%F z{9BfD5(U0(IV&NM2QB9hU~nr6poZ^Q&H=?|0^%ef9T z|7-ExXi z!>8x~2;_vNJbOX3!;t6k+i4GeHu=)5m0&F6b`bQXTV__txDMj*b+%+#lIzXbh6ee% zn4~YG1}yoynxsF2>qcLeNv3D?W7)GzGBcwUedp_Dl35v#qGNsCO|nPE2jJP)!z2S4 zACOc}lg!K5Os;yFWZ#UZ(H398B>QK48?yK1SkHlfJ9lu#R!G5@XVpn^SVlV9@9S+^ zG$P|N@_epIj>@`s#u6qkH_15} z6--XH3Z-muh6kPIyTb6iFyl{bLXmYu>Md27_aWljeqX83!dj>3is-q>D96naY0z$%5pw zZ0LW|Z&5*VccYI<`_VtiJ#@;OGz}Fb_xwbp(vyOqn4C?I z6~0bVCrBqbN2juq-eDWh{ZOd%NV=9(`so_-l2)=@f1T=^bQ}6Vd4Nv!Px>XP3?!4# zI_)QRLPBKcQJvO-)pUD#;&sT&=nAU#ibUO^DkBH2w{JB`Z|C{N%Dznyv4PO}#+#LW zyGi-c2PE2MF@}r1T5`M0g_P||9eGpjc)|16=v4)xsT2)jA7kGB9%VNgG z5W9W9V1mbtu@uHr9fnHxQCy=@y36m#+zTBfz8Jk`|5OTe?FH;6%kJ#^0f=VQup7Gg zs)T2iF&~EA?rM_Wj31HLET5K?FJm*Cf0jx5Gkyr|x4Zc?lj)s{mn*xwZ;nvP>>Mmx zs_Y&nnUzt*7MyL0_Q?1n8{X3-0~zm=i(bN!otu}D!UhF=&!dbdcbA>9kM(8?HqxGq z{cKu}N$>V#RAI7!AZk#xCwGr0|)%-Ai?O5O&)>*Y`HE61|TD*y#n3%(9313c-rz{(Qr|%Gkrk3^z${#<$4b z1t#gscpJs-5hm%+=)ksIXp-p}E6Msr!aA2AS$?a)TFE;2HoJAHwSnlls&3XIzeU;h+sfJ{yR)}wrWxw7t}0rS*u ztL%YorMsDL*DmaFV+B&{rCV#~^eDx5T&#OU1!{}2MUJvdm9JW&txgY0?R)WPeFCW7!0=llkRp88yK ze@lvP|H7Ou$a$2xx4_D#$SA~aBL4M!Edj;v^UKUiR!UFe+zjjtfa;N*`}~HTUQ2Fd zN;Bcjy9Be9U1O57a;`$r5){o|j57E8je6wNn(VxZSwP=p(2MiFNoCq>k_)rZpa=X> zu6iDzG{5h+m_>$u_z(EOVCo~o)c0Mc0!uWeF6$K@zi-I;@+w*B4@`1K&U2vj7$|9G zo;LZj@J2sZZuD)bwE zi@fK2iS--!9}C}-EgOQWgXmD!V+q9{GJ zdn#29%NzjF>}f>=aN73N?ybyu%FUk%tmsu52;YeiFgNK@_FB1)l$$i%LlMpkvgQ(FZgu*5si6%j%Y5rSPi zBj?v{Hkj#Qm?@4@_0-;7nNOVlUQ3-1Re*HJloceJSg zN0Ma}6Ad|94J#{fvHYtIT_h7QMfL@2J#vU_eBFv*&VB9BANB+IdV5 zGHIwSlY@;;??Dpt*br0u-pahMv%%*XFtilNLrv=B>b%>?>#&ZOK&GDBHI=;wBhrY* z=QU$`vd0;%C{Q_Xbg_e=R$Bm)tDBm zaMKK$D4b4cg<}b+h@);=MB(0Xv!QU)ViitP6@??txk;9Kj1@xRrWwX&<#>@x2vuoX zjKb|lp03QT%TucrPM7u6Lg6MtCVwDFDBQHD!rcsMNLeVH%coX2)$<|h&+Lve%TmkE zO|#Tqbn|6!1gL4VT}=~vI@|E+%@e)*n(5komD&v6V88`=-=GnnV-#;(ei!H`7ngTn zNMtWA0p0l>$$?!vS>=xgU=N;G2*mgMl)cx`Daxw^8}?63a#~J_LCwa8sqE*ac8qbD zjKBHup{b6RdQU1q=l;p`+ANjN{HIba)djRf1^+BM2ji173EB7I$&yckZ!5*cxDq3I_Bb`Z<+l%o^KGu31n!hoLy*(ZmSj-$B{c zD4TZ|#(;f=$)A(=8MX3ClPs12aFu8XSj!2cP=6NsDF@mNgTi+M$;`V ztMltoPj(ke)uHX|mU~S>jDZsd#D2L~`h~iOp??|h`*gKb1{QgFcW@55-;`=yoxQ9a z6@J51*tYuYThX#?d{xulZ1OOx<#LvLz?9ial4C$_FP>XSauJ=|@1=69!Be}VGUqM> zq(A!u!#alI<(wOL8MK|1IrjtYVLaKFDfn`nb8P$t~2rlMt9E57k6$sAgr3B@g*~!#^nq}&R*tnG`e+Ycg{^sW^1p(-9+4; zRGV2R=GKEEGULo0G}cq*R-MpuD@$~w6gamYl%~>|OLDWp%E8#7t*Oe~N}L<;*w9nz z&_NNdT{|mhxPefG4#o`aPhF}Q+PW@xZq;RFZmmIME+I(_?Ss*w{n*)tQApb5lfwu9 zdiuG1`=uU196i_PSZWJK(1dI~f_@n{f_`cE^m2^+%JlBmN{%Nt1!a{9@sQD%9hFqV zw~Ry!v=08-=pdA40xF$`=Tq_xJ$&2f;XW)x?m+$yJedJk3*R*ij8i#B0Jsv|QVHKT zeKaxu3*@F}Tj~!eB06}?bj)N<2s+`Z#X2Sp_sfd#EFmHUHJk$b{ggW+{|j4xCAB&A4l> zR`y>^Gw0;2NB#vmpV@j=FV4B&&4#xA#mIIcmmV%kDchtIvi?FHs^>)P(Ab~r&Spn{ zKOb&8I(uu5?(EOq&JHZ3H~x7lwZyLV2JWTL@ws8jm-7tDK7z7&vVow|G*{#FmVuR~ z{g8=zrne1ra-J;uJ(83eUW~psP1jM-dnf8z1V*xHs*??oqWs;tmKvL9sUZMq%}O<} zX*q8gSl%)ECXT5I6Ku{Rlz1PHo-@ueC1&N_&#jYgCOIc>6zyhrQ?3~0`lD6SvOf}W zs9|nlP66^C!joM#Pub_2@-l{cD%L$FP39m9^qsy?*aVz^cw+*Z%h3ObVW8(4TW*q5xv?ZCu*m4TAFqcC+EyDh4OaM z>|Y~o;gUP=Zu-;LnzUZT%rG)2$ZtTYU-ZHHo^0HRu~lpc7G*Cj!(wQmNlw%Kd6O&j zJbpXWzS%G{OAEf#B@T&H2l8F&wy zOZL_@QnhQ-^HRCR{g^3RkoOpu36GoPxV*tMm^)3<$ozXoPsH{9zTtUtp0wx(2DK>f zM;v{-Of}Q;Hn3NoFvtt@j?%??GEFao=V&QCWy;Q=3EgcHvvT&IYpKuhB$DLO-j2d%rQ_@Hw8yW zW}qpCV=VF^zAFri*fJ1r=}N<5jd6>LOnOsg{v1@e7|%Tzu$+>vN~eo0u3UZ<^54Vr z7-8Uax?yN1+@f;_Vj9DvZTQzr^?NFFMj`V94N&&iObIM~#<}^h;$JiTVLV>yBEcbI z9d3yetHB1`m4_jW+32uR$X_>gx#`bwTtuyq3b1s{e;$ZQK%B~sf`o1}kjW~C*^}{P z7ca-4t~BM*&YYW&`vx9O`5ptAM#|Nu{0tJ?U=p0Za-ZUOdpO;qs=a%OrS?JH9`DCd z$%XX8qDm2U`#k-5p!8Y9HNc7VA0eJI6y<(}2y1y<2zzSlR-auD+Il6p$4K+QYCWy| z*dVp8?%f2WYtI4VDDJgBYY^c`W6x0Kb>MidGuUuFe|#SNyrK9jSABH{zG6TpS5xk8 zYK`n)zGC`>TX!tu3O;BTW1;%>Z>uGgOpt8>O6pD=MZ-26u9LC!146@K7uypqoKC%4OTR-SJuKiQVs2r&Nv+}714 z@L$hwa1Qf0GV#y~l#jMl8!Um<9}|yE$sUIl@Q|;Tla!nE>M6(TkQDYEv}coGkQb_+ zSJGB=(v(T{Nj&{AF;639aaKE-vSxc(Ue$94nIGVYBf@eW3M|F%x*TU&Y3P7RS!t-7^lE2gprf4) zUv~NJ*nKv*rK;z2NXs6o36tmuJcmGd*Z`OC&tMO=7(h9PNmes=iOt>%P*F-AR_~h=&)*`u>ESb+raCvn!q!0M6+~ z&u+G3F{2-zJRiFD1F|qzV>5FDo>>5t4|HdwxvsY4TmaZwJe+yvIeH}%1P#UQ&zp2J zSpG3Qn?U0!(9j3gHyOUofwi0Ts_q5w%kbh1o5f;$%c}uJKS0rQrwJ^D3RSCm;BJof zf!qA<#ZW8jBPqu+eDjN6aTz^GseZ+a@G8qk;Ed(^^CffZ<0}D3@bFKH_J0TTtu{?* zVgD~XBK3)`q*PzABCg!YNLC$4u;+7drt$8T)W(|lJ{i|TH2?;h(0Tw(b$L9Iw-QVe zYWgs%&(LLAE@Paff+*W-Fv{-12@QZO>#c2gxH9i4W^|T}1P}kNXx?7(*Tv$khy)M+ zdT7p-V=+PE$)-f~1v+o-v4<7YMD^GDw9#Ip+&dS<~-u}U?7nV;Y}3P1%)OVvw69^Cru*_a00yDYJ0 z#M?9;=a0#_WDDFt2dEf;FeL1cKzHaOlGzmlJp07oR6d}O zKXiDUcN{INKljJ1J2}=8JOyi@&LdIG{$9dKh%dpj?GEIWL1OmBzIUPL37);osb)@r z?{Nee1jyb|F z?dvAt*SwNxV*ub)oLc_IzmoYbvdk-)>kzdguVfMseI-+;+*dM>3e2ZG?kkxeA|uJW zh1o^;@h)eSuVi8yUM2KGLM5!ikCpH%{MyR?Jp%9kFYwD(GFJo3ypqZ0kqOGeTTh0` z#Lt1?{Tb14HO{<}StTj=l}wH4zLKd^d?k}uN#0h{!5NP7HfH0uqt|=u(5GJ$Z{(J-!{@3%w9_F0uH&gl1>7uohuwj1|Yu71rN@ ziOZpyohu{kbg^0ih^bhTrs7YeV%CwhpEAA5x)1Rbf5Oy}FyVf-J7JoxNBQ1_pc1CD zB)j=h;3r(eR-l`4^2Fq|KKd5T#JlM#L_Ox+uyYB#d7k7XwxEUf3&bfEOnWgG11t{EdnQ?G7zX%Ys6n@VC}$1$ z0okcl-Tow41Yp0mJKilFB*ZJG_2@{v!EA+fO^;`p7{Wr<)jcX%<~*vgb?w=&g8nU_ z-~D!FO^~ryJBwXLDfP705_L1CY%B_Z^e9F=Z$qs_eZAym&CMdhTE(irDOJ~&b{8wU zMaD_(QWdbWSvy<~7QgCpY_NmGdmxc5Bs{viYcUbyTBOJNqi#$NI|WJUm0Dj8dj(19 zmRf(VG(5o~n|5t6PRWzJB$;moiC1P>WQLXf8P5?^cy$6+cTXY{K*qUJYGM^-hNjBd ztCPA$Z*Splnc;3>?k`EW+#r`iL?Otw&qpi+Z-Ld!o{TME82W#(D(R=YEYnfCHFufP zUfmiu>C=L^+f?Q64Po9R4Y01sm42+2g4VUU(vcg4s&#d)^yEfMBwaD*>>Sj88J^zg z%N_dt0$l2WOsdKHXO{FMnQX!{8H_%Gc~HybGb0nzC!ZTuyxH;+?iZF=1oQ$ef_2h# zia)1>g#TfZ>1Qtn-DmOSW-`B{P5M|q=KfG8jeg`Fpv*c#eFMUaS+l1>~CvrG+Ym`#c;S>543g18mudpq0{yjYW#@mzRUXCsW#LUTD?c?O z#gp1S)xi}BzENrCsg#sUki|L7@}#DuT$F-$IN;Rai&mc09`v5NAi~^9$W1i(aAG=S z#NqooM$q^T$D}k6W{G646(yRI5tHzvgijM@iHLop*37VC~;F-i3=WE~ZwH&(m}@iI+}muUxAroL!pqy4Nsskb!uU)360 zl{d;lf|h)uOTL4q=8Nqtyb$QNk3Es>iax~^y+5Ayj(BY>qD!gK!qTUvH)A!!an-F; zgpO!HhAC>gJkxCg`W6tXkAZZF?dq;VB!N(NaaOEOokbnddV@ZsVsvj`F?z&n!r3X- zbc~pu200;Ki+UL?0ypfCfYA<<%r>M*)h+x1dl@C@ zI*yW4Rn)akFga{^R;T=lEN_72$C8S*uf2h8#wl6u)1J!rjViJsCd$Yz5+?@O7vxvN$k!TsPgg zC|IsZodE&PP#jz-^CzdtCU`3@)qi$ar%rE+bgFLFLWL#E(%`~nC3B1295fVV7L}pT zp?c6IH;Nrm{PAI(U5>x^J-=M*$Goy7^OwwBT%uD{626({S!-EqE0+5fJp8aP^ze4y z%x%8VLc3q)6Hj=8xWonR2_)LNT}JnP;)$7F+jB52xZoE(B>y4k!_4=u8xotYc>g-j z8}`5juRr`7m4*g;p#NEx*pQ7c_{RSVKTfqTM&6qGI{mPdxv;Z}LpR^^GF@D1JOR)be@~ zXAb_E)bb4T6Cc8_1qK^EI{`EoQUuhIpj3j=2uk+R%GgY&|DW!@JwC3hI(IzdC?{{ncAh05KjN4q$QsF(WfCPG zOCxz~Jti8-ww%E6SQ?GSmS&WB$To0`6DOgx&_+pkltScC$`4AMghFpA4v_L{lJZD< zxp6{ie<9$Iv=p3#0EM=>-?!G@=bRahO}O`;d+(9XtiASouf5jVYwvURKF;UyyEuF~ zb6X<*=hOcJXIzkrne#Eh*(WzXQxnfy6R!_E#5Fzq?v1i`s%-gz(KXAuiOUs|wO zewR9|$px(Ng;Jf1!mF8N2_s(YuuYeW|0Utp#SL%C?tfs`+4^$j82sn=v~aI5WCIfl z{Ua2dNP`3k%LzLOa|^nU!OlbcWu}qv8%cr)WlMe*Gf}bGThQ=Q!<@4< z8~JwV)+{J_2!fi;W+NAu4^yz0A9E_IyH46@WU`P^79N>(UU9aDqZc1p!;Jq3KjDV) z250%it6L{NwbjWwOTurw;K*!E@KOdJI)uQ>FL0he;>^xE*<(-Nab%?RQ02(%$f4Q0 znA<#+A$_HK%vsz3oqc5PM-oxEH=@$8&=FNHbh4@s={)1^ihe{C^>1U!+_qGGL@ts&XmwyNqn`dWrD{@x6FQ!%k-; z>r7;yMu;k!Fy$q}pN0H6Ei4SspptXCN{-b0jF%96Ci6Xu4LO@X=Zf5%$C@mXa?V$| zIfu~}i`*;~|0R^0n@GL|k@R?E*}WYz*$OcXKgW8XJLLS#Igr`uyrK6CPNp|<;W)a@ z$%a>6@^tpTxz;0d4@y)vCrmx2)%|=!_>D{PFJY{ESc)=Zh_mEAXW5aX&I$}Y5@WpK z$n5OV=4_<0BXTY^3%a-AvGBD^4j*~Uxnm6?&uY(#J|~@}m<0`yw~a?ct1yAH)tPJrh7UT~2OO$VG(vRh{{u2N zk`1q4ddyK-G0JUe$J+`I(;k;~2ry#1Jp0^1RxXokJ&ENl=39bUfVfg49!ve}ojmB%RZ z;RaTI!L<#Ca*xgZoK79vYU-xr;IX+%1LE~k!^_zRvJc!o(iXXBJexg~y$6a%^%AQS z7ULkYqhX`=9raw5o%)4Jg#$UpGl?y|qU60sytg#m@tNZsgJAAv4~tiwcFcZpF1+f( zi6h5nkL{^AOO9l3o0n%pNA_4`-+1J_Ems|jVCRLD+3<45y2!;sb!)OaKl(cftL<&} zl3Ekabx+}p4yCCyFDwI2L{!2;kaYw`#KSi_OFkNY^O7TvoigSN$jP!b>l6vc76a%= zcbW`?)>d1|fN;al`1|mU_+8L|3C871Z^}NbMlY~8UX1(>Wu3|)XU>Tpnwtw>vGm~^ zA0u8l<}CR_G;)*kXygiQ{pTBw-ahicXXYYjRue#b-vQra7T|O^9mgK4U?^F=NUtPdYVvTo|R3$)?xcMTiK$_m&ov-RyM47HRRRKmf=Cw_oYUO zQ+8iRT^}^G$#0FK=H8$+Tqbo>Lz|J8KS^%8oYJ?-xfQ>wCA%vn+Er4F8rq_D-Xz{l z;tflCCs-{UTe1tuK#n^bNGuThh_mFexx>etj-7(MeB*ItakVMUtmOZ9daH@N>FO-Gmo(enNIVD=d6>c6gA<&u$2JGr>7b zaITJP=Sh#5i3$h0#li=$cbMV=_V|U;ofoP7VP+~X!C&WYmxMPgZrB+aX^LFc80p#( zS$1vYGAlWc9G<9TMWwxr_zOO@b4F(}u$zxLvmMPa#m{*39nNjdS!Wh&1(=LP`O`cx za>!XGzb`k7`R0vI5H?*Q_mD|}~;U|Gv)eCYAn$1&r2_`cc5(t9K2MobcBoLT(c z?OgZRe_J&Yfwhx`&Uota?KjQs8(F&-Ql7cpnVZ1Afez%b@9^Qdx%+N&jvn6H8@_hw z#KdDeu|k3rJHxF@C#WWFbS56}n2MZDy@{ne{Dx(8jX)vxYIkCoYH()WEfMZ)ID8cB z@bHaTaa%j(xZ;W6>oBRg5es&gH0820Pfa*GU+HMeACWb=yRi5a4!x(|nw)u%_rJGN z$py&Qyhw@2?r33Zs*uNe6xQ#!9>sSU@NK{MU-#`p*H1WapM5;Cq&!h^?w*UzMGxW5 zMzhyv+amj&TO(&SZj5~Ho#e&^MaEY9`Q27mzARM|Jzx5U^ z`FIrze)@bB{YP@~>1*W51V zL378lWy_IV|2l-r*um4q%rw`o(bL6DsK}+uP$~Dej9U(McPHF|t)cW}E}NgidSwV7 zCCHY~E8fi|W3Mt&20UZKy;_X0 z;$)#zK^-flOwk=nm(vn!xIeL@ZLOr{f$ug~){Tj{x>_lPD)|x`kr`_ook$l$`SINT z%$PKW(qu^h$&hmJKG9?$pKX*MFC{-jkS`a?=}AfG$6x@9?M@6Dq7X}sq_%}}rI~aw zmmbTFhDsBKV!4rV{f3$brDeT1wEI9gQwoHlPmtOqtg1#XpDX9kE$F3mHe1YO)8$ME zwG!fM7R=Hup*TYu!N(^HX)Rw+Hesw#**%$&QpK-=B+)h>XBMq3K5pWZY>dx!a-l@M ze1b(Y-*!KnnL=*N$EFJi%@lnPmKv-DQwM9o@b-|K>U4YK+v2^gZfC4>bKFfOcE-gw z(Ldz&#dd^7Co1{9CGqNuCBbLtq~3T+JUTZgdb=bRcm#X(?OMNEF6O2}uDdsvAJd4v zTiv0~t)cPBG$>5=Q~y+==Z4|xO`$)U}Iad7(J zz@}kvNOCYS;3oPv#|INbAv_6Ckx3Ve=>x7*SkiRGw8$)|o!OqRG zL21S~WVo2g_(>=~O2DDP80%x|?YjBQ{&Hw)Dm^_-dZ<9McKeFCa>m`AE~y^T#69t$ zob0cN9n-i;J{G4Q*&TLz;f}yel%qab;nyY=pR-L<7avj452krK0wmWcq-+lK=pW6 zNF$i8ADhHkc@iI*3TKu{Lxl4Eu|8!rceGMOzLS-yJh@GJll|ao!eV9$3BfC-pOw&F zOQrH%@!p}BHfsuR{(x6Y6}+Md?&+ygr~rm@%bBTZH?u!8 z>W=3i=Tnt(X1`{No{TDAph zy{sw({fmTsDU+@=ZA+m*45SinXD`IB428l*K}4rRQe9ADYPabT)69f6Qx#Z^{6jP{ zqkE;EMk}V0@y$WE%V%M!w%XZ+`1##gDwc7t<~sSL5F(> z`c<>|apV1)-9)q;mn3GV3Y;lK^@eqe;S13Q3 z8Ry`jq)f(oOjjZmyOOrTP`pp{6vqzaUY3D?FW)lj7LVJp)z>z3Fz$8@Csl|&1{JR@zVXL5K5dKK-? z=0+h8XrZzh<7hkFuGmm)Q!Evi@!UL64``2|wsgCC24iTsr34j3qQ56(WL625@+v2z zteYMiQ@RYR%1#Vu8-gmKT6#>gT~aqx@<^CUCi(+9Y1o<#b%aT(I2hY*2CNi1cMQVi zQzjK~yQx@r-0dFj@5G?epCadT6E!1m9~kU%?Z`72?}=keY@OoV!ZZ{f&n?QkMlY2x zvWT8yHnG%5f9K}Gf&PKv6m10hkxHE~%u}vERKQ#Le;#eD&EzW<#5J8Pdd%$uH;6{^ zI9Icx#VrLw_J`s-hD2#lWtUXKs`^*;yo$n<(N(N0^oge&Hzm;F*(p~9fbUPJ^&rs# zI#i+|X(|p|Zu7vPvE!tttpOw|QpOOzi2<8?5<*a+;vGZf+?y-NTui-6-l!zcCg{Ul zBf)WxW3#r;AOnLbcc8zQ3P4y#xSqn#8V;v0uJsHK3@54Sa#I0i)gpSmS7tYiN>Z!5 zqFQcYe7uA$Qw&IM9!a^P7SP9|zP+II#z2XhLFO>ERO56Kwx+A^kVtx1qW!U6t#luF zVrZZry0MeO6xx?ARoPL4Lx?T4;Ap{@O|;qpdZ9+5mN=#*HVqFMLI+w3y~}zV)!I4i z=_mR^9JQ5f83uwKLy16a+Zw}bT4~X`lnT|Vo`CMylv-EKC!Uo)UMQ5Oix`mYK*(Xn z7*W`YRy+U^!r9EokfJv_ks~KSU#NL%t>@t?t1_8@)@BoAPU`llDKxNY3k##w61Rxf%bt5lrH8~ORy)S)2M*A%d~|?jtl`vVjB%tsXDcv4>R3slX=O6Q6p7@v zHYs590H$hw&Ip3-2yKSZNiwuuptZ4vePRHH?g%p49Pb*2y!X^ClBNmX2~5N0cBe#h zhHjP)qN?|MB8i?rS659Z41dA?$A==N>=rk*-5bU|>9Q(ex>zW4{BfQeBR-~M8Wv4_TSTo&NCoB1*QoJ6Q9-1u zVIZk7W*@N;YBZg5GT2xngTrb+!E_E-n~cyO(8ZY7c1uQH?=nVAjR6VR6dN2&V1gis zjIvc4N`?$Du`C*R10XClN>7;+o*9uUQ(KvcTa(ii%$ZxQf`E+506p$f2zWAl5xMQ8%! zjAybw#Z;Si75j0p9!T=3-grrf~{je!^xzgmeG&9)1!MU)5Z*tl{4xC9rnoh zno^mz55qMqYHdxt&+UhO;47}7>fs1WFAz{H6JeoYz^c({kQDYQ6))Gd0)>6_^@976UpR042wBJd!e#pWzB3hdquUcF+UobG-+$ z+!rc)(wf>Fhq__qyTRE#eM4MaNYA8mlj5mN&^b7g)Y=;Z+sxZMA!2%rk7$US8cu>o zhM>-MTnf}&ObJ!qY#^8s^Ho%ol=Jig%^g*DT>J8D0->cuwQ~_BOVzL=c%3WRTwWV5 z$(V#UaWloDwHEByDD@TMr{cXB=IxR~wdKLJ$4Xuoaj?b%BVK1ID7eb}^vHlIij@Yb zDrWaP5@sZlmXu}0q0_Uj?HJoRz%?smjdc-Ots#lrnX;_i1*}H!xw|daYZf4-AytI; z$*I{TIW>^HkassH5a^g`v)n}M$y+2)Qh21o5+GL8uo$hJFoXfl<8E1PFqImcwDu3l z&Oxq(EGh3Ge4%Lc5-qYRp#gOfXR{T|BDKLP$}ojfDH(*oxH2EGoLi;1&7=;EfwXYG z={2q@nzty7sn(#2RTS+8VHz`uvid-lP4We)i&1)kWh%q*WCzxmeIu|^eLtl&JXx9e4a13Tm_1l_K{=S}pexjLwu=%n!)JW*Xsy-5 z=+bjGHac{t-$7UoPHwMV4pxfy#(QF&BN#1|>yW=0a6}aB@^ig`6WUH$o~9uO@v#sd zl5j26TN1^ZNfwd9-Lss7N0uyV(}r|RC;s*i^t-L1TH6o7V%>;zYtd|@QlQD zN6Y&Kfs!ghMl1R>hgo9=JdME~8A}9x{({y{W8nr1(^xQP4NCf~N@@cNVv3o@x#xW3 zA|*-{AdW+tyu^&L+@A|Ps*;-O!z@ldXkI{KGh=+Nh~?X{3_Y2Jd8T96gfM_@Lshui z?o%O<&v<$-o^WZK_{az4at||VFpZ5Bun{b2T$7_b1j95fEv11Vy!a?K94ap)+KPhn&G(aR_m~n8bm%T*PsDnD`OmBrr)wcsd@1GaaEL?lQK1=tcYt<41Uwx*fu} zxGN5!oY{an*>uEvB)q(kFrjXTkbBcv9^q~3@1-MtyYVA@m%1IozYurDA>`NLNdsFR zA(|q7gh9GoCvB}bgg6+hDjo5S#*YvO2Ql0se7CqO4j~Vd2$o0u4~!q-d)4g_J}K^s zLwHR6y?PRV%J>m}SKSWbmreT!zf#ja;$Jm>gkM*;L%0ErB|QoG>DFL9iObGv(2B4_ z!yUqZMXi{Q@WbY6 zBhD}6vF?QX)$I_<=faSV@HzEIE%YM(BjZQ-g1Q~TRoIiw@(B6q%OG8duQq;!SE}0~ z{Bs;}!*qn761QJZ;`bXr!Uxpt5Y8>tbcBb+?WZGtpYbF7Q*}Fp|AwPySRUa|#OIS&`{{`1jUVB(x*fvrzgg1}J}YiN z9q}I+Kf>ecb_fq|)^vpTiQ7*{{G-N?@MG$B2-^~xj&QxW{dC0JjUVBg)$I`8EbfX! zSX6&crihn}A7Mq^4&mM6#`{r#_o%;@j`;hGAK?eo?GQdL?utYBck1t@BmNEJNBAvu zJA}U$cf}$65B2xb5&uu)NBF9`9m3^XlrDrT#O>3C_)6nPxJum);SO*B6Bgnz64UOM8B8b89v)$I^IA?}Jp`1k7Xr6c|i#*gqj>UIdfEAEOz z_>b!Er6c}5<45?<>UIc!BJPSq_*3=w(h)yl{0LuCw?l~6rbLbilj8R2N&E)mN0?H# zLzokH#Ub3I{$6>+_ZmOKDRn!9cZ(Z*26&J9d+CV3&-f93K-~`EaIe;raGSXOdJ^Am z{0K+X?GPRkH*6BX+tlAHkNEAzkMLdUb_k#B)A9(9iQ6xa_*2G@@Vn}E2;=>lj<8$Y zemdek#*YwFG0?v35PnYF6^HPz)!(Zp@y{DS!iUxE5N^kzW7LC$ctXegymZ7zj342R z>UIeC-Jt0R_lw(4NBn^CBYeBM9m4O6yW$W&tNvbj#D8G?2#>4VA#5Gg@(9<8+b@s! zI^##UUfm91x40_~VUPNI<#Bw0c_nQogy|#nBs@**uZlz1Aa1`r;-?!w!ZXzE5H^ba zRdEQHsV(M}M|`>QBV4I&hwvNXt~i9>RDUlW@oyPF!Y9=25Kf5QR&fY(;`ZxFe2?)X zoK&|%_>{OS4&l@4@0CaVyT*_3d+K%wZ$vEe3*k-T_RAyw7UM_gs@oy_cX3x7!e6OB ze)S^$YvV`wpXzo9S7JXo%Okv8+hW62jYjCN(ZK}x(8S;Dx9#ce4(6a!if`2yDNBr`6j&5As80hCqhkQ2l7a%5Q}UQ zGJ`jaxcVPNT!i6C1mmk?8t-~F@hNtw32zADcN_?U3i4KMQ)U8dQ+R^mPT)NY57QJ_ zFyI5orqOZ%uQD}_DcCcLh{!%Yg*RQBvUudgpC&0s^W2TE@HO#eay~+B8q4giaP5KX z+RR1JTzncRTL0a5LTyAz8eifV40SNdzte+=!cL8Sk?f=>6+} zG%zkNGRUi@+=p62A>O}2pe>i_c@N|MFB9+nb*LNehv3I@8JCdP816H0GtEtSPo4Lp zxNUxn=ir{gZR4#3!khJGyfbmzcr$Rbu9&EX)*=q?fDn4+-W-TGjX1oU;b$-A%{{V#dIG-ganLWoFE-v4CO_Mb)!)aLuW;g@y%>*oi-~9b&-^c(E_{x~{QBX;09Ea1 z!_wn^W?(Ms0Q=INZGL0hnIvyhq0Qn0v>;en|-?oH+4oZziY1! zhniQx(_A(`iw7ZfFRzHHId!b!S~7L1;#varrs6DJb)(`MkvdRu?HKAh#i{XCrzx(z zPu-@QN6Tpl(rIJA^t!acTn97mBO-M4g~GDTlGb!_|zU zjPr_w77L^-D-M$i7iE@L#7G)tQ*k8>Wl-@81ya@&R|ce9DUOZ|T#*ny#u&IFRhpIM zan$s$NGQOl@Q#E61DW7hJ_}rt5SGOPS0vO}#j6WLFTP40W9fm4($&+NX&58$Ryf%{wh*5<4DAH`DsXZN@d?0#f%r-Zc;aIDWx(eReOdl3z`x_+ z=Y{?Vc(D9G0Uk2_&+Ka3^$)t>1aT-xI)Ti9b}p zm%tI9`BT2_#q>DZGjsysYI~d&s?>zP1Ng!BU`A`wBWv$s`ul3|`+@UhGRh#!`j9abHVe;FC#Uh3PeMKyz@bh;JWKr<#AEoyz~2rcu^*X! zHSn(k?=$!Y;7fjq6?lX5$;qq0_ZfT$_{_iK>4zzQ5AfB$4u!f5UIETy_!|xW4&amj zffWXW{~mCDvYvJe%bx|#56;I7{wKisz4+UpN*Ml0;QV&>E9gMte+islIPN$2mw@vt zz1KiBG5i>C9>BlHY*7eh2k7s3yk$1#o`VagE9EG^jv+_-_D^;THn( zlTBZO>LGp^aDFkYV#-?uoZqx-Hu2j5`Qf#1BVWe99ymYr@_tjF&A|D2mTL??2%O(m zS#I#R0O$8o)*HMCoS!*~8vNbB`K^*G41O1Iet(22nB{#KIKL^9H~60ekHl~=zQMl$ zoSz4=_;-Nw%OJEVnEnUA`9YAh!G8&yANQaQ$nY@e#H08vzr7B4*Fh^^7ll>;=Ry1c zA;!8Ij*lb#l_q^0IKSNSWrK6Z&d+mDzVJ1ZP!2f1wb5k4ZvoElXQWL3{V{NUAOk@1 z|8qF_c?^pmg<}`$GbG^`g}wuvhy2^{7vbRdE+Ep#R_|2Hyw#tU&nh0{<~~vD*0W0{(IUp9TJ_ z06qu&*#Q0t;7Y6d z0B8EY2EOP7R$<^?jP^Vc2xor(0(|3sJOzcoF?^YtNC?sTtPSAI?*`xx9mfg~NX+L(7Bd|Dh&)0Zg)qTU4IUhrF%;-uN$|OIYLg0bf0+ z;mc57MolY4dXb^z$-v#`kyYR#S_;TQn13!EO^D2|yOM&#P z|GzW*gP^fV-$KP?E`}#+@V5kT)~8T|-va!hS)Et2J|6&X?c2q$Z$HBD7xl?J!;b;y z3GVkIFXAVFC*OSuo%heN)#082&oo8-Ii`)5LEF{;gN=kz<1=fCtNWf%E$W zoIf#r8MwXJzIy^V+w<`N&hQ6;{~q{-`jq_gE#L7o8_X$q(sqWdLXSJ-|=Afb*E)7J33-^f+{yq3_3P;(r3lhWGyL@PLC?Rz6M2)q3HY4>{lPDx+KcfA zYVb7h6M^_Q18@EW`p4w=!9aW#{TDU(=L0zNe>#9O{?7wA!_VN4xfs3*`1GG*j5p;a zf%DV{s0g9=TY*0T{c^j(^MUkq{vm!Ay%-8{9>MjBu&w~2u65#e? z(yM?^zgP7;<*^I+L!ZJ5yQ$yaKz!!^r5gNI;D;gHT<~OlR#C9c#rR#ogZlc$n(#sZ zC+Thp;H=->zz=@_HUr$u|1W{>`ZLIysoy^V-}n)%W18^B3*ayp%kK-|%x_l>elzfA z9t9n2{{z4C6<^={Sxx+}1OG;Vf1a!fe?EY-{ACxSKmQJM9Fu0T1ezF9PRxJr0@h{{j3N^e5*bjQ?xk_G11^F0vT%fpd8c-Uj?3 z=-(lPF@2ulU&50MQ~&n?=f_+uz3;6_e}4`BwHo{ep?MU$A{~I?}G5X2RQS;hw<20 zca`v_jA?H-@OuLKv=oTX`usldPXzeykAXMN!XII*55FB`FUEhg27e`hv;4DPXJa!x z__YRa0lpFawEroEU?>6CA z0k;>+>md$#>H(mB90Pvv*-+>xaF(|}5TEtG1Ng=d;j0rS{6XNm5ZR7b#~6N>)|dI6 z03Nh|76g{OnBR&Tyt4)`0bg_<-kdS~^S(eh>;GBcPkaq;zZiP`V@>!^fb(N}gQmWr zMyv;5{`Xe~KL_|h^nZuJW5Dgj`c)dyo*&?i1h`3$yMaHC`6(~<-v@!)i}AnKi1Fyh zs8>Ye{{;9iF&Un$jPae(|;5G z?m#%{@gVTz=TJ9O{*%Bz8_55;n)u=6;NQD^dpi!?Ud(Sh@LkX9a{<=p=0G^>a|iJK z$ltc_L%>gb#qWo61fQkG;eSLq?<*SQ9=4@3fQ%K~>||j#js}rO;%*veui|m{^kk--8Ea~5#?fF5 z;Ff-3c$Z$Du#Z)yrm)3V%Zc}QNip3x5+UwdjKx^DUy3|`*HW-Wfcxd}BK>%wX%w~Y z=^fY<>&0gE%5FE|x;y)$EuAJ(0&$}2`AWYe;TG^o?C!yK2|a>I((mkTPvLwJ>{iEq zu${ddwhzXV+((FQMDwyN$k2%Xkvg4**amdyz zwjBE^Oq$>pGk>ex$#$G#3!WZ%;2k1Rvpvaf9wJ1UIF5_w+}I6W=I9A;)3X=B+p=!9 z+IZ&|_?y9y$ZjvHyP3Jh(;k@QorL6XQnSVDDF_}`wE!E&?V|bnbAh6xO|4AOijx|n?Fnxf*@P|j*cDfblZb6f zuta~V*|l_v%PB1UOi!IkwWV+XOg&6yjj)Ioh(P&SmY(48zpi?NI$=41|6bt*} z`$sd=%p?vr@LT9ALE~s2=~!(qv}SQ|PNrfG6w?^hlwKQ$O03f`cjwSHW#VYFGRpR1 zdKxs8&8amc1X0?DiU&H=IQ+_1+){2svQi!fU$wT>Ev4VaGhLp=nYA2ci)9*ATV~wB zOcn>W!P>wX0wuh1h(_8ZHtV*vb+WEfUH0=%ID7-_$^%8ylYSe>t?tgT$>faj+p)uK zZCX2@p0Ido*{j{_#dth9lUj>P@9a;N*9qAZ$(AIiE#a9&h&7f$yabd& zD$QSXVi%37I&F-$)oCM`t(X~~U(aZ>FBtP`91sYUmgUom|H$Jffat@GPqEM4!ETW_~7qLP9}e{9&!6Evwqpzd?YTrLFr2|74P zB=lJ&Z+gwEQkzj9at@%H_46tbZONiosDEK7k!6(JvaLP3t`El~Ci33VmGR_BMha0U z8$3m_F3!#UHR{APh5Ce)7v-h{J8E<)cD9^b%>vP8XgW)53eUP;j8l`UnpNwfC@5B_ z>S#-B6lbfH3PnzKq=g=ZUL!}3P4c~J)Rf~x%Q{~SQqWtuQ7f#c946RKzf0MhGGsxq zokH>Yazws{wv#lzFsTl01u;&V3pm3bEi_r+8D#;UB^6^hJP*_nf&MM12+1uMCIjlO zW+;wERnP+!u9ei57>6`alu9g~+<=m5TT&3qpBz}I&b_}i?F-8aBm=|G`ImPBc5u-**IF84Ji&@Wt^r> z36;Ku(yl4fN=-YshGTDgy{cN_mIIx$FxBy@elVZ3$YRjP(+4!)VWI~*za4$w%`>X& zlvLH{8xkeo@PkQC4OBfkbJpyas$+AIX`;g@-IuSay|50Zru`HidwV9yhRx|xXS$M| zC|3^+(Pq@T(HemKqZI#RNO39kUCWdHp7C zjJ6nN3~HciGeuUpv~ET_i15xp_IN5$u*)&nk4^_>LBfeO!(q#O9#J0fA)UOftV%Au1TfbVjrd?Wn2#Nz`hPLpXIfDXl#{XVx*4 za2tkUVZoNoj6rnMyGu1y;>fU8x}|RCozimRT+gRc;20H@T+Khid-V&LX=TjE8!9(MtTO>ISPPSzU$XOp@yt0fuDfqYLUzQP?ah(GY9~ofuQU zGqrCAKwOOymZ^^#^=rGKE+#!&6(_Wd#xb+ZdQ;hGdpGhfO}rLk#VEJBRh~!mHkq?! z;>q>Wn%uaOz)%6z@&{${2f&QpB>Z5H` zvNdZzQLg=1+xw&%HVJ#B#w0>ZjU@_Vs2N{t#mBkowI22hjt$22I37>M+&IpLpJf|ff56%Rs|S!EGod)4H*4l z>tLM|2S86^?&I6`pf)VvtX<)BtA^rnEFI^u9vZNEn&k`ORUVXvQ!LO#Rjwzy3i%9| z$tq>65mHsl>BUv+h@u#b;^riPq7BMmcD|t#tDME{ne^UMOv|n0vP-+uZ2r6Gz(ML-`*+aW*WutX$ znHtuqV=33Mll=_Q{1~6Jq2tDWTk{6%t##MBYpWy+Y-}GMypB%nb;ognrQbrC)y!K~ zS~tuk8`U4Sw(F#(@ja%i*A5jcW{kBlT3TeK2dDApXkU4cMX@*p3oAfLE6FF*{`2Z% z1<01fQF_8uWIa>@C)Lq)m=<>z3sb}93rbp5ik7Ews;ACUCuOl{Ygev>BWW|nlsNf{ zlC8o?EEB7%W#M&Sy3W+jW?-y{x{JB6tfvYyRk9nDIM^51P);9NBc!?Un?$>9JL-bR z*+#Chs%~3fB@yj|>3yA?J-r6ujka)M(OZZ}rZB*t@*x213SG3Y^C&YwRZl=pLRc=) zThjRomqv5oV@=e*u%QF27Hxx!y@pkqTGe1}MO!2dsFN)%xI0tj=z1JS$j25qiPk*# zwrj=ya1_i8TwAy6*{iY$$)?GviKV>iUrMhpxMsor|77W%X1G3U_p=py!q&5DG1Kyk zl{e|99B4hXtw!Yp>2F27Zd=h$)#HA&g<{CZDno^BSX9Qh24H8*ADqKM+T{yaW?83g`(!c4^@kr(j})bJNU$+I)#~Xd7?nE zrYQJh@p@gXsb}(gsVG2LwHuq{JYG}=-c6B*rY=4uRguTzeBz2Zu6&`+7XtIl)rtWp z&!@6deCw^9&y@JVAfwr;=n!4Eot%wFdzP5VXtOs>Eakkl0=xuNWo)6{U{YgsZonv) eIxw{x^M@pDPrSxLeH{`W5>oq7y|sMNX8#MAV&!lE literal 0 HcmV?d00001 diff --git a/node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node b/node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node new file mode 100644 index 0000000000000000000000000000000000000000..ff13d06365a8cbcd221131a95018444788594253 GIT binary patch literal 159079 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%0DA?;^dg#GZ-W>iK}%{lO{ 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ⅅ`0;e!MR}o-J?I`2zSU~J-59;C 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 literal 0 HcmV?d00001 diff --git a/node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node b/node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node new file mode 100644 index 0000000000000000000000000000000000000000..b0cc4b144d5e7cbdf2610a75c8b83f28e7cf8a50 GIT binary patch literal 92160 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
|iwNv)1N#ev5CXd^xur`4 z_CC^>C9u0}fxXWmu)B1iEKguVVj^+|foGRF1oj>jxSRHfSVg5hxk`08fjwEJoWR~q zfsJZ=L|`Ab1oj>g*!vW>T5~PuZ-(L% zL$ZUU)U=bGEE0C_pltqK3UF)t2s#o|20vlS9YRFF28Qrqz_Ur^Ih4w|3=@&Zj{+T* ziv)pkdPtlKpU@rivV`u$4YTNO)TX>AN=?>LQp*x>Ncc#!CggsnDud)pV*$(h`{0*jyz@ZSs>Krj(hx)bT z6H^XxPDt_SWD#~3REi%6-B4fsag;&kjA@#f0}obWv=B!dU{{WbZ#M{~hERelQJik1 z_NNtk3~t1ckt?uc!8c1ELO27!9CIO<%&pZiB*as@9Or&T0#f}r%8^smH1zB%M$=F_ zC7aQmzY)NZQrGz=K3Vcewxca02fOCa4xb*HK1j}Cg5H3?qn{8*2TuQdHf(9xgT}Uu zw(O6L_>!-{u7=p@mfdKW``ynYtWC53$Vf@@6-XWw9(JF#9A7()B)`eZ@tu>e6rZ~o z31O48KJ0EfB;Hg18S58ETnk9`B!?&FK>H!|)%0j>)A$Cc!$&1EL9fDC@_y`^B36&O z0i!d5HAGq;oaE-9@i3p1yd$2!jbH%0kL&5`LNhgc5%{Wsf=Pbbc773j3;4fY#z$mm zAlO9}F;QE8(4U+tttJo)6w7`Bes*os1YAKRM>43l4|)xWcUHixX&+e`o(JS1Y;*tw zS-20{z;-?03esc($#l^Wh1}Yo+_ImpAYX%Q@?OIgtluxSMe^3r6-0w4;4;FP-9v#C zfJlb=py39?dp}%3D&H=8LNI{3`am5{SMY%C3KHrvAU|tmLx=3JGS(gfdc%o4~h z#Y;_!jb+RWK%3dNO$}F&Hp&4Ctt%;~n*+7c3Aln(Z2J)zh2=%T1ynd(!Tq)?NJ#e+ z(haC0axNrj1sWjDchVJPm7SJbhblWI+PYJE%(`{50zs%gIz4DZk-E!ThyB=7!r*wJ zbx3v{vd-~%V8eV^8E%BUthAB|JDvdbF}Q-%)6R=obzP1oa&j$POXI?e^5* zpxJ2&+X)Y1x+LfAqC5^lpFw$yRETYL(5P=02)#xs4xUN=h`(J4f6m+;Vc=k!ycext z6^UYaouWsgUsiNjLPSmtAyFtDLdq Z0sS<`jmGgo$Rhf?+zArK^h>3L%}>xGrnJ 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 literal 0 HcmV?d00001 diff --git a/node_modules/node-ffi/deps/libffi/.npmignore b/node_modules/node-ffi/deps/libffi/.npmignore new file mode 100644 index 000000000..0fbeb18ce --- /dev/null +++ b/node_modules/node-ffi/deps/libffi/.npmignore @@ -0,0 +1,18 @@ +.libs +.deps +*.o +*.lo +.dirstamp +*.la +Makefile +config.log +config.status +*~ +fficonfig.h +include/ffi.h +include/ffitarget.h +libffi.pc +libtool +stamp-h1 +libffi*gz +autom4te.cache diff --git a/node_modules/node-ffi/doc/benchmark.js b/node_modules/node-ffi/doc/benchmark.js new file mode 100644 index 000000000..a5c452874 --- /dev/null +++ b/node_modules/node-ffi/doc/benchmark.js @@ -0,0 +1,45 @@ +var FFI = require("../lib/ffi"), + util = require("util"); + +function measureIterationsOverTime(what, duration, f, granularity) { + granularity = granularity || 1000; + + var iterations = 0, + start = Date.now(); + + while (Date.now() < (start + duration)) { + for (var i = 0; i < granularity; i++) { + f(); + } + iterations += granularity; + }; + + var duration = Date.now() - start, + persec = (iterations / (Date.now() - start)) * 1000; + + util.log("Executed " + what + " " + iterations + " times in " + duration + "ms " + "(" + persec + "/sec)"); +} + + +var benchLibrary = new FFI.Library(null, { + "abs": [ "int", [ "int" ] ], + "strtoul": [ "ulong", [ "string", "pointer", "int" ] ] +}); + +var blabs = benchLibrary.abs; +var blstrtoul = benchLibrary.strtoul; + +measureIterationsOverTime("FFI abs", 5000, function() { + blabs(1234); +}); + +measureIterationsOverTime("FFI strtoul", 5000, function() { + blstrtoul("1234567890", null, 0); +}); + +strtoulFunc = FFI.Bindings.strtoul; +strtoulPtr = FFI.Pointer.NULL; + +measureIterationsOverTime("Binding strtoul", 5000, function() { + strtoulFunc("1234567890", strtoulPtr, 0); +}); diff --git a/node_modules/node-ffi/doc/benchmark.txt b/node_modules/node-ffi/doc/benchmark.txt new file mode 100644 index 000000000..6d515cf84 --- /dev/null +++ b/node_modules/node-ffi/doc/benchmark.txt @@ -0,0 +1,10 @@ +# BENCHMARK OUTPUT + +We'll just use git to keep revisions on this. + +On my MacBook Pro: (2.4GHz Core 2 Duo P8600 -- 8GB RAM) + + 28 Apr 11:55:46 - Executed FFI abs 6948000 times in 5000ms (1389322.1355728854/sec) + 28 Apr 11:55:51 - Executed FFI strtoul 2157000 times in 5001ms (431313.7372525495/sec) + 28 Apr 11:55:56 - Executed Binding strtoul 11207000 times in 5000ms (2241400/sec) + diff --git a/node_modules/node-ffi/example/factorial/README b/node_modules/node-ffi/example/factorial/README new file mode 100644 index 000000000..f76f4d688 --- /dev/null +++ b/node_modules/node-ffi/example/factorial/README @@ -0,0 +1,7 @@ +If you've already got node installed, this can be used: + + $ node-waf configure + $ node-waf build + ... + $ node factorial.js 35 + Your output: 6399018521010896896 diff --git a/node_modules/node-ffi/example/factorial/factorial.c b/node_modules/node-ffi/example/factorial/factorial.c new file mode 100644 index 000000000..ea3528aad --- /dev/null +++ b/node_modules/node-ffi/example/factorial/factorial.c @@ -0,0 +1,11 @@ +#include + +uint64_t factorial(int max) { + int i = max; + uint64_t result = 1; + + while (i >= 2) + result *= i--; + + return result; +} diff --git a/node_modules/node-ffi/example/factorial/factorial.js b/node_modules/node-ffi/example/factorial/factorial.js new file mode 100644 index 000000000..b1d8577be --- /dev/null +++ b/node_modules/node-ffi/example/factorial/factorial.js @@ -0,0 +1,14 @@ +var FFI = require("../../lib/ffi"); + +var libfactorial = new FFI.Library("./libfactorial", { + "factorial": [ "uint64", [ "int" ] ] +}); + +if (process.argv.length < 3) { + console.log("Arguments: " + process.argv[0] + " " + process.argv[1] + " "); + process.exit(); +} + +var output = libfactorial.factorial(parseInt(process.argv[2])) + +console.log("Your output: " + output); diff --git a/node_modules/node-ffi/example/factorial/wscript b/node_modules/node-ffi/example/factorial/wscript new file mode 100644 index 000000000..eed556635 --- /dev/null +++ b/node_modules/node-ffi/example/factorial/wscript @@ -0,0 +1,24 @@ +import Options +from os import unlink, symlink, popen +from os.path import exists +from logging import fatal + +srcdir = '.' +blddir = 'build' + +def set_options(opt): + opt.tool_options('compiler_cc') + +def configure(conf): + conf.check_tool('compiler_cc') + +def build(bld): + obj = bld.new_task_gen('cc', 'shlib') + obj.target = 'factorial' + obj.source = 'factorial.c' + +def shutdown(): + if exists("build/default/libfactorial.dylib") and not exists("libfactorial.dylib"): + symlink("build/default/libfactorial.dylib", "libfactorial.dylib") + if exists("build/default/libfactorial.so") and not exists("libfactorial.so"): + symlink("build/default/libfactorial.so", "libfactorial.so") \ No newline at end of file diff --git a/node_modules/node-ffi/example/sqlite.js b/node_modules/node-ffi/example/sqlite.js new file mode 100644 index 000000000..ebc484e69 --- /dev/null +++ b/node_modules/node-ffi/example/sqlite.js @@ -0,0 +1,57 @@ +var FFI = require("../"), + 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); +}); diff --git a/node_modules/node-ffi/example/structs/struct.c b/node_modules/node-ffi/example/structs/struct.c new file mode 100644 index 000000000..c3ed2345d --- /dev/null +++ b/node_modules/node-ffi/example/structs/struct.c @@ -0,0 +1,25 @@ +#include + +struct test { + int a; + double b; + const char *c; +}; + +void inspect () { + printf("sizeof(struct test): %lu\n", sizeof(struct test)); +} + +double test_struct_arg_by_value (struct test by_value) { + printf("Input a: %d\n", by_value.a); + printf("Input c: %s\n", by_value.c); + return by_value.a + (double)by_value.b; +} + +struct test test_struct_rtn_by_value () { + struct test rtn; + rtn.a = 10; + rtn.b = 123.213; + rtn.c = "Does this work?"; + return rtn; +} diff --git a/node_modules/node-ffi/example/structs/struct.js b/node_modules/node-ffi/example/structs/struct.js new file mode 100644 index 000000000..378ff1893 --- /dev/null +++ b/node_modules/node-ffi/example/structs/struct.js @@ -0,0 +1,31 @@ +var FFI = require("../../lib/ffi"); + +var Test = FFI.Struct([ + ['int32', 'a'] + , ['double', 'b'] + , ['string', 'c'] +]); +console.log('Test Struct:', Test.__structInfo__) +//var i = new Test() +//console.log(i) + +var libstruct = new FFI.Library("./libstruct", { + test_struct_arg_by_value: [ 'double', [ Test ] ] + , test_struct_rtn_by_value: [ Test, [ ] ] + , inspect: ['void', [] ] +}); +console.log('After Library') + +libstruct.inspect() + +var output = libstruct.test_struct_rtn_by_value() + +console.log(output.a) +console.log(output.b) +console.log(output.c) + +output.c = 'test' + +var rtn = libstruct.test_struct_arg_by_value(output) + +console.log(rtn) diff --git a/node_modules/node-ffi/example/structs/wscript b/node_modules/node-ffi/example/structs/wscript new file mode 100644 index 000000000..0e0d3a7c2 --- /dev/null +++ b/node_modules/node-ffi/example/structs/wscript @@ -0,0 +1,24 @@ +import Options +from os import unlink, symlink, popen +from os.path import exists +from logging import fatal + +srcdir = '.' +blddir = 'build' + +def set_options(opt): + opt.tool_options('compiler_cc') + +def configure(conf): + conf.check_tool('compiler_cc') + +def build(bld): + obj = bld.new_task_gen('cc', 'shlib') + obj.target = 'struct' + obj.source = 'struct.c' + +def shutdown(): + if exists("build/default/libstruct.dylib") and not exists("libstruct.dylib"): + symlink("build/default/libstruct.dylib", "libstruct.dylib") + if exists("build/default/libstruct.so") and not exists("libstruct.so"): + symlink("build/default/libstruct.so", "libstruct.so") diff --git a/node_modules/node-ffi/lib/bindings.js b/node_modules/node-ffi/lib/bindings.js new file mode 100644 index 000000000..f9b46bf18 --- /dev/null +++ b/node_modules/node-ffi/lib/bindings.js @@ -0,0 +1,37 @@ + +var join = require('path').join + , bindings = 'ffi_bindings.node' + +function requireTry () { + + var i = 0 + , l = arguments.length + , n + + for (; i' +} + +/** + * Returns `true` if the given argument is a `Pointer` instance. + * Returns `false` otherwise. + * + * @param {Object} p A pointer object (possibly...) + * @return {Boolean} `true` if the object is a `Pointer` instance + */ + +Pointer.isPointer = function isPointer (p) { + return p instanceof Pointer +} + +/** + * Allocates a pointer big enough to fit *type* and *value*, writes the value, + * and returns it. + */ + +Pointer.alloc = function alloc (type, value) { + var size = type == 'string' + ? Buffer.byteLength(value, 'utf8') + 1 + : ffi.sizeOf(type) + + // malloc() the buffer + var ptr = new Pointer(size) + + // write the value + ptr['put' + ffi.TYPE_TO_POINTER_METHOD_MAP[type]](value) + + if (type == 'string') { + // XXX: consider removing this string special case. it's dumb. + // we have to actually build an "in-between" pointer for strings + var dptr = new ffi.Pointer(ffi.Bindings.TYPE_SIZE_MAP.pointer) + ptr.attach(dptr) // save it from garbage collection + dptr.putPointer(ptr) + return dptr + } + + return ptr +} + +/** + * Appends the `NON_SPECIFIC_TYPES` to the `TYPE_TO_POINTER_METHOD_MAP` by + * discovering the method suffix by type size. + */ + +Object.keys(ffi.NON_SPECIFIC_TYPES).forEach(function (type) { + var method = ffi.NON_SPECIFIC_TYPES[type] + , suffix = ffi.TYPE_TO_POINTER_METHOD_MAP[type] + + if (!suffix) { + // No hard mapping, determine by size + var size = ffi.sizeOf(type) + , szFunc = ffi.SIZE_TO_POINTER_METHOD_MAP[size] + , signed = type !== 'byte' && type != 'size_t' && type[0] != 'u' + suffix = (signed ? '' : 'U') + szFunc + } + + ffi.TYPE_TO_POINTER_METHOD_MAP[type] = suffix + + Pointer.prototype['put' + method] = Pointer.prototype['put' + suffix] + Pointer.prototype['get' + method] = Pointer.prototype['get' + suffix] +}) + +/** + * Define the `NULL` pointer. Used internally in other parts of node-ffi. + */ + +Pointer.NULL = new Pointer(0) diff --git a/node_modules/node-ffi/lib/struct.js b/node_modules/node-ffi/lib/struct.js new file mode 100644 index 000000000..d4c926d81 --- /dev/null +++ b/node_modules/node-ffi/lib/struct.js @@ -0,0 +1,182 @@ +var ffi = require('./ffi') + +/** + * An interface for modeling and instantiating C-style data structures. This is + * not a constructor per-say, but a constructor generator. It takes an array of + * tuples, the left side being the type, and the right side being a field name. + * The order should be the same order it would appear in the C-style struct + * definition. It returns a function that can be used to construct an object that + * reads and writes to the data structure using properties specified by the + * initial field list. + * + * Example: + * + * var PasswordEntry = ffi.Struct( + * ['string', 'username'] + * , ['string', 'password'] + * ) + * var pwd = new PasswordEntry() + * pwd.username = 'ricky' + * pwd.password = 'rbransonlovesnode.js' + */ + +function Struct () { + var struct = {} + , fields = arguments + + // Legacy API, pass an Array of Arrays + if (arguments.length > 0) { + var firstArg = arguments[0] + if (Array.isArray(firstArg) && firstArg.length > 0 && Array.isArray(firstArg[0])) { + fields = firstArg + } + } + + struct.struct = {} + struct.members = [] + struct.size = 0 + struct.alignment = 0 + + function read (ptr, name) { + var info = struct.struct[name] + var fptr = ptr.seek(info.offset) + + if (ffi.isStructType(info.type)) { + return new info.type(fptr) + } else if (info.type == 'string') { + return fptr.getPointer().getCString() + } else { + return fptr['get' + ffi.TYPE_TO_POINTER_METHOD_MAP[info.type]]() + } + } + + function write (ptr, name, val) { + var info = struct.struct[name] + var fptr = ptr.seek(info.offset) + + if (ffi.isStructType(info.type)) { + new info.type(fptr, val) + } else if (info.type == 'string') { + if (typeof val == 'undefined' || val === null) { + return fptr.putPointer(ffi.Pointer.NULL) + } + var len = Buffer.byteLength(val, 'utf8') + var strPtr = new ffi.Pointer(len+1) + strPtr.putCString(val) + fptr.putPointer(strPtr) + } else { + return fptr['put' + ffi.TYPE_TO_POINTER_METHOD_MAP[info.type]](val) + } + } + + // Read the fields list and apply all the fields to the struct + for (var i=0, len=fields.length; i left) { + offset += left + } + + struct.size = offset + sz + + struct.struct[name] = { + name: name + , type: type + , size: sz + , offset: offset + } + struct.members.push(name) + } + //console.log('before left:', struct.size, struct.alignment) + var left = struct.size % struct.alignment + if (left) { + struct.size += struct.alignment - left + } + //console.log('after left:', struct.size) + + var constructor = function (arg, data) { + if (!(this instanceof constructor)) { + return new constructor(arg, data) + } + if (ffi.Pointer.isPointer(arg)) { + this.pointer = arg + arg = data + } else { + this.pointer = new ffi.Pointer(struct.size) + } + if (arg) { + for (var key in arg) { + write(this.pointer, key, arg[key]) + } + } + } + + // Function to return an `FFI_TYPE` struct instance from this struct + constructor._ffiType = function ffiType () { + // return cached if available + if (this._ffiTypeCached) { + return this._ffiTypeCached + } + var props = this.__structInfo__.struct + , propNames = Object.keys(props) + , numProps = propNames.length + var t = new ffi.FFI_TYPE() + t.size = 0 + t.alignment = 0 + t.type = 13 // FFI_TYPE_STRUCT + t.elements = new ffi.Pointer(ffi.Bindings.POINTER_SIZE * (numProps+1)) + var tptr = t.elements.clone() + for (var i=0; i (http://tootallnate.net)" + ] +, "description": "A foreign function interface (FFI) for Node.js" +, "homepage": "http://github.com/rbranson/node-ffi" +, "engines": { "node": ">=0.6.0" } +, "main": "./lib/ffi" +, "devDependencies": { + "expect.js": "*" + , "mocha": "*" + } +, "scripts": { + "test": "node --expose-gc ./node_modules/mocha/bin/_mocha" + } +, "repository": { + "type": "git" + , "url": "http://github.com/rbranson/node-ffi.git" + } +, "bugs": { + "url": "http://github.com/rbranson/node-ffi/issues" + } +} diff --git a/node_modules/node-ffi/src/callback_info.cc b/node_modules/node-ffi/src/callback_info.cc new file mode 100644 index 000000000..79380ab64 --- /dev/null +++ b/node_modules/node-ffi/src/callback_info.cc @@ -0,0 +1,156 @@ +#include "ffi.h" + +Persistent CallbackInfo::callback_template; +pthread_t CallbackInfo::g_mainthread; +pthread_mutex_t CallbackInfo::g_queue_mutex; +std::queue CallbackInfo::g_queue; +uv_async_t CallbackInfo::g_async; + + +CallbackInfo::CallbackInfo(Handle func, void *closure, void *code) { + m_function = Persistent::New(func); + m_closure = closure; + this->code = code; +} + +CallbackInfo::~CallbackInfo() { + ffi_closure_free(m_closure); + m_function.Dispose(); +} + +void CallbackInfo::DispatchToV8(CallbackInfo *self, void *retval, void **parameters) { + HandleScope scope; + + Handle argv[2]; + argv[0] = Pointer::WrapPointer((unsigned char *)retval); + argv[1] = Pointer::WrapPointer((unsigned char *)parameters); + + TryCatch try_catch; + + self->m_function->Call(self->m_this, 2, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } +} + +void CallbackInfo::WatcherCallback(uv_async_t *w, int revents) { + pthread_mutex_lock(&g_queue_mutex); + + while (!g_queue.empty()) { + ThreadedCallbackInvokation *inv = g_queue.front(); + g_queue.pop(); + + DispatchToV8(inv->m_cbinfo, inv->m_retval, inv->m_parameters); + inv->SignalDoneExecuting(); + } + + pthread_mutex_unlock(&g_queue_mutex); +} + +void CallbackInfo::Initialize(Handle target) { + HandleScope scope; + + if (callback_template.IsEmpty()) { + callback_template = Persistent::New(MakeTemplate()); + } + + Handle t = callback_template; + + target->Set(String::NewSymbol("CallbackInfo"), t->GetFunction()); + + // initialize our threaded invokation stuff + g_mainthread = pthread_self(); + uv_async_init(uv_default_loop(), &g_async, CallbackInfo::WatcherCallback); + pthread_mutex_init(&g_queue_mutex, NULL); + + // allow the event loop to exit while this is running + uv_unref(uv_default_loop()); +} + +Handle CallbackInfo::New(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 2) { + return ThrowException(String::New("Not enough arguments.")); + } + + // Args: cif pointer, JS function + // TODO: Check args + Pointer *cif = ObjectWrap::Unwrap(args[0]->ToObject()); + Local callback = Local::Cast(args[1]); + ffi_closure *closure; + ffi_status status; + void *code; + + closure = (ffi_closure *)ffi_closure_alloc(sizeof(ffi_closure), &code); + + if (!closure) { + return ThrowException(String::New("ffi_closure_alloc() Returned Error")); + } + + CallbackInfo *self = new CallbackInfo(callback, closure, code); + + status = ffi_prep_closure_loc( + closure, + (ffi_cif *)cif->GetPointer(), + Invoke, + (void *)self, + code + ); + + if (status != FFI_OK) { + delete self; + return ThrowException(String::New("ffi_prep_closure() Returned Error")); + } + + self->Wrap(args.This()); + self->m_this = args.This(); + + return scope.Close(args.This()); +} + +Handle CallbackInfo::MakeTemplate() { + HandleScope scope; + + Handle t = FunctionTemplate::New(New); + + Local inst = t->InstanceTemplate(); + inst->SetInternalFieldCount(1); + inst->SetAccessor(String::NewSymbol("pointer"), GetPointer); + + return scope.Close(t); +} + +void CallbackInfo::Invoke(ffi_cif *cif, void *retval, void **parameters, void *user_data) { + CallbackInfo *self = (CallbackInfo *)user_data; + + // are we executing from another thread? + if (pthread_equal(pthread_self(), g_mainthread)) { + DispatchToV8(self, retval, parameters); + } else { + // create a temporary storage area for our invokation parameters + ThreadedCallbackInvokation *inv = new ThreadedCallbackInvokation(self, retval, parameters); + + // push it to the queue -- threadsafe + pthread_mutex_lock(&g_queue_mutex); + g_queue.push(inv); + pthread_mutex_unlock(&g_queue_mutex); + + // send a message to our main thread to wake up the WatchCallback loop + uv_async_send(&g_async); + + // wait for signal from calling thread + inv->WaitForExecution(); + + delete inv; + } +} + +Handle CallbackInfo::GetPointer(Local name, const AccessorInfo& info) { + HandleScope scope; + + CallbackInfo *self = ObjectWrap::Unwrap(info.Holder()); + Handle ptr = Pointer::WrapPointer((unsigned char *)self->m_closure); + return scope.Close(ptr); +} diff --git a/node_modules/node-ffi/src/ffi.cc b/node_modules/node-ffi/src/ffi.cc new file mode 100644 index 000000000..1d021ba8d --- /dev/null +++ b/node_modules/node-ffi/src/ffi.cc @@ -0,0 +1,184 @@ +#include "ffi.h" + +/////////////// + +void FFI::InitializeStaticFunctions(Handle target) { + Local o = Object::New(); + + // atoi and abs here for testing purposes + o->Set(String::New("atoi"), Pointer::WrapPointer((unsigned char *)atoi)); + // Windows has multiple `abs` signatures, so we need to manually disambiguate + int (*absPtr)(int)(abs); + o->Set(String::New("abs"), Pointer::WrapPointer((unsigned char *)absPtr)); + + // dl functions used by the DynamicLibrary JS class + o->Set(String::New("dlopen"), Pointer::WrapPointer((unsigned char *)dlopen)); + o->Set(String::New("dlclose"), Pointer::WrapPointer((unsigned char *)dlclose)); + o->Set(String::New("dlsym"), Pointer::WrapPointer((unsigned char *)dlsym)); + o->Set(String::New("dlerror"), Pointer::WrapPointer((unsigned char *)dlerror)); + + target->Set(String::NewSymbol("StaticFunctions"), o); +} + +/////////////// + +void FFI::InitializeBindings(Handle target) { + Local o = Object::New(); + + target->Set(String::New("free"), FunctionTemplate::New(Free)->GetFunction()); + target->Set(String::New("prepCif"), FunctionTemplate::New(FFIPrepCif)->GetFunction()); + target->Set(String::New("strtoul"), FunctionTemplate::New(Strtoul)->GetFunction()); + target->Set(String::New("POINTER_SIZE"), Integer::New(sizeof(unsigned char *))); + target->Set(String::New("FFI_TYPE_SIZE"), Integer::New(sizeof(ffi_type))); + + bool hasObjc = false; +#if __OBJC__ || __OBJC2__ + hasObjc = true; +#endif + target->Set(String::New("HAS_OBJC"), Boolean::New(hasObjc), static_cast(ReadOnly|DontDelete)); + + Local smap = Object::New(); + smap->Set(String::New("byte"), Integer::New(sizeof(unsigned char))); + smap->Set(String::New("int8"), Integer::New(sizeof(int8_t))); + smap->Set(String::New("uint8"), Integer::New(sizeof(uint8_t))); + smap->Set(String::New("int16"), Integer::New(sizeof(int16_t))); + smap->Set(String::New("uint16"), Integer::New(sizeof(uint16_t))); + smap->Set(String::New("int32"), Integer::New(sizeof(int32_t))); + smap->Set(String::New("uint32"), Integer::New(sizeof(uint32_t))); + smap->Set(String::New("int64"), Integer::New(sizeof(int64_t))); + smap->Set(String::New("uint64"), Integer::New(sizeof(uint64_t))); + smap->Set(String::New("char"), Integer::New(sizeof(char))); + smap->Set(String::New("uchar"), Integer::New(sizeof(unsigned char))); + smap->Set(String::New("short"), Integer::New(sizeof(short))); + smap->Set(String::New("ushort"), Integer::New(sizeof(unsigned short))); + smap->Set(String::New("int"), Integer::New(sizeof(int))); + smap->Set(String::New("uint"), Integer::New(sizeof(unsigned int))); + smap->Set(String::New("long"), Integer::New(sizeof(long))); + smap->Set(String::New("ulong"), Integer::New(sizeof(unsigned long))); + smap->Set(String::New("longlong"), Integer::New(sizeof(long long))); + smap->Set(String::New("ulonglong"), Integer::New(sizeof(unsigned long long))); + smap->Set(String::New("float"), Integer::New(sizeof(float))); + smap->Set(String::New("double"), Integer::New(sizeof(double))); + smap->Set(String::New("pointer"), Integer::New(sizeof(unsigned char *))); + smap->Set(String::New("string"), Integer::New(sizeof(char *))); + smap->Set(String::New("size_t"), Integer::New(sizeof(size_t))); + // Size of a Persistent handle to a JS object + smap->Set(String::New("Object"), Integer::New(sizeof(Persistent))); + + Local ftmap = Object::New(); + ftmap->Set(String::New("void"), Pointer::WrapPointer((unsigned char *)&ffi_type_void)); + ftmap->Set(String::New("byte"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint8)); + ftmap->Set(String::New("int8"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint8)); + ftmap->Set(String::New("uint8"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint8)); + ftmap->Set(String::New("uint16"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint16)); + ftmap->Set(String::New("int16"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint16)); + ftmap->Set(String::New("uint32"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint32)); + ftmap->Set(String::New("int32"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint32)); + ftmap->Set(String::New("uint64"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint64)); + ftmap->Set(String::New("int64"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint64)); + ftmap->Set(String::New("uchar"), Pointer::WrapPointer((unsigned char *)&ffi_type_uchar)); + ftmap->Set(String::New("char"), Pointer::WrapPointer((unsigned char *)&ffi_type_schar)); + ftmap->Set(String::New("ushort"), Pointer::WrapPointer((unsigned char *)&ffi_type_ushort)); + ftmap->Set(String::New("short"), Pointer::WrapPointer((unsigned char *)&ffi_type_sshort)); + ftmap->Set(String::New("uint"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint)); + ftmap->Set(String::New("int"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint)); + ftmap->Set(String::New("float"), Pointer::WrapPointer((unsigned char *)&ffi_type_float)); + ftmap->Set(String::New("double"), Pointer::WrapPointer((unsigned char *)&ffi_type_double)); + ftmap->Set(String::New("pointer"), Pointer::WrapPointer((unsigned char *)&ffi_type_pointer)); + ftmap->Set(String::New("string"), Pointer::WrapPointer((unsigned char *)&ffi_type_pointer)); + ftmap->Set(String::New("size_t"), Pointer::WrapPointer((unsigned char *)&ffi_type_pointer)); + + // libffi is weird when it comes to long data types (defaults to 64-bit), so we emulate here, since + // some platforms have 32-bit longs and some platforms have 64-bit longs. + if (sizeof(long) == 4) { + ftmap->Set(String::New("ulong"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint32)); + ftmap->Set(String::New("long"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint32)); + } else if (sizeof(long) == 8) { + ftmap->Set(String::New("ulong"), Pointer::WrapPointer((unsigned char *)&ffi_type_uint64)); + ftmap->Set(String::New("long"), Pointer::WrapPointer((unsigned char *)&ffi_type_sint64)); + } + + // Let libffi handle "long long" + ftmap->Set(String::New("ulonglong"),Pointer::WrapPointer((unsigned char *)&ffi_type_ulong)); + ftmap->Set(String::New("longlong"), Pointer::WrapPointer((unsigned char *)&ffi_type_slong)); + + target->Set(String::New("FFI_TYPES"), ftmap); + target->Set(String::New("TYPE_SIZE_MAP"), smap); +} + +Handle FFI::Free(const Arguments &args) { + HandleScope scope; + + Pointer *p = ObjectWrap::Unwrap(args[0]->ToObject()); + free(p->GetPointer()); + return Undefined(); +} + +/** + * Hard-coded `stftoul` binding, for the benchmarks. + */ + +Handle FFI::Strtoul(const Arguments &args) { + HandleScope scope; + + Pointer *middle = ObjectWrap::Unwrap(args[1]->ToObject()); + char buf[128]; + args[0]->ToString()->WriteUtf8(buf); + + unsigned long val = strtoul(buf, (char **)middle->GetPointer(), args[2]->Int32Value()); + + return scope.Close(Integer::NewFromUnsigned(val)); +} + +/** + * Function that creates and returns an `ffi_cif` pointer from the given return + * value type and argument types. + */ + +Handle FFI::FFIPrepCif(const Arguments& args) { + HandleScope scope; + + unsigned int nargs; + Pointer *rtype, *atypes, *cif; + ffi_status status; + + if (args.Length() != 3) { + return THROW_ERROR_EXCEPTION("prepCif() requires 3 arguments!"); + } + + nargs = args[0]->Uint32Value(); + rtype = ObjectWrap::Unwrap(args[1]->ToObject()); + atypes = ObjectWrap::Unwrap(args[2]->ToObject()); + + cif = new Pointer(NULL); + cif->Alloc(sizeof(ffi_cif)); + + status = ffi_prep_cif( + (ffi_cif *)cif->GetPointer(), + FFI_DEFAULT_ABI, + nargs, + (ffi_type *)rtype->GetPointer(), + (ffi_type **)atypes->GetPointer()); + + if (status != FFI_OK) { + delete cif; + return THROW_ERROR_EXCEPTION("ffi_prep_cif() returned error."); + } + + return scope.Close(Pointer::WrapInstance(cif)); +} + +/////////////// + +extern "C" { + static void init(Handle target) { + HandleScope scope; + + Pointer::Initialize(target); + FFI::InitializeBindings(target); + FFI::InitializeStaticFunctions(target); + CallbackInfo::Initialize(target); + ForeignCaller::Initialize(target); + } + NODE_MODULE(ffi_bindings, init); +} diff --git a/node_modules/node-ffi/src/ffi.h b/node_modules/node-ffi/src/ffi.h new file mode 100644 index 000000000..190ed3f41 --- /dev/null +++ b/node_modules/node-ffi/src/ffi.h @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#define __STDC_LIMIT_MACROS true +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#if __OBJC__ || __OBJC2__ + #include +#endif + +#ifdef _WIN32 + #define snprintf _snprintf_s + #define strtoll _strtoi64 + #define strtoull _strtoui64 +#endif + +#include "node_async_shim.h" + +#define INTEGER_CONVERSION_BUFFER_SIZE 64 + +#define UINT8_MIN 0 +#define UINT16_MIN 0 +#define UINT32_MIN 0 +#define UINT64_MIN 0 + +#define THROW_ERROR_EXCEPTION(x) ThrowException(Exception::Error(String::New(x))) + +#define STR_TO_INT64(x) strtoll(x, NULL, 0) +#define STR_TO_UINT64(x) strtoull(x, NULL, 0) + +using namespace v8; +using namespace node; + +class Pointer : public ObjectWrap { + public: + Pointer(unsigned char *ptr); + ~Pointer(); + + static void Initialize(Handle Target); + static Handle WrapInstance(Pointer *inst); + static Handle WrapPointer(unsigned char *ptr); + unsigned char *GetPointer(); + void MovePointer(int bytes); + Handle Alloc(size_t bytes); + + protected: + static Handle New(const Arguments& args); + static Handle Seek(const Arguments& args); + static Handle PutInt8(const Arguments& args); + static Handle GetInt8(const Arguments& args); + static Handle PutUInt8(const Arguments& args); + static Handle GetUInt8(const Arguments& args); + static Handle PutInt16(const Arguments& args); + static Handle GetInt16(const Arguments& args); + static Handle PutUInt16(const Arguments& args); + static Handle GetUInt16(const Arguments& args); + static Handle PutInt32(const Arguments& args); + static Handle GetInt32(const Arguments& args); + static Handle PutUInt32(const Arguments& args); + static Handle GetUInt32(const Arguments& args); + static Handle PutInt64(const Arguments& args); + static Handle GetInt64(const Arguments& args); + static Handle PutUInt64(const Arguments& args); + static Handle GetUInt64(const Arguments& args); + static Handle PutFloat(const Arguments& args); + static Handle GetFloat(const Arguments& args); + static Handle PutDouble(const Arguments& args); + static Handle GetDouble(const Arguments& args); + static Handle PutPointerMethod(const Arguments& args); + static Handle GetPointerMethod(const Arguments& args); + static Handle PutObject(const Arguments& args); + static Handle GetObject(const Arguments& args); + static Handle PutCString(const Arguments& args); + static Handle GetCString(const Arguments& args); + static Handle IsNull(const Arguments& args); + static Handle ToBuffer(const Arguments& args); + + static void unref_pointer_callback(char *data, void *hint); + + static Handle GetAddress(Local name, const AccessorInfo& info); + static Handle GetAllocated(Local name, const AccessorInfo& info); + static Handle GetFree(Local name, const AccessorInfo& info); + static void SetFree(Local name, Local value, const AccessorInfo& info); + + private: + static Persistent pointer_template; + static Handle MakeTemplate(); + unsigned char *m_ptr; + unsigned char *origPtr; + unsigned int m_allocated; + bool doFree; +}; + +class AsyncCallParams { + public: + ffi_cif *cif; + void (*ptr)(void); + void *res; + void **args; + Persistent emitter; +}; + +class FFI : public ObjectWrap { + public: + static void InitializeStaticFunctions(Handle Target); + static void InitializeBindings(Handle Target); + + protected: + static Handle Free(const Arguments& args); + static Handle FFIPrepCif(const Arguments& args); + static Handle Strtoul(const Arguments& args); +}; + +class ForeignCaller : public ObjectWrap { + public: + ForeignCaller(); + ~ForeignCaller(); + static void Initialize(Handle Target); + + protected: + static Handle New(const Arguments& args); + static Handle Exec(const Arguments& args); + static async_rtn AsyncFFICall(uv_work_t *req); + static async_rtn FinishAsyncFFICall(uv_work_t *req); + + ffi_cif *m_cif; + void (*m_fn)(void); + void *m_res; + void **m_fnargs; + + bool m_async; + + private: + static Persistent foreign_caller_template; + static Handle MakeTemplate(); +}; + +class ThreadedCallbackInvokation; + +class CallbackInfo : public ObjectWrap { + public: + CallbackInfo(Handle func, void *closure, void *code); + ~CallbackInfo(); + static void Initialize(Handle Target); + Handle GetPointerObject(); + static void WatcherCallback(uv_async_t *w, int revents); + + protected: + static void DispatchToV8(CallbackInfo *self, void *retval, void **parameters); + static Handle New(const Arguments& args); + static Handle GetPointer(Local name, const AccessorInfo& info); + static void Invoke(ffi_cif *cif, void *retval, void **parameters, void *user_data); + + private: + static Persistent callback_template; + static Handle MakeTemplate(); + + static pthread_t g_mainthread; + static pthread_mutex_t g_queue_mutex; + static std::queue g_queue; + static uv_async_t g_async; + + void *m_closure; + void *code; + Persistent m_function; + Handle m_this; +}; + +class ThreadedCallbackInvokation { + public: + ThreadedCallbackInvokation(CallbackInfo *cbinfo, void *retval, void **parameters); + ~ThreadedCallbackInvokation(); + + void SignalDoneExecuting(); + void WaitForExecution(); + + void *m_retval; + void **m_parameters; + CallbackInfo *m_cbinfo; + + private: + pthread_cond_t m_cond; + pthread_mutex_t m_mutex; +}; diff --git a/node_modules/node-ffi/src/foreign_caller.cc b/node_modules/node-ffi/src/foreign_caller.cc new file mode 100644 index 000000000..4cdc6153d --- /dev/null +++ b/node_modules/node-ffi/src/foreign_caller.cc @@ -0,0 +1,135 @@ +#include "ffi.h" + +ForeignCaller::ForeignCaller() { +} + +ForeignCaller::~ForeignCaller() { +} + +Persistent ForeignCaller::foreign_caller_template; + +Handle ForeignCaller::MakeTemplate() { + HandleScope scope; + Handle t = FunctionTemplate::New(New); + + Local inst = t->InstanceTemplate(); + inst->SetInternalFieldCount(1); + + return scope.Close(t); +} + +void ForeignCaller::Initialize(Handle target) { + HandleScope scope; + + if (foreign_caller_template.IsEmpty()) { + foreign_caller_template = Persistent::New(MakeTemplate()); + } + + Handle t = foreign_caller_template; + + NODE_SET_PROTOTYPE_METHOD(t, "exec", Exec); + + target->Set(String::NewSymbol("ForeignCaller"), t->GetFunction()); +} + +Handle ForeignCaller::New(const Arguments& args) { + HandleScope scope; + ForeignCaller *self = new ForeignCaller(); + + if (args.Length() != 5) { + return THROW_ERROR_EXCEPTION("new ForeignCaller() requires 5 arguments!"); + } + + Pointer *cif = ObjectWrap::Unwrap(args[0]->ToObject()); + Pointer *fn = ObjectWrap::Unwrap(args[1]->ToObject()); + Pointer *fnargs = ObjectWrap::Unwrap(args[2]->ToObject()); + Pointer *res = ObjectWrap::Unwrap(args[3]->ToObject()); + + self->m_cif = (ffi_cif *)cif->GetPointer(); + self->m_fn = (void (*)(void))fn->GetPointer(); + self->m_res = (void *)res->GetPointer(); + self->m_fnargs = (void **)fnargs->GetPointer(); + self->m_async = args[4]->BooleanValue(); + + self->Wrap(args.This()); + return scope.Close(args.This()); +} + +Handle ForeignCaller::Exec(const Arguments& args) { + HandleScope scope; + ForeignCaller *self = ObjectWrap::Unwrap(args.This()); + + if (self->m_async) { + AsyncCallParams *p = new AsyncCallParams(); + + // cuter way of doing this? + p->cif = self->m_cif; + p->ptr = self->m_fn; + p->res = self->m_res; + p->args = self->m_fnargs; + + // get the events.EventEmitter constructor + Local global = Context::GetCurrent()->Global(); + Local events = global->Get(String::NewSymbol("process"))->ToObject(); + Local emitterConstructor = Local::Cast(events->Get(String::NewSymbol("EventEmitter"))); + + // construct a new EventEmitter object + p->emitter = Persistent::New(emitterConstructor->NewInstance()); + + BEGIN_ASYNC(p, ForeignCaller::AsyncFFICall, ForeignCaller::FinishAsyncFFICall); + return scope.Close(p->emitter); + } else { +#if __OBJC__ || __OBJC2__ + @try { +#endif + ffi_call( + self->m_cif, + self->m_fn, + self->m_res, + self->m_fnargs + ); +#if __OBJC__ || __OBJC2__ + } @catch (id ex) { + return ThrowException(Pointer::WrapPointer((unsigned char *)ex)); + } +#endif + } + + return Undefined(); +} + +async_rtn ForeignCaller::AsyncFFICall(uv_work_t *req) { + AsyncCallParams *p = (AsyncCallParams *)req->data; + ffi_call(p->cif, p->ptr, p->res, p->args); + RETURN_ASYNC; +} + +async_rtn ForeignCaller::FinishAsyncFFICall(uv_work_t *req) { + HandleScope scope; + + AsyncCallParams *p = (AsyncCallParams *)req->data; + Local argv[1]; + + argv[0] = Local::New(String::New("success")); + + // get a reference to the 'emit' function + Local emitVal = p->emitter->Get(String::NewSymbol("emit")); + Local emit = Local::Cast(emitVal); + + TryCatch try_catch; + + // emit a success event + emit->Call(p->emitter, 1, argv); + + // dispose of our persistent handle to the EventEmitter object + p->emitter.Dispose(); + + // free up our memory (allocated in FFICall) + delete p; + + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + + RETURN_ASYNC_AFTER; +} diff --git a/node_modules/node-ffi/src/node_async_shim.h b/node_modules/node-ffi/src/node_async_shim.h new file mode 100644 index 000000000..c531e36dc --- /dev/null +++ b/node_modules/node-ffi/src/node_async_shim.h @@ -0,0 +1,42 @@ +/** + * eio_custom() vs. uv_queue_work() file. + * Original gist: https://gist.github.com/1368935 + * + * Copyright (c) 2011-2012, Nathan Rajlich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* Node Thread Pool version compat */ +#if NODE_VERSION_AT_LEAST(0, 5, 6) + #define BEGIN_ASYNC(_data, async, after) \ + uv_work_t *_req = new uv_work_t; \ + _req->data = _data; \ + uv_queue_work(uv_default_loop(), _req, async, after); + typedef void async_rtn; + #define RETURN_ASYNC + #define RETURN_ASYNC_AFTER delete req; +#else + #define BEGIN_ASYNC(data, async, after) \ + ev_ref(EV_DEFAULT_UC); \ + eio_custom(async, EIO_PRI_DEFAULT, after, data); + typedef int async_rtn; + typedef eio_req uv_work_t; + #define RETURN_ASYNC return 0; + #define RETURN_ASYNC_AFTER \ + ev_unref(EV_DEFAULT_UC); \ + RETURN_ASYNC; +#endif diff --git a/node_modules/node-ffi/src/pointer.cc b/node_modules/node-ffi/src/pointer.cc new file mode 100644 index 000000000..5b515e3de --- /dev/null +++ b/node_modules/node-ffi/src/pointer.cc @@ -0,0 +1,705 @@ +#include "ffi.h" +#include + +Pointer::Pointer(unsigned char *ptr) { + this->origPtr = ptr; + this->m_ptr = ptr; + this->m_allocated = 0; + this->doFree = false; + //fprintf(stderr, "Creating new Pointer %p\n", this->m_ptr); +} + +Pointer::~Pointer() { + if (this->doFree) { + //fprintf(stderr, "Pointer destructor called on ALLOCATED area: %p\n", this->m_ptr); + free(this->origPtr); + } +} + +Persistent Pointer::pointer_template; + +Handle Pointer::MakeTemplate() { + HandleScope scope; + Handle t = FunctionTemplate::New(New); + t->SetClassName(String::NewSymbol("Pointer")); + + Local inst = t->InstanceTemplate(); + inst->SetInternalFieldCount(1); + inst->SetAccessor(String::NewSymbol("address"), GetAddress); + inst->SetAccessor(String::NewSymbol("allocated"), GetAllocated); + inst->SetAccessor(String::NewSymbol("free"), GetFree, SetFree); + + return scope.Close(t); +} + +void Pointer::Initialize(Handle target) { + HandleScope scope; + + if (pointer_template.IsEmpty()) { + pointer_template = Persistent::New(MakeTemplate()); + } + + Handle t = pointer_template; + + NODE_SET_PROTOTYPE_METHOD(t, "seek", Seek); + NODE_SET_PROTOTYPE_METHOD(t, "putUInt8", PutUInt8); + NODE_SET_PROTOTYPE_METHOD(t, "getUInt8", GetUInt8); + NODE_SET_PROTOTYPE_METHOD(t, "putInt8", PutInt8); + NODE_SET_PROTOTYPE_METHOD(t, "getInt8", GetInt8); + NODE_SET_PROTOTYPE_METHOD(t, "putInt16", PutInt16); + NODE_SET_PROTOTYPE_METHOD(t, "getInt16", GetInt16); + NODE_SET_PROTOTYPE_METHOD(t, "putUInt16", PutUInt16); + NODE_SET_PROTOTYPE_METHOD(t, "getUInt16", GetUInt16); + NODE_SET_PROTOTYPE_METHOD(t, "putInt32", PutInt32); + NODE_SET_PROTOTYPE_METHOD(t, "getInt32", GetInt32); + NODE_SET_PROTOTYPE_METHOD(t, "putUInt32", PutUInt32); + NODE_SET_PROTOTYPE_METHOD(t, "getUInt32", GetUInt32); + NODE_SET_PROTOTYPE_METHOD(t, "putInt64", PutInt64); + NODE_SET_PROTOTYPE_METHOD(t, "getInt64", GetInt64); + NODE_SET_PROTOTYPE_METHOD(t, "putUInt64", PutUInt64); + NODE_SET_PROTOTYPE_METHOD(t, "getUInt64", GetUInt64); + NODE_SET_PROTOTYPE_METHOD(t, "putFloat", PutFloat); + NODE_SET_PROTOTYPE_METHOD(t, "getFloat", GetFloat); + NODE_SET_PROTOTYPE_METHOD(t, "putDouble", PutDouble); + NODE_SET_PROTOTYPE_METHOD(t, "getDouble", GetDouble); + NODE_SET_PROTOTYPE_METHOD(t, "_putPointer", PutPointerMethod); + NODE_SET_PROTOTYPE_METHOD(t, "getPointer", GetPointerMethod); + NODE_SET_PROTOTYPE_METHOD(t, "getObject", GetObject); + NODE_SET_PROTOTYPE_METHOD(t, "putObject", PutObject); + NODE_SET_PROTOTYPE_METHOD(t, "putCString", PutCString); + NODE_SET_PROTOTYPE_METHOD(t, "getCString", GetCString); + NODE_SET_PROTOTYPE_METHOD(t, "isNull", IsNull); + NODE_SET_PROTOTYPE_METHOD(t, "toBuffer", ToBuffer); + + target->Set(String::NewSymbol("Pointer"), t->GetFunction()); +} + +unsigned char *Pointer::GetPointer() { + return this->m_ptr; +} + +void Pointer::MovePointer(int bytes) { + this->m_ptr += bytes; +} + +Handle Pointer::Alloc(size_t bytes) { + if (!this->m_allocated && bytes > 0) { + this->m_ptr = (unsigned char *)malloc(bytes); + this->origPtr = this->m_ptr; + //fprintf(stderr, "malloc()'d %p\n", this->m_ptr); + + if (this->m_ptr != NULL) { + this->m_allocated = bytes; + + // Any allocated Pointer gets free'd by default + // This can be changed in JS-land with `free` + this->doFree = true; + } else { + return THROW_ERROR_EXCEPTION("malloc(): Could not allocate Memory"); + } + } + return Undefined(); +} + +/** + * Sentinel Object used to determine when Pointer::New() is being called from + * JS-land or from within a Pointer::WrapInstance() call. + */ + +static Persistent SENTINEL = Persistent::New(Object::New()); +static Persistent WRAP_ARGS[] = { SENTINEL }; + +Handle Pointer::WrapInstance(Pointer *inst) { + HandleScope scope; + + Local obj = pointer_template->GetFunction()->NewInstance(1, WRAP_ARGS); + inst->Wrap(obj); + return scope.Close(obj); +} + +Handle Pointer::WrapPointer(unsigned char *ptr) { + return WrapInstance(new Pointer(ptr)); +} + +Handle Pointer::New(const Arguments& args) { + HandleScope scope; + + int argc = args.Length(); + + if (argc < 1) { + return THROW_ERROR_EXCEPTION("new Pointer() requires at least 1 argument"); + } + + Handle arg0 = args[0]; + if (!arg0->StrictEquals(SENTINEL)) { + Pointer *self = new Pointer(NULL); + unsigned int sz = arg0->Uint32Value(); + self->Alloc(sz); + + if (argc >= 2) { + // Second argument specifies whether or not to call `free()` on the + // allocated buffer when this Pointer gets garbage collected + self->doFree = args[1]->BooleanValue(); + } + self->Wrap(args.This()); + } + + return scope.Close(args.This()); +} + +Handle Pointer::GetAddress(Local name, const AccessorInfo& info) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(info.Holder()); + Handle ret; + + ret = Number::New((size_t)self->GetPointer()); + + return scope.Close(ret); +} + +Handle Pointer::GetAllocated(Local name, const AccessorInfo& info) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(info.Holder()); + Handle ret = Integer::New(self->m_allocated); + return scope.Close(ret); +} + +Handle Pointer::GetFree(Local name, const AccessorInfo& info) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(info.Holder()); + Handle ret = Boolean::New(self->doFree); + return scope.Close(ret); +} + +void Pointer::SetFree(Local property, Local value, const AccessorInfo &info) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(info.Holder()); + Handle val = value->ToBoolean(); + self->doFree = val->BooleanValue(); +} + + +Handle Pointer::Seek(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + Handle ret; + + if (args.Length() > 0 && args[0]->IsNumber()) { + size_t offset = args[0]->IntegerValue(); + ret = WrapPointer(static_cast(self->GetPointer()) + offset); + } + else { + return THROW_ERROR_EXCEPTION("Must specify an offset"); + } + + return scope.Close(ret); +} + +Handle Pointer::PutUInt8(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= UINT8_MIN && val <= UINT8_MAX) { + uint8_t cvt = (uint8_t)val; + memcpy(ptr, &cvt, sizeof(uint8_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(uint8_t)); + } + + return Undefined(); +} + +Handle Pointer::GetUInt8(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(uint8_t)); + } + + return scope.Close(Integer::New(*ptr)); +} + +Handle Pointer::PutInt8(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= INT8_MIN && val <= INT8_MAX) { + int8_t cvt = (int8_t)val; + memcpy(ptr, &cvt, sizeof(int8_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(int8_t)); + } + + return Undefined(); +} + +Handle Pointer::GetInt8(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + int8_t val = *((int8_t *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(int8_t)); + } + + return scope.Close(Integer::New(val)); +} + + +Handle Pointer::PutInt16(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + // TODO: Exception handling here for out of range values + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= INT16_MIN && val <= INT16_MAX) { + int16_t cvt = (int16_t)val; + memcpy(ptr, &cvt, sizeof(int16_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(int16_t)); + } + + return Undefined(); +} + +Handle Pointer::GetInt16(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + int16_t val = *((int16_t *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(int16_t)); + } + + return scope.Close(Integer::New(val)); +} + +Handle Pointer::PutUInt16(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + // TODO: Exception handling here for out of range values + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= UINT16_MIN && val <= UINT16_MAX) { + uint16_t cvt = (uint16_t)val; + memcpy(ptr, &cvt, sizeof(uint16_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(uint16_t)); + } + + return Undefined(); +} + +Handle Pointer::GetUInt16(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + unsigned short val = *((uint16_t *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(uint16_t)); + } + + return scope.Close(Integer::New(val)); +} + +Handle Pointer::PutInt32(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= INT32_MIN && val <= INT32_MAX) { // XXX: Will this ever be false? + memcpy(ptr, &val, sizeof(int32_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(int32_t)); + } + + return Undefined(); +} + +Handle Pointer::GetInt32(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + int32_t val = *((int32_t *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(int32_t)); + } + + return scope.Close(Integer::New(val)); +} + +Handle Pointer::PutUInt32(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + int64_t val = args[0]->IntegerValue(); + + if (val >= UINT32_MIN && val <= UINT32_MAX) { // XXX: Will this ever be false? + memcpy(ptr, &val, sizeof(uint32_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(uint32_t)); + } + + return Undefined(); +} + +Handle Pointer::GetUInt32(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + uint32_t val = *((uint32_t *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(uint32_t)); + } + + return scope.Close(Integer::NewFromUnsigned(val)); +} + +Handle Pointer::PutInt64(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + // Have to do this because strtoll doesn't set errno to 0 on success :( + errno = 0; + + if (args.Length() >= 1) { + if (args[0]->IsNumber() || args[0]->IsString()) { + int64_t val; + + if (args[0]->IsNumber()) { + val = args[0]->IntegerValue(); + } + else { // assumed args[0]->IsString() from condition above + String::Utf8Value str(args[0]->ToString()); + val = STR_TO_INT64(*str); + } + + if (errno != ERANGE && (val >= INT64_MIN && val <= INT64_MAX)) { + memcpy(ptr, &val, sizeof(int64_t)); + } + else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(int64_t)); + } + + return Undefined(); +} + +Handle Pointer::GetInt64(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + int64_t val = *((int64_t *)ptr); + char buf[INTEGER_CONVERSION_BUFFER_SIZE]; + + memset(buf, 0, INTEGER_CONVERSION_BUFFER_SIZE); + snprintf(buf, INTEGER_CONVERSION_BUFFER_SIZE, "%lld", val); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(int64_t)); + } + + return scope.Close(String::New(buf)); +} + +Handle Pointer::PutUInt64(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + // Have to do this because strtoull doesn't set errno to 0 on success :( + errno = 0; + + if (args.Length() >= 1) { + if (args[0]->IsNumber() || args[0]->IsString()) { + uint64_t val; + + // Convert everything to a string because it's easier this way + String::Utf8Value str(args[0]->ToString()); + val = STR_TO_UINT64(*str); + + if ((*str)[0] != '-' && errno != ERANGE && val <= UINT64_MAX) { + memcpy(ptr, &val, sizeof(uint64_t)); + } else { + return THROW_ERROR_EXCEPTION("Value out of Range."); + } + } + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(uint64_t)); + } + + return Undefined(); +} + +Handle Pointer::GetUInt64(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + uint64_t val = *((uint64_t *)ptr); + char buf[INTEGER_CONVERSION_BUFFER_SIZE]; + + memset(buf, 0, INTEGER_CONVERSION_BUFFER_SIZE); + snprintf(buf, INTEGER_CONVERSION_BUFFER_SIZE, "%llu", val); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(uint64_t)); + } + + return scope.Close(String::New(buf)); +} + +Handle Pointer::PutFloat(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + float val = args[0]->NumberValue(); + memcpy(ptr, &val, sizeof(float)); + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(float)); + } + + return Undefined(); +} + +Handle Pointer::GetFloat(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + float val = *((float *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(float)); + } + + return scope.Close(Number::New((double)val)); +} + +Handle Pointer::PutDouble(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsNumber()) { + double val = args[0]->NumberValue(); + memcpy(ptr, &val, sizeof(double)); + } + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(double)); + } + + return Undefined(); +} + +Handle Pointer::GetDouble(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + double val = *((double *)ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(double)); + } + + return scope.Close(Number::New(val)); +} + +Handle Pointer::PutPointerMethod(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1) { + if (args[0]->IsNull()) { + *((unsigned char **)ptr) = NULL; + } + else { + Pointer *obj = ObjectWrap::Unwrap(args[0]->ToObject()); + *((unsigned char **)ptr) = obj->GetPointer(); + } + //printf("Pointer::PutPointerMethod: writing pointer %p at %p\n", *((unsigned char **)ptr), ptr); + + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(unsigned char *)); + } + } + + return Undefined(); +} + +Handle Pointer::GetPointerMethod(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + unsigned char *val = *((unsigned char **)ptr); + + //printf("Pointer::GetPointerMethod: got %p from %p\n", val, ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(unsigned char *)); + } + + return scope.Close(WrapPointer(val)); +} + +Handle Pointer::PutObject(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + + if (args.Length() >= 1) { + Local obj = args[0]; + *reinterpret_cast*>(self->GetPointer()) = Persistent::New(obj); + + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(sizeof(Persistent)); + } + } + + return Undefined(); +} + +Handle Pointer::GetObject(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + Persistent rtn = *reinterpret_cast*>(ptr); + + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(sizeof(Persistent)); + } + + return scope.Close(rtn); +} + +Handle Pointer::PutCString(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + unsigned char *ptr = self->GetPointer(); + + if (args.Length() >= 1 && args[0]->IsString()) { + args[0]->ToString()->WriteUtf8((char *)ptr); + + if (args.Length() == 2 && args[1]->IsBoolean() && args[1]->BooleanValue()) { + self->MovePointer(args[0]->ToString()->Utf8Length() + 1); + } + } + + return Undefined(); +} + +Handle Pointer::GetCString(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + char *val = (char *)self->GetPointer(); + + Local rtn = String::New(val); + if (args.Length() == 1 && args[0]->IsBoolean() && args[0]->BooleanValue()) { + self->MovePointer(rtn->Utf8Length() + 1); + } + + return scope.Close(rtn); +} + +Handle Pointer::IsNull(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + return scope.Close(Boolean::New(self->GetPointer() == NULL)); +} + +// Callback that gets invoked when the Buffer returned from ToBuffer is being freed +void Pointer::unref_pointer_callback(char *data, void *hint) { + Pointer *p = static_cast(hint); + //printf("Unref()ing pointer\n"); + p->Unref(); +} + +Handle Pointer::ToBuffer(const Arguments& args) { + HandleScope scope; + Pointer *self = ObjectWrap::Unwrap(args.This()); + + // Defaults to the size of the allocated Buffer area, but can be explicitly + // specified as the first argument. + unsigned int sz = self->m_allocated; + if (args.Length() == 1 && args[0]->IsNumber()) { + sz = args[0]->Uint32Value(); + } + if (sz > self->m_allocated) { + return ThrowException(Exception::Error(String::New("Out of bounds!"))); + } + + // http://sambro.is-super-awesome.com/2011/03/03/creating-a-proper-buffer-in-a-node-c-addon + Buffer *slowBuffer = Buffer::New((char *)self->GetPointer(), (size_t)sz, unref_pointer_callback, self); + Local globalObj = Context::GetCurrent()->Global(); + Local bufferConstructor = Local::Cast(globalObj->Get(String::New("Buffer"))); + Handle constructorArgs[3] = { slowBuffer->handle_, Integer::New(sz), Integer::New(0) }; + Local actualBuffer = bufferConstructor->NewInstance(3, constructorArgs); + + // increase the reference count for this Pointer + self->Ref(); + + return scope.Close(actualBuffer); +} diff --git a/node_modules/node-ffi/src/threaded_callback_invokation.cc b/node_modules/node-ffi/src/threaded_callback_invokation.cc new file mode 100644 index 000000000..6581a5251 --- /dev/null +++ b/node_modules/node-ffi/src/threaded_callback_invokation.cc @@ -0,0 +1,29 @@ +#include "ffi.h" + +ThreadedCallbackInvokation::ThreadedCallbackInvokation(CallbackInfo *cbinfo, void *retval, void **parameters) { + m_cbinfo = cbinfo; + m_retval = retval; + m_parameters = parameters; + + pthread_mutex_init(&m_mutex, NULL); + pthread_cond_init(&m_cond, NULL); + uv_ref(uv_default_loop()); // hold the event loop open while this is executing +} + +ThreadedCallbackInvokation::~ThreadedCallbackInvokation() { + uv_unref(uv_default_loop()); + pthread_cond_destroy(&m_cond); + pthread_mutex_destroy(&m_mutex); +} + +void ThreadedCallbackInvokation::SignalDoneExecuting() { + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_cond); + pthread_mutex_unlock(&m_mutex); +} + +void ThreadedCallbackInvokation::WaitForExecution() { + pthread_mutex_lock(&m_mutex); + pthread_cond_wait(&m_cond, &m_mutex); + pthread_mutex_unlock(&m_mutex); +} diff --git a/node_modules/node-ffi/test.old/test.js b/node_modules/node-ffi/test.old/test.js new file mode 100644 index 000000000..fd61e910e --- /dev/null +++ b/node_modules/node-ffi/test.old/test.js @@ -0,0 +1,348 @@ +var util = require("util"); + +var assert = require("assert"), + FFI = require("../lib/ffi"), + rss = process.memoryUsage()["rss"]; + +var Pointer = FFI.Pointer; + +//////////////// + +assert.throws(function() { ptr.putInt32(Math.pow(2, 31) + 1); }); +assert.throws(function() { ptr.putInt32(0 - Math.pow(2, 31) - 1); }); + +ptr.putInt32(0 - Math.pow(2, 31)); +assert.equal(0 - Math.pow(2, 31), ptr.getInt32()); + +assert.throws(function() { ptr.putUInt32(-1); }); +assert.throws(function() { ptr.putUInt32(Math.pow(2, 32)); }); + +ptr.putUInt32(Math.pow(2, 32) - 1); +assert.equal(Math.pow(2, 32) - 1, ptr.getUInt32()); + +//////////////// + +// This checks for int64 behavior remaining the same (V8 using floats for these numbers) +assert.notEqual(Math.pow(2, 64).toString(), "18446744073709551616"); +assert.notEqual(Math.pow(2, 63).toString(), "9223372036854775808"); + +//assert.throws(function() { ptr.putInt64(-9223372036854785808); }); +//assert.throws(function() { ptr.putInt64(9223372036854785808); }); + +ptr.putInt64(0 - Math.pow(2, 63)); +assert.equal(0 - Math.pow(2, 63), ptr.getInt64()); + +assert.throws(function() { ptr.putUInt64(-1); }); +assert.throws(function() { ptr.putUInt64(18446744073709551616); }); + +ptr.putUInt64(Math.pow(2, 63) - 10000); +assert.equal(Math.pow(2, 63) - 10000, ptr.getUInt64()); + +// check for string support +assert.throws(function() { ptr.putInt64("9223372036854775808"); }); +assert.throws(function() { ptr.putInt64("-9223372036854775809"); }); + +// allows INT64_MAX value +ptr.putInt64("9223372036854775807"); +assert.equal("9223372036854775807", ptr.getInt64()); + +// allows INT64_MIN value +ptr.putInt64("-9223372036854775808"); +assert.equal("-9223372036854775808", ptr.getInt64()); + +// Uint should throw error on value > UINT64_MAX or negative number +assert.throws(function() { ptr.putUInt64("18446744073709551616"); }); +assert.throws(function() { ptr.putUInt64("-1"); }); + +// allows UINT64_MAX value +ptr.putUInt64("18446744073709551615"); +assert.equal("18446744073709551615", ptr.getUInt64()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUChar(-8); +}); +ptr.putChar(-8); +assert.ok(ptr.getUChar() != 8 && ptr.getUChar() > 0); + +ptr.putUChar(8); +assert.equal(8, ptr.getUChar()); + +/////////////////////////////// + +ptr.putShort(9); +assert.equal(9, ptr.getShort()); + +ptr.putShort(-9); +assert.equal(-9, ptr.getShort()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUShort(-8); +}); +ptr.putShort(-8); +assert.ok(ptr.getUShort() != 8 && ptr.getUShort() > 0); + +ptr.putUShort(11); +assert.equal(11, ptr.getUShort()); + +/////////////////////////////// + +ptr.putInt(12); +assert.equal(12, ptr.getInt()); + +ptr.putInt(-12); +assert.equal(-12, ptr.getInt()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUInt(-8); +}); +ptr.putInt(-8); +assert.ok(ptr.getUInt() != 8 && ptr.getUInt() > 0); + +ptr.putUInt(13); +assert.equal(13, ptr.getUInt()); + +/////////////////////////////// + +ptr.putLong(14); +assert.equal(14, ptr.getLong()); + +ptr.putLong(-14); +assert.equal(-14, ptr.getLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putULong(-8); +}); +ptr.putLong(-8); +assert.ok(ptr.getULong() != 8 && ptr.getULong() > 0); + +ptr.putULong(15); +assert.equal(15, ptr.getULong()); + +/////////////////////////////// + +ptr.putLongLong(16); +assert.equal(16, ptr.getLongLong()); + +ptr.putLongLong(-16); +assert.equal(-16, ptr.getLongLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putULongLong(-8); +}); +ptr.putLongLong(-8); +assert.ok(ptr.getULongLong() != 8 && ptr.getULongLong() > 0); + +ptr.putULongLong(17); +assert.equal(17, ptr.getULongLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putSizeT(-1); +}); + +ptr.putSizeT(18); +assert.equal(18, ptr.getSizeT()); + +// test put + advance calls +var advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putByte(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt8(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt16(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt16(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt32(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt32(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt64(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt64(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putFloat(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putDouble(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putPointer(ptr, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putCString("hi", true); +assert.ok(advptr.address > ptr.address); + +// test get + advance calls + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getByte(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt8(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt16(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt16(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt32(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt32(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt64(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt64(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getFloat(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getDouble(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getPointer(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getCString(true); +assert.ok(advptr.address > ptr.address); + +////////////////////// + +var tcif = new FFI.CIF("int32", ["int32"]); +assert.ok(tcif.getArgTypesPointer() instanceof FFI.Pointer); +var cifat = tcif.getArgTypesPointer().seek(0); +assert.equal(FFI.Bindings.FFI_TYPES["int32"].address, cifat.getPointer(true).address); + +////////////////////// + +var builtValuePtr = FFI.Pointer.alloc("int32", 1234); +assert.equal(1234, builtValuePtr.getInt32()); +assert.equal(1234, FFI.derefValuePtr("int32", builtValuePtr)); + +var int32Derefer = FFI.derefValuePtrFunc("int32"); +assert.equal(1234, int32Derefer(builtValuePtr)); + +var builtStringPtr = FFI.Pointer.alloc("string", "Hello World!"); +assert.equal("Hello World!", FFI.derefValuePtr("string", builtStringPtr)); + +var stringDerefer = FFI.derefValuePtrFunc("string"); +assert.equal("Hello World!", stringDerefer(builtStringPtr)); + +////////////////////// + +var libm = new FFI.DynamicLibrary("libm" + FFI.PLATFORM_LIBRARY_EXTENSIONS[process.platform], FFI.DynamicLibrary.FLAGS.RTLD_NOW); +assert.ok(libm instanceof FFI.DynamicLibrary); + +var ceilPtr = libm.get("ceil"); +assert.ok(ceilPtr instanceof FFI.Pointer); +assert.ok(!ceilPtr.isNull()); + +var ceil = FFI.ForeignFunction.build(ceilPtr, "double", [ "double" ]); +assert.ok(ceil instanceof Function); + +assert.equal(2, ceil(1.5)); + +libm.close(); + +/////////////////////// + +var closureCalled = 0; +var cifPtr = new FFI.CIF("int32", [ "int32" ]); +var clz = new FFI.CallbackInfo(cifPtr.getPointer(), function(result, args) { + closureCalled++; +}); + +var callMyTestClosure = FFI.ForeignFunction.build(clz.pointer, "int32", [ "int32" ]); +callMyTestClosure(1); +assert.equal(1, closureCalled); +callMyTestClosure(1); +assert.equal(2, closureCalled); + +/////////////////////// + +// Test string argument NULL handling +var memcpyLib = new FFI.Library(null, { "memcpy": [ "int", [ "pointer", "string", "size_t" ] ] }); +var bufPtr = new Pointer(128); + +memcpyLib.memcpy(bufPtr, "1234", 5); +assert.equal("1234", bufPtr.getCString()); + +memcpyLib.memcpy(bufPtr, null, 0); + +memcpyLib.memcpy(bufPtr, "4321", 5); +assert.equal("4321", bufPtr.getCString()); + +/////////////////////// + +util.log("Heap increased by " + ((process.memoryUsage()["rss"] - rss) / 1024) + " KB"); diff --git a/node_modules/node-ffi/test.old/test.old.js b/node_modules/node-ffi/test.old/test.old.js new file mode 100644 index 000000000..00fcd3be0 --- /dev/null +++ b/node_modules/node-ffi/test.old/test.old.js @@ -0,0 +1,711 @@ +var util = require("util"); + +var assert = require("assert"), + FFI = require("../lib/ffi"), + rss = process.memoryUsage()["rss"]; + +var Pointer = FFI.Pointer; + +///////// + +var ptr = new Pointer(1024); + +assert.ok(ptr.address > 0); + +var ptr2 = ptr.seek(32); +assert.equal(ptr.address + 32, ptr2.address); + +ptr.attach(ptr2); +assert.equal(ptr.address, ptr2.__attached[0].address); + +//////////////// + +assert.throws(function() { ptr.putInt8(Math.pow(2, 7) + 1); }); +assert.throws(function() { ptr.putInt8(0 - Math.pow(2, 7) - 1); }); + +ptr.putInt8(0 - Math.pow(2, 7)); +assert.equal(0 - Math.pow(2, 7), ptr.getInt8()); + +assert.throws(function() { ptr.putUInt8(-1); }); +assert.throws(function() { ptr.putUInt8(Math.pow(2, 8)); }); + +ptr.putUInt8(Math.pow(2, 8) - 1); +assert.equal(Math.pow(2, 8) - 1, ptr.getUInt8()); + +//////////////// + +assert.throws(function() { ptr.putInt16(Math.pow(2, 15) + 1); }); +assert.throws(function() { ptr.putInt16(0 - Math.pow(2, 15) - 1); }); + +ptr.putInt16(0 - Math.pow(2, 15)); +assert.equal(0 - Math.pow(2, 15), ptr.getInt16()); + +assert.throws(function() { ptr.putUInt16(-1); }); +assert.throws(function() { ptr.putUInt16(Math.pow(2, 16)); }); + +ptr.putUInt16(Math.pow(2, 16) - 1); +assert.equal(Math.pow(2, 16) - 1, ptr.getUInt16()); + +//////////////// + +assert.throws(function() { ptr.putInt32(Math.pow(2, 31) + 1); }); +assert.throws(function() { ptr.putInt32(0 - Math.pow(2, 31) - 1); }); + +ptr.putInt32(0 - Math.pow(2, 31)); +assert.equal(0 - Math.pow(2, 31), ptr.getInt32()); + +assert.throws(function() { ptr.putUInt32(-1); }); +assert.throws(function() { ptr.putUInt32(Math.pow(2, 32)); }); + +ptr.putUInt32(Math.pow(2, 32) - 1); +assert.equal(Math.pow(2, 32) - 1, ptr.getUInt32()); + +//////////////// + +// This checks for int64 behavior remaining the same (V8 using floats for these numbers) +assert.notEqual(Math.pow(2, 64).toString(), "18446744073709551616"); +assert.notEqual(Math.pow(2, 63).toString(), "9223372036854775808"); + +//assert.throws(function() { ptr.putInt64(-9223372036854785808); }); +//assert.throws(function() { ptr.putInt64(9223372036854785808); }); + +ptr.putInt64(0 - Math.pow(2, 63)); +assert.equal(0 - Math.pow(2, 63), ptr.getInt64()); + +assert.throws(function() { ptr.putUInt64(-1); }); +assert.throws(function() { ptr.putUInt64(18446744073709551616); }); + +ptr.putUInt64(Math.pow(2, 63) - 10000); +assert.equal(Math.pow(2, 63) - 10000, ptr.getUInt64()); + +// check for string support +assert.throws(function() { ptr.putInt64("9223372036854775808"); }); +assert.throws(function() { ptr.putInt64("-9223372036854775809"); }); + +// allows INT64_MAX value +ptr.putInt64("9223372036854775807"); +assert.equal("9223372036854775807", ptr.getInt64()); + +// allows INT64_MIN value +ptr.putInt64("-9223372036854775808"); +assert.equal("-9223372036854775808", ptr.getInt64()); + +// Uint should throw error on value > UINT64_MAX or negative number +assert.throws(function() { ptr.putUInt64("18446744073709551616"); }); +assert.throws(function() { ptr.putUInt64("-1"); }); + +// allows UINT64_MAX value +ptr.putUInt64("18446744073709551615"); +assert.equal("18446744073709551615", ptr.getUInt64()); + +//////////////// + +// TODO: values outside of "float" precision create unpredictable results +ptr.putFloat(1.5); +assert.equal(1.5, ptr.getFloat()); + +ptr.putDouble(1000.005); +assert.equal(1000.005, ptr.getDouble()); + +var nptr = new Pointer(32); +nptr.putDouble(1234.5678); +ptr.putPointer(nptr); + +assert.equal(nptr.address, ptr.getPointer().address); +assert.equal(1234.5678, ptr.getPointer().getDouble()); +assert.equal(32, nptr.allocated); + +ptr.putCString("Hello World!"); +assert.equal("Hello World!", ptr.getCString()); + +//////////////////////// + + +////////////////////////// + +// Exercise the "non-specific" getters/putters + +ptr.putByte(6); +assert.equal(6, ptr.getByte()); + +/////////////////////////////// + +ptr.putChar(-6); +assert.equal(-6, ptr.getChar()); + +ptr.putChar(6); +assert.equal(6, ptr.getChar()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUChar(-8); +}); +ptr.putChar(-8); +assert.ok(ptr.getUChar() != 8 && ptr.getUChar() > 0); + +ptr.putUChar(8); +assert.equal(8, ptr.getUChar()); + +/////////////////////////////// + +ptr.putShort(9); +assert.equal(9, ptr.getShort()); + +ptr.putShort(-9); +assert.equal(-9, ptr.getShort()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUShort(-8); +}); +ptr.putShort(-8); +assert.ok(ptr.getUShort() != 8 && ptr.getUShort() > 0); + +ptr.putUShort(11); +assert.equal(11, ptr.getUShort()); + +/////////////////////////////// + +ptr.putInt(12); +assert.equal(12, ptr.getInt()); + +ptr.putInt(-12); +assert.equal(-12, ptr.getInt()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putUInt(-8); +}); +ptr.putInt(-8); +assert.ok(ptr.getUInt() != 8 && ptr.getUInt() > 0); + +ptr.putUInt(13); +assert.equal(13, ptr.getUInt()); + +/////////////////////////////// + +ptr.putLong(14); +assert.equal(14, ptr.getLong()); + +ptr.putLong(-14); +assert.equal(-14, ptr.getLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putULong(-8); +}); +ptr.putLong(-8); +assert.ok(ptr.getULong() != 8 && ptr.getULong() > 0); + +ptr.putULong(15); +assert.equal(15, ptr.getULong()); + +/////////////////////////////// + +ptr.putLongLong(16); +assert.equal(16, ptr.getLongLong()); + +ptr.putLongLong(-16); +assert.equal(-16, ptr.getLongLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putULongLong(-8); +}); +ptr.putLongLong(-8); +assert.ok(ptr.getULongLong() != 8 && ptr.getULongLong() > 0); + +ptr.putULongLong(17); +assert.equal(17, ptr.getULongLong()); + +/////////////////////////////// + +assert.throws(function() { + ptr.putSizeT(-1); +}); + +ptr.putSizeT(18); +assert.equal(18, ptr.getSizeT()); + +////////////////////// + +var nullptr = new Pointer(0); +assert.ok(nullptr.isNull()); + +// test put + advance calls +var advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putByte(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt8(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt16(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt16(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt32(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt32(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putInt64(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putUInt64(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putFloat(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putDouble(1, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putPointer(ptr, true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.putCString("hi", true); +assert.ok(advptr.address > ptr.address); + +// test get + advance calls + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getByte(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt8(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt16(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt16(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt32(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt32(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getInt64(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getUInt64(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getFloat(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getDouble(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getPointer(true); +assert.ok(advptr.address > ptr.address); + +advptr = ptr.seek(0); +assert.ok(advptr.address == ptr.address); +advptr.getCString(true); +assert.ok(advptr.address > ptr.address); + +////////////////////// +// https://github.com/rbranson/node-ffi/issues/27 + +var basePtr = new FFI.Pointer(128); +var ptr = basePtr.seek(0); + +ptr.putCString('one', true); +ptr.putCString('two', true); +ptr.putCString('three', true); +assert.equal(basePtr.getCString(true), 'one'); +assert.equal(basePtr.getCString(true), 'two'); +assert.equal(basePtr.getCString(true), 'three'); + +////////////////////// + +var p = new FFI.Pointer(128) + , orig = p.seek(0) + +var put1 = { test: { equality: true } } + , put2 = { test2: 'does this work?' } + +p.putObject(put1, true) +p.putObject(put2, true) + +var get1 = orig.getObject(true) + , get2 = orig.getObject(true) + +assert.ok(put1 === get1) +assert.ok(put2 === get2) +assert.deepEqual(put1, get1) +assert.deepEqual(put2, get2) + +////////////////////// + +assert.ok(FFI.Bindings.StaticFunctions instanceof Object); +assert.ok(FFI.Bindings.StaticFunctions.dlopen instanceof Pointer); +assert.ok(FFI.Bindings.StaticFunctions.dlclose instanceof Pointer); +assert.ok(FFI.Bindings.StaticFunctions.dlsym instanceof Pointer); +assert.ok(FFI.Bindings.StaticFunctions.dlerror instanceof Pointer); + +////////////////////// + +var SimpleStruct = new FFI.Struct([ + ["byte", "first"], + ["byte", "last"] +]); + +var stInstance = new SimpleStruct(); + +stInstance.first = 50; +stInstance.last = 100; + +assert.equal(50, stInstance.first); +assert.equal(100, stInstance.last); + +var MegaStruct = new FFI.Struct([ + ["byte", "byteVal"], + ["int8", "int8Val"], + ["int16", "int16Val"], + ["uint16", "uint16Val"], + ["int32", "int32Val"], + ["uint32", "uint32Val"], + ["float", "floatVal"], + ["double", "doubleVal"], + ["pointer", "pointerVal"] +]); + +var msTestPtr = new Pointer(4); + +var msInstance = new MegaStruct({ + "byteVal": 100, + "int8Val": -100, + "int16Val": -1000, + "uint16Val": 1000, + "int32Val": -10000, + "uint32Val": 10000, + "floatVal": 1.25, + "doubleVal": 1000.0005, + "pointerVal": msTestPtr +}); + +assert.equal(100, msInstance.byteVal); +assert.equal(-100, msInstance.int8Val); +assert.equal(1000, msInstance.uint16Val); +assert.equal(-1000, msInstance.int16Val); +assert.equal(10000, msInstance.uint32Val); +assert.equal(-10000, msInstance.int32Val); +assert.equal(1.25, msInstance.floatVal); +assert.equal(1000.0005, msInstance.doubleVal); +assert.equal(msTestPtr.address, msInstance.pointerVal.address); + +assert.throws(function() { + FFI.Struct([ + ["byte", "a"], + ["byte", "a"] + ]); +}, Error, "Error when constructing Struct: a field specified twice!"); + +////////////////////// +assert.ok(FFI.Bindings.FFI_TYPES["void"] instanceof FFI.Pointer); +assert.ok(FFI.Bindings.FFI_TYPES["int8"] instanceof FFI.Pointer); + +////////////////////// + +var tcif = new FFI.CIF("int32", ["int32"]); +assert.ok(tcif.getArgTypesPointer() instanceof FFI.Pointer); +var cifat = tcif.getArgTypesPointer().seek(0); +assert.equal(FFI.Bindings.FFI_TYPES["int32"].address, cifat.getPointer(true).address); + +//////////////////////// + +var ff = new FFI.ForeignFunction(FFI.Bindings.StaticFunctions.abs, "int32", [ "int32" ]); +var absFunc = ff.getFunction(); +assert.ok(absFunc instanceof Function); +assert.equal(1234, absFunc(-1234)); + +////////////////////// + +var builtValuePtr = FFI.Pointer.alloc("int32", 1234); +assert.equal(1234, builtValuePtr.getInt32()); +assert.equal(1234, FFI.derefValuePtr("int32", builtValuePtr)); + +var int32Derefer = FFI.derefValuePtrFunc("int32"); +assert.equal(1234, int32Derefer(builtValuePtr)); + +var builtStringPtr = FFI.Pointer.alloc("string", "Hello World!"); +assert.equal("Hello World!", FFI.derefValuePtr("string", builtStringPtr)); + +var stringDerefer = FFI.derefValuePtrFunc("string"); +assert.equal("Hello World!", stringDerefer(builtStringPtr)); + +////////////////////// + +var abs = FFI.ForeignFunction.build(FFI.Bindings.StaticFunctions.abs, "int32", [ "int32" ]); +assert.equal(1234, abs(-1234)); + +var atoi = FFI.ForeignFunction.build(FFI.Bindings.StaticFunctions.atoi, "int32", [ "string" ]); +assert.equal(1234, atoi("1234")); + +////////////////////// + +var libm = new FFI.DynamicLibrary("libm" + FFI.PLATFORM_LIBRARY_EXTENSIONS[process.platform], FFI.DynamicLibrary.FLAGS.RTLD_NOW); +assert.ok(libm instanceof FFI.DynamicLibrary); + +var ceilPtr = libm.get("ceil"); +assert.ok(ceilPtr instanceof FFI.Pointer); +assert.ok(!ceilPtr.isNull()); + +var ceil = FFI.ForeignFunction.build(ceilPtr, "double", [ "double" ]); +assert.ok(ceil instanceof Function); + +assert.equal(2, ceil(1.5)); + +libm.close(); + +/////////////////////// + +var libm = new FFI.Library("libm", { "ceil": [ "double", [ "double" ] ] }); +assert.ok(libm instanceof FFI.Library); +assert.ok(libm.ceil instanceof Function); +assert.equal(2, libm.ceil(1.5)); + +/////////////////////// + +var thisfuncs = new FFI.Library(null, { + "fopen": [ "pointer", [ "string", "string" ] ], + "fclose": [ "int32", [ "pointer" ] ] +}); + +assert.ok(thisfuncs instanceof FFI.Library); + +var fd = thisfuncs.fopen("/etc/passwd", "r"); +assert.ok(!fd.isNull()); + +assert.equal(0, thisfuncs.fclose(fd)); + + +/////////////////////// + +var closureCalled = 0; +var cifPtr = new FFI.CIF("int32", [ "int32" ]); +var clz = new FFI.CallbackInfo(cifPtr.getPointer(), function(result, args) { + closureCalled++; +}); + +var callMyTestClosure = FFI.ForeignFunction.build(clz.pointer, "int32", [ "int32" ]); +callMyTestClosure(1); +assert.equal(1, closureCalled); +callMyTestClosure(1); +assert.equal(2, closureCalled); + +/////////////////////// + +var callback = new FFI.Callback(["int32", ["int32"]], function(inValue) { + return Math.abs(inValue); +}); + +var callMyTestCallback = FFI.ForeignFunction.build(callback.getPointer(), "int32", ["int32"]); + +// force a garbage collection for --gc_interval=10 +var gcTestObj = {}; +for (var i = 0; i < 25; i++) { + gcTestObj[i] = {i: gcTestObj, s: ""}; +} + +assert.equal(1234, callMyTestCallback(-1234)); + +/////////////////////// + +var asyncAbsCallExecuted = false; +var asyncAbs = FFI.ForeignFunction.build(FFI.Bindings.StaticFunctions.abs, "int32", [ "int32" ], true); +asyncAbs(-1234).on("success", function (res) { + asyncAbsCallExecuted = true; + assert.equal(1234, res); +}); + +var libmCeilAsyncCallExecuted = false; +var libm = new FFI.Library("libm", { "ceil": [ "double", [ "double" ], {"async": true } ] }); +assert.ok(libm instanceof FFI.Library); +assert.ok(libm.ceil instanceof Function); + +libm.ceil(1.5).on("success", function(res) { + libmCeilAsyncCallExecuted = true; + assert.equal(2, res); +}); + +/////////////////////// + +// allow the event loop to complete +setTimeout(function() { + assert.ok(asyncAbsCallExecuted); + assert.ok(libmCeilAsyncCallExecuted); + util.log("Tests pass!"); +}, 250); + +/////////////////////// + +// test gettimeofday() with FFI.Struct +(function() { + var timeval = FFI.Struct([ + ["long", "tv_sec"], + ["long", "tv_usec"] + ]); + + var lib = new FFI.Library(null, { "gettimeofday": [ "int", [ "pointer", "pointer"] ] }); + + var tv = new timeval(); + lib.gettimeofday(tv.ref(), null); + + assert.equal(tv.tv_sec, Math.floor(Date.now() / 1000)); +})(); + +// Test FFI.Struct nesting +(function() { + var ChildStructType = FFI.Struct([ + ["int", "a"], + ["int", "b"] + ]); + + var ParentStructType = FFI.Struct([ + [ChildStructType, "childA"], + [ChildStructType, "childB"] + ]); + + var ps = new ParentStructType({ + "childA": { "a": 100, "b": 200 }, + "childB": { "a": 300, "b": 400 } + }); + + assert.equal(100, ps.childA.a); + assert.equal(200, ps.childA.b); + assert.equal(300, ps.childB.a); + assert.equal(400, ps.childB.b); +})(); + +/////////////////////// + +var strtoulLib = new FFI.Library(null, { "strtoul": [ "ulong", [ "string", "pointer", "int"] ] }); +strtoulLib.strtoul("1234567890123456789012345678901234567890", null, 0); +assert.equal(34, FFI.errno()); // errno == ERANGE because value was outside of strtoul's range. + +/////////////////////// + +// Test string argument resizing +var ZEROS_128 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +var ZEROS_2K = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +var strcpyLib = new FFI.Library(null, { "strcpy": [ "pointer", [ "pointer", "string" ] ] }); +var bufPtr = new Pointer(4096); + +strcpyLib.strcpy(bufPtr, ZEROS_128); +assert.equal(ZEROS_128, bufPtr.getCString()); + +strcpyLib.strcpy(bufPtr, ZEROS_2K); +assert.equal(ZEROS_2K, bufPtr.getCString()); + +// Test string argument NULL handling +var memcpyLib = new FFI.Library(null, { "memcpy": [ "int", [ "pointer", "string", "size_t" ] ] }); +var bufPtr = new Pointer(128); + +memcpyLib.memcpy(bufPtr, "1234", 5); +assert.equal("1234", bufPtr.getCString()); + +memcpyLib.memcpy(bufPtr, null, 0); + +memcpyLib.memcpy(bufPtr, "4321", 5); +assert.equal("4321", bufPtr.getCString()); + +/////////////////////// + +// Test Objective-C @try/@catch support when available (Darwin, GNUstep) +if (FFI.Bindings.HAS_OBJC) { + var objcLib = new FFI.Library('libobjc', { + 'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer' ] ] + , 'objc_getClass': [ 'pointer', [ 'string' ] ] + , 'sel_registerName': [ 'pointer', [ 'string' ] ] + }); + var pool = objcLib.objc_msgSend(objcLib.objc_getClass('NSAutoreleasePool'), objcLib.sel_registerName('new')) + var NSObject = objcLib.objc_getClass('NSObject'); + var badSel = objcLib.sel_registerName('badSelector'); + util.log("=== 'NSObject badSelector' Exception log on next line is OK ==="); + assert.throws(function () { + objcLib.objc_msgSend(NSObject, badSel); + }); +} + +/////////////////////// + +// Test Pointer#toBuffer() +var p = new FFI.Pointer(128) + , b = p.toBuffer() +assert.equal(p.allocated, b.length) + +// Test Buffer -> Pointer +b.write('hello\0'); +assert.equal(p.getCString(), 'hello') + +// Test Pointer -> Buffer +var hw = 'hello world'; +p.putCString(hw); +assert.equal(b.slice(0, hw.length).toString(), hw) + +// Test modifying a single byte in the Buffer +b[2] = 'L'.charCodeAt(0) +assert.equal(p.getCString(), 'heLlo world') + + +/////////////////////// + +util.log("Heap increased by " + ((process.memoryUsage()["rss"] - rss) / 1024) + " KB"); diff --git a/node_modules/node-ffi/test/buffer.js b/node_modules/node-ffi/test/buffer.js new file mode 100644 index 000000000..a7338f6bd --- /dev/null +++ b/node_modules/node-ffi/test/buffer.js @@ -0,0 +1,50 @@ + +var expect = require('expect.js') + , ffi = require('../') + , Pointer = ffi.Pointer + +describe('Pointer', function () { + + afterEach(gc) + + describe('toBuffer()', function () { + + it('should return a "Buffer" instance', function () { + var p = new Pointer(8) + , b = p.toBuffer() + expect(Buffer.isBuffer(b)).to.be(true) + }) + + it('`Buffer#length` should match `Pointer#allocated`', function () { + var p = new Pointer(128) + , b = p.toBuffer() + expect(b.length).to.equal(p.allocated) + }) + + it('should work when writing to Buffer, reading from Pointer', function () { + var p = new Pointer(6) + , b = p.toBuffer() + , msg = 'hello' + b.write(msg + '\0') + expect(p.getCString()).to.equal(msg) + }) + + it('should work when writing to Pointer, reading from Buffer', function () { + var p = new Pointer(12) + , b = p.toBuffer() + , msg = 'hello world' + p.putCString(msg) + expect(b.toString('ascii', 0, msg.length)).to.equal(msg) + }) + + it('should work when modifying a single byte in the Buffer', function () { + var p = new Pointer(6) + , b = p.toBuffer() + p.putCString('hello') + b[2] = 'L'.charCodeAt(0) + expect(p.getCString()).to.equal('heLlo') + }) + + }) + +}) diff --git a/node_modules/node-ffi/test/callback.js b/node_modules/node-ffi/test/callback.js new file mode 100644 index 000000000..bebe8dd59 --- /dev/null +++ b/node_modules/node-ffi/test/callback.js @@ -0,0 +1,28 @@ + +var expect = require('expect.js') + , ffi = require('../') + , Pointer = ffi.Pointer + , Callback = ffi.Callback + +describe('Callback', function () { + + afterEach(gc) + + it('should create a C function pointer for a JS function', function () { + var callback = new Callback(['int32', ['int32']], function (val) { + return Math.abs(val) + }) + var pointer = callback.getPointer() + expect(Pointer.isPointer(pointer)).to.be(true) + }) + + it('should be invokable', function () { + var callback = new Callback(['int32', ['int32']], function (val) { + return Math.abs(val) + }) + var pointer = callback.getPointer() + , func = ffi.ForeignFunction.build(pointer, 'int32', ['int32']) + expect(func(-1234)).to.equal(1234) + }) + +}) diff --git a/node_modules/node-ffi/test/errno.js b/node_modules/node-ffi/test/errno.js new file mode 100644 index 000000000..475097870 --- /dev/null +++ b/node_modules/node-ffi/test/errno.js @@ -0,0 +1,25 @@ + +var expect = require('expect.js') + , ffi = require('../') + , errno = ffi.errno + +describe('errno()', function () { + + afterEach(gc) + + it('should be a function', function () { + expect(errno).to.be.a('function') + }) + + it('should set the errno with out-of-range "strtoul" value', function () { + var lib = process.platform == 'win32' ? 'msvcrt' : 'libc' + var strtoul = new ffi.Library(lib, { + 'strtoul': [ 'ulong', [ 'string', 'pointer', 'int' ] ] + }).strtoul + var before = errno() + strtoul('1234567890123456789012345678901234567890', null, 0) + expect(errno()).to.not.equal(before) + expect(errno()).to.equal(34) // errno == ERANGE + }) + +}) diff --git a/node_modules/node-ffi/test/foreign_function.js b/node_modules/node-ffi/test/foreign_function.js new file mode 100644 index 000000000..7f43119a5 --- /dev/null +++ b/node_modules/node-ffi/test/foreign_function.js @@ -0,0 +1,39 @@ + +var expect = require('expect.js') + , ffi = require('../') + +describe('ForeignFunction', function () { + + afterEach(gc) + + it('should call the static "abs" bindings', function () { + var abs = ffi.ForeignFunction.build( + ffi.Bindings.StaticFunctions.abs + , 'int32', [ 'int32' ]) + expect(abs).to.be.a('function') + expect(abs(-1234)).to.equal(1234) + }) + + it('should call the static "atoi" bindings', function () { + var atoi = ffi.ForeignFunction.build( + ffi.Bindings.StaticFunctions.atoi + , 'int32', [ 'string' ]) + expect(atoi).to.be.a('function') + expect(atoi('1234')).to.equal(1234) + }) + + describe('async', function () { + + it('should call the static "abs" bindings asynchronously', function (done) { + var abs = ffi.ForeignFunction.build( + ffi.Bindings.StaticFunctions.abs + , 'int32', [ 'int32' ], true) + expect(abs).to.be.a('function') + abs(-1234).on('success', function (res) { + expect(res).to.equal(1234) + done() + }) + }) + + }) +}) diff --git a/node_modules/node-ffi/test/library.js b/node_modules/node-ffi/test/library.js new file mode 100644 index 000000000..c3b11a70b --- /dev/null +++ b/node_modules/node-ffi/test/library.js @@ -0,0 +1,114 @@ + +var expect = require('expect.js') + , ffi = require('../') + , Library = ffi.Library + +describe('Library', function () { + + afterEach(gc) + + it('should be a function', function () { + expect(Library).to.be.a('function') + }) + + it('should work with the `new` operator', function () { + var l = new Library() + expect(l).to.be.an('object') + }) + + it('should accept `null` as a first argument', function () { + var thisFuncs = new Library(null, { + 'printf': [ 'void', [ 'string' ] ] + }) + var test = thisFuncs.printf instanceof Function + expect(test).to.be(true) + }) + + it('should accept a lib name as a first argument', function () { + var lib = process.platform == 'win32' ? 'msvcrt' : 'libm' + var libm = new Library(lib, { + 'ceil': [ 'double', [ 'double' ] ] + }) + var test = libm.ceil instanceof Function + expect(test).to.be(true) + expect(libm.ceil(1.1)).to.equal(2) + }) + + it('should throw when an invalid function name is used', function () { + expect(function () { + new Library(null, { + 'doesnotexist__': [ 'void', [] ] + }) + }).to.throwException() + }) + + it('should work with "strcpy" and a 128 length string', function () { + var ZEROS_128 = Array(128 + 1).join('0') + var buf = new ffi.Pointer(256) + var strcpy = new Library(null, { + 'strcpy': [ 'pointer', [ 'pointer', 'string' ] ] + }).strcpy + strcpy(buf, ZEROS_128) + expect(buf.getCString()).to.equal(ZEROS_128) + }) + + it('should work with "strcpy" and a 2k length string', function () { + var ZEROS_2K = Array(2e3 + 1).join('0') + var buf = new ffi.Pointer(4096) + var strcpy = new Library(null, { + 'strcpy': [ 'pointer', [ 'pointer', 'string' ] ] + }).strcpy + strcpy(buf, ZEROS_2K) + expect(buf.getCString()).to.equal(ZEROS_2K) + }) + + if (process.platform == 'win32') { + + it('should work with "GetTimeOfDay" and a "FILETIME" Struct pointer', + function () { + var FILETIME = new ffi.Struct([ + ['uint32', 'dwLowDateTime'] + , ['uint32', 'dwHighDateTime'] + ]) + var l = new Library('kernel32', { + 'GetSystemTimeAsFileTime': [ 'void', [ 'pointer' ]] + }) + var ft = new FILETIME() + l.GetSystemTimeAsFileTime(ft.ref()) + // TODO: Add an expect() clause here... + }) + + } else { + + it('should work with "gettimeofday" and a "timeval" Struct pointer', + function () { + var timeval = new ffi.Struct([ + ['long','tv_sec'] + , ['long','tv_usec'] + ]) + var l = new Library(null, { + 'gettimeofday': ['int', ['pointer', 'pointer']] + }) + var tv = new timeval() + l.gettimeofday(tv.ref(), null) + expect(tv.tv_sec == Math.floor(Date.now() / 1000)).to.be(true) + }) + + } + + describe('async', function () { + + it('should call a function asynchronously', function (done) { + var lib = process.platform == 'win32' ? 'msvcrt' : 'libm' + var libm = new Library(lib, { + 'ceil': [ 'double', [ 'double' ], { async: true } ] + }) + libm.ceil(1.1).on('success', function (res) { + expect(res).to.equal(2) + done() + }) + }) + + }) + +}) diff --git a/node_modules/node-ffi/test/objc.js b/node_modules/node-ffi/test/objc.js new file mode 100644 index 000000000..1deb4dde5 --- /dev/null +++ b/node_modules/node-ffi/test/objc.js @@ -0,0 +1,42 @@ + +var expect = require('expect.js') + , ffi = require('../') + +if (ffi.Bindings.HAS_OBJC) { + + describe('@try / @catch', function () { + + afterEach(gc) + + var objcLib = new ffi.Library('libobjc', { + 'objc_msgSend': [ 'pointer', [ 'pointer', 'pointer' ] ] + , 'objc_getClass': [ 'pointer', [ 'string' ] ] + , 'sel_registerName': [ 'pointer', [ 'string' ] ] + }) + + var NSAutoreleasePool = objcLib.objc_getClass('NSAutoreleasePool') + , sel_new = objcLib.sel_registerName('new') + , pool = objcLib.objc_msgSend(NSAutoreleasePool, sel_new) + + it('should proxy @try/@catch to JavaScript via try/catch/throw', function () { + var sel_retain = objcLib.sel_registerName('retain') + expect(function () { + objcLib.objc_msgSend(pool, sel_retain) + }).to.throwException() + }) + + it('should throw a Pointer instance when an exception happens', function () { + var sel_retain = objcLib.sel_registerName('retain') + try { + objcLib.objc_msgSend(pool, sel_retain) + expect(false).to.be(true) + } catch (e) { + expect(ffi.Pointer.isPointer(e)).to.be(true) + expect(e.isNull()).to.be(false) + expect(e.address).to.be.greaterThan(0) + } + }) + + }) + +} diff --git a/node_modules/node-ffi/test/pointer.js b/node_modules/node-ffi/test/pointer.js new file mode 100644 index 000000000..d66b289c2 --- /dev/null +++ b/node_modules/node-ffi/test/pointer.js @@ -0,0 +1,340 @@ + +var expect = require('expect.js') + , ffi = require('../') + , Pointer = ffi.Pointer + +describe('Pointer', function () { + + afterEach(gc) + + it('should be a function', function () { + expect(Pointer).to.be.a('function') + }) + + it('shoulhd have "name" set to "Pointer"', function () { + expect(Pointer.name).to.equal('Pointer') + }) + + it('should work with the `new` operator', function () { + var p = new Pointer(8) + , test = p instanceof Pointer + expect(test).to.be(true) + }) + + it('should have its internal __class__ set to "Pointer"', function () { + var p = new Pointer(1) + expect(Object.prototype.toString.call(p)).to.equal('[object Pointer]') + }) + + it('should detect the `null` pointer', function () { + var p = new Pointer(0) + expect(p.isNull()).to.be(true) + }) + + it('should not free the `null` pointer', function () { + var p = new Pointer(0) + expect(p.free).to.be(false) + }) + + it('should have a valid "allocated" property when malloc()\'d', function () { + var p = new Pointer(1024) + expect(p.allocated).to.equal(1024) + }) + + it('should have a valid "address"', function () { + var p = new Pointer(8) + expect(p.address).to.be.greaterThan(0) + }) + + describe('seek()', function () { + + it('should have function "seek()"', function () { + expect(Pointer.prototype).to.have.property('seek') + expect(Pointer.prototype.seek).to.be.a('function') + }) + + it('should return a new Pointer instance', function () { + var p = new Pointer(8) + , p2 = p.seek(0) + , test = p2 instanceof Pointer + expect(test).to.be(true) + expect(p).to.not.equal(p2) + }) + + it('should not attempt to `free` the seeked Pointer', function () { + var p = new Pointer(8) + , p2 = p.seek(1) + expect(p.free).to.be(true) + expect(p2.free).to.be(false) + }) + + it('should return a Pointer with its "address" offset by the specified amount' + , function () { + var p = new Pointer(8) + , p2 = p.seek(32) + expect(p2.address).to.equal(p.address + 32) + }) + + }) + + describe('int8', function () { + + it('should throw on out of bounds values (minimum)', function () { + var p = new Pointer(8) + , val = 0 - Math.pow(2, 7) - 1 + expect(p.putInt8.bind(p, val)).to.throwException() + }) + + it('should throw on out of bounds values (maximum)', function () { + var p = new Pointer(8) + , val = Math.pow(2, 7) + 1 + expect(p.putInt8.bind(p, val)).to.throwException() + }) + + it('should write an "int8" value properly', function () { + var p = new Pointer(8) + , val = 0 - Math.pow(2, 7) + p.putInt8(val) + expect(p.getInt8()).to.equal(val) + }) + + }) + + describe('uint8', function () { + + it('should throw on out of bounds values (minimum)', function () { + var p = new Pointer(8) + , val = -1 + expect(p.putUInt8.bind(p, val)).to.throwException() + }) + + it('should throw on out of bounds values (maximum)', function () { + var p = new Pointer(8) + , val = Math.pow(2, 8) + expect(p.putUInt8.bind(p, val)).to.throwException() + }) + + it('should write a "uint8" value properly', function () { + var p = new Pointer(8) + , val = Math.pow(2, 8) - 1 + p.putUInt8(val) + expect(p.getUInt8()).to.equal(val) + }) + + }) + + describe('int16', function () { + + it('should throw on out of bounds values (minimum)', function () { + var p = new Pointer(8) + , val = 0 - Math.pow(2, 15) - 1 + expect(p.putInt16.bind(p, val)).to.throwException() + }) + + it('should throw on out of bounds values (maximum)', function () { + var p = new Pointer(8) + , val = Math.pow(2, 15) + 1 + expect(p.putInt16.bind(p, val)).to.throwException() + }) + + it('should write an "int16" value properly', function () { + var p = new Pointer(8) + , val = 0 - Math.pow(2, 15) + p.putInt16(val) + expect(p.getInt16()).to.equal(val) + }) + + }) + + describe('uint16', function () { + + it('should throw on out of bounds values (minimum)', function () { + var p = new Pointer(8) + , val = -1 + expect(p.putUInt8.bind(p, val)).to.throwException() + }) + + it('should throw on out of bounds values (maximum)', function () { + var p = new Pointer(8) + , val = Math.pow(2, 16) + expect(p.putUInt16.bind(p, val)).to.throwException() + }) + + it('should write a "uint16" value properly', function () { + var p = new Pointer(8) + , val = Math.pow(2, 16) - 1 + p.putUInt16(val) + expect(p.getUInt16()).to.equal(val) + }) + + }) + + describe('int64', function () { + + it('should write "int64" values properly', function () { + var p = new Pointer(8) + , val = 0 - Math.pow(2, 63) + p.putInt64(val) + expect(p.getInt64()).to.eql(val) + }) + + it('should accept String values', function () { + var p = new Pointer(8) + , val = '1' + p.putInt64(val) + expect(p.getInt64()).to.equal(val) + }) + + it('should return String values', function () { + var p = new Pointer(8) + , val = '1' + p.putInt64(val) + expect(p.getInt64()).to.be.a('string') + }) + + it('should allow INT64_MAX value', function () { + var p = new Pointer(8) + , val = '9223372036854775807' + p.putInt64(val) + expect(p.getInt64()).to.equal(val) + }) + + it('should allow INT64_MIX value', function () { + var p = new Pointer(8) + , val = '-9223372036854775808' + p.putInt64(val) + expect(p.getInt64()).to.equal(val) + }) + + }) + + describe('uint64', function () { + + it('should throw on out of bounds values (minimum)', function () { + var p = new Pointer(8) + , val = -1 + expect(p.putUInt64.bind(p, val)).to.throwException() + }) + + it('should throw on out of bounds values (maximum)', function () { + var p = new Pointer(8) + , val = 18446744073709551616 + expect(p.putUInt64.bind(p, val)).to.throwException() + }) + + it('should allow UINT64_MAX value', function () { + var p = new Pointer(8) + , val = '18446744073709551615' + p.putUInt64(val) + expect(p.getUInt64()).to.equal(val) + }) + + }) + + describe('float', function () { + + it('should write a "float" value properly', function () { + var p = new Pointer(16) + , val = 1.5 + p.putFloat(val) + expect(p.getFloat()).to.equal(val) + }) + + }) + + describe('double', function () { + + it('should write a "double" value properly', function () { + var p = new Pointer(16) + , val = 1000.005 + p.putDouble(val) + expect(p.getDouble()).to.equal(val) + }) + + }) + + describe('pointer', function () { + + it('should write another Pointer instance properly', function () { + var p = new Pointer(16) + , p2 = new Pointer(32) + , val = 1234.5678 + p2.putDouble(val) + p.putPointer(p2) + expect(p.getPointer().address).to.equal(p2.address) + expect(p.getPointer().getDouble()).to.equal(val) + }) + + }) + + describe('string', function () { + + it('should write a C string (char array) properly', function () { + var p = new Pointer(32) + , msg = 'Hello World!' + p.putCString(msg) + expect(p.getCString()).to.equal(msg) + }) + + // https://github.com/rbranson/node-ffi/issues/27 + it('should write multiple strings properly', function () { + var p = new Pointer(128) + , base = p.seek(0) + p.putCString('one', true) + p.putCString('two', true) + p.putCString('three', true) + expect(base.getCString(true)).to.equal('one') + expect(base.getCString(true)).to.equal('two') + expect(base.getCString(true)).to.equal('three') + }) + + }) + + describe('Object', function () { + + it('should write a JavaScript Object reference properly', function () { + var p = new Pointer(32) + , o = { foo: 'bar' } + p.putObject(o) + expect(p.getObject()).to.eql(o) + }) + + it('should write multiple JavaScript Object references properly', function () { + var p = new Pointer(32) + , base = p.seek(0) + , o = { foo: 'bar' } + , o2 = { test: { equality: true } } + p.putObject(o, true) + p.putObject(o2, true) + expect(base.getObject(true)).to.eql(o) + expect(base.getObject(true)).to.eql(o2) + }) + + }) + + describe('byte', function () { + + it('should write a "byte" properly', function () { + var p = new Pointer(8) + , val = 6 + p.putByte(val) + expect(p.getByte()).to.equal(val) + }) + + }) + + describe('char', function () { + + it('should write a "char" properly', function () { + var p = new Pointer(8) + , val = 6 + p.putChar(val) + expect(p.getChar()).to.equal(val) + val = -6 + p.putChar(val) + expect(p.getChar()).to.equal(val) + }) + + }) + +}) diff --git a/node_modules/node-ffi/test/struct.js b/node_modules/node-ffi/test/struct.js new file mode 100644 index 000000000..b84cbcd0d --- /dev/null +++ b/node_modules/node-ffi/test/struct.js @@ -0,0 +1,212 @@ + +var expect = require('expect.js') + , ffi = require('../') + , Struct = ffi.Struct + +describe('Struct', function () { + + afterEach(gc) + + it('should be a function', function () { + expect(Struct).to.be.a('function') + }) + + it('should return a struct constuctor function', function () { + var S = Struct() + expect(S).to.be.a('function') + }) + + it('should throw when the same field name is speicified more than once', function () { + expect(Struct.bind(null, [ + [ 'byte', 'a' ] + , [ 'byte', 'a' ] + ])).to.throwException() + }) + + it('should work in a simple case', function () { + var SimpleStruct = new Struct([ + ['byte', 'first'] + , ['byte', 'last'] + ]) + var ss = new SimpleStruct({ first: 50, last: 100 }) + expect(ss.first).to.be.equal(50) + expect(ss.last).to.be.equal(100) + }) + + it('should work in a more complex case', function () { + var MegaStruct = new Struct([ + ['byte', 'byteVal'] + , ['int8', 'int8Val'] + , ['int16', 'int16Val'] + , ['uint16', 'uint16Val'] + , ['int32', 'int32Val'] + , ['uint32', 'uint32Val'] + , ['float', 'floatVal'] + , ['double', 'doubleVal'] + , ['pointer', 'pointerVal'] + ]) + var msTestPtr = new ffi.Pointer(1) + var ms = new MegaStruct({ + byteVal: 100 + , int8Val: -100 + , int16Val: -1000 + , uint16Val: 1000 + , int32Val: -10000 + , uint32Val: 10000 + , floatVal: 1.25 + , doubleVal: 1000.0005 + , pointerVal: msTestPtr + }) + expect(ms.byteVal).to.equal(100) + expect(ms.int8Val).to.equal(-100) + expect(ms.int16Val).to.equal(-1000) + expect(ms.uint16Val).to.equal(1000) + expect(ms.int32Val).to.equal(-10000) + expect(ms.uint32Val).to.equal(10000) + expect(ms.floatVal).to.equal(1.25) + expect(ms.doubleVal).to.equal(1000.0005) + expect(ms.pointerVal.address).to.equal(msTestPtr.address) + }) + + it('should allow Struct nesting', function () { + + var ChildStruct = new Struct([ + ['int', 'a'] + , ['int', 'b'] + ]) + var ParentStruct = new Struct([ + [ChildStruct, 'childA'] + , [ChildStruct, 'childB'] + ]) + + var ps = new ParentStruct({ + childA: { a: 100, b: 200 } + , childB: { a: 300, b: 400 } + }) + + expect(ps.childA.a).to.equal(100) + expect(ps.childA.b).to.equal(200) + expect(ps.childB.a).to.equal(300) + expect(ps.childB.b).to.equal(400) + }) + + describe('offsets and sizeofs', function () { + + function inspect (struct, expectedSize, expectedOffsets) { + var info = struct.__structInfo__ + , props = info.struct + , types = [] + + Object.keys(props).forEach(function (prop) { + var type = props[prop].type + if (ffi.isStructType(type)) { + // Output more info about the struct + type = 'Struct' + } + types.push(type) + }) + + it('Struct {'+types.join(', ')+'}, expectedSize='+expectedSize+', expectedOffsets=['+expectedOffsets.join(',')+']', function () { + + expect(ffi.sizeOf(struct)).to.equal(expectedSize) + + Object.keys(props).forEach(function (prop, i) { + expect(props[prop].offset).to.equal(expectedOffsets[i]) + }) + + }) + + } + + // TODO: build struct examples statically as part of the build and expose + // via the bindings interface. + + var test1 = ffi.Struct([ + ['int32', 'a'] + , ['int32', 'b'] + , ['double', 'c'] + ]) + inspect(test1, 16, [0,4,8]) + + var test2 = ffi.Struct([ + ['int32', 'a'] + , ['double', 'b'] + , ['int32', 'c'] + ]) + inspect(test2, 24, [0,8,16]) + + var test3 = ffi.Struct([ + ['double', 'a'] + , ['int32', 'b'] + , ['int32', 'c'] + ]) + inspect(test3, 16, [0,8,12]) + + var test4 = ffi.Struct([ + ['double', 'a'] + , ['double', 'b'] + , ['int32', 'c'] + ]) + inspect(test4, 24, [0,8,16]) + + var test5 = ffi.Struct([ + ['int32', 'a'] + , ['double', 'b'] + , ['double', 'c'] + ]) + inspect(test5, 24, [0,8,16]) + + var test6 = ffi.Struct([ + ['char', 'a'] + , ['short','b'] + , ['int32','c'] + ]) + inspect(test6, 8, [0,2,4]) + + var test7 = ffi.Struct([ + ['int32','a'] + , ['short','b'] + , ['char', 'c'] + ]) + inspect(test7, 8, [0,4,6]) + + var test8 = ffi.Struct([ + ['int32','a'] + , ['short','b'] + , ['char', 'c'] + , ['char', 'd'] + ]) + inspect(test8, 8, [0,4,6,7]) + + var test9 = ffi.Struct([ + ['int32','a'] + , ['short','b'] + , ['char', 'c'] + , ['char', 'd'] + , ['char', 'e'] + ]) + inspect(test9, 12, [0,4,6,7,8]) + + var test10 = ffi.Struct([ + [test1, 'a'] + , ['char','b'] + ]) + inspect(test10, 24, [0,16]) + + var ffi_type = ffi.Struct([ + ['size_t','size'] + , ['ushort','alignment'] + , ['ushort','type'] + , ['pointer','elements'] + ]) + if (ffi.Bindings.POINTER_SIZE == 4) { + inspect(ffi_type, 12, [0,4,6,8]) + } else if (ffi.Bindings.POINTER_SIZE == 8) { + inspect(ffi_type, 24, [0,8,10,16]) + } else { + throw new Error("Bad platform pointer size: %d bytes", ffi.Bindings.POINTER_SIZE) + } + + }) + +}) diff --git a/package.json b/package.json new file mode 100644 index 000000000..dbd975337 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "gpii-windows", + "description": "Components of the GPII personalization infrastructure for use on Microsoft's \"Windows\" ™", + "version": "0.1", + "author": "GPII", + "bugs": "http://wiki.gpii.net/index.php/Main_Page", + "homepage": "http://gpii.net/", + "dependencies": {}, + "licenses": [ + { + "type": "BSD-3-Clause", + "url": "http://www.opensource.org/licenses/BSD-3-Clause" + } + ], + "keywords": ["gpii", "accessibility", "settings", "fluid", "IoC", "Inversion of Control", "configuration", "evented"], + "repository": "git://github.com/GPII/windows.git", + "main": "./gpii/index.js", + "engines": { "node" : ">=0.1.9" } +} \ No newline at end of file From 8910f516af07d9877fa0b651e39a4ceb96c74303 Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Tue, 3 Apr 2012 03:27:31 -0600 Subject: [PATCH 02/11] A few missed file permissions --- gpii/index.js | 0 gpii/node_modules/registrySettingsHandler/index.js | 0 gpii/node_modules/registrySettingsHandler/package.json | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 gpii/index.js mode change 100755 => 100644 gpii/node_modules/registrySettingsHandler/index.js mode change 100755 => 100644 gpii/node_modules/registrySettingsHandler/package.json diff --git a/gpii/index.js b/gpii/index.js old mode 100755 new mode 100644 diff --git a/gpii/node_modules/registrySettingsHandler/index.js b/gpii/node_modules/registrySettingsHandler/index.js old mode 100755 new mode 100644 diff --git a/gpii/node_modules/registrySettingsHandler/package.json b/gpii/node_modules/registrySettingsHandler/package.json old mode 100755 new mode 100644 From 924f168b74bb5b5a25576c32629391d16d191c6e Mon Sep 17 00:00:00 2001 From: Antranig Basman Date: Tue, 10 Apr 2012 15:41:37 -0600 Subject: [PATCH 03/11] Adjusted payload and implementation for consistency with other platforms. Adjusted permissions of node-ffi binaries so that they are executable again (!) --- gpii/node_modules/registrySettingsHandler/README.md | 2 +- .../src/RegistrySettingsHandler.js | 10 +++++----- .../registrySettingsHandler/test/testPayload.json | 6 ++---- .../node-ffi/compiled/darwin/x64/ffi_bindings.node | Bin .../node-ffi/compiled/linux/ia32/ffi_bindings.node | Bin .../node-ffi/compiled/linux/x64/ffi_bindings.node | Bin .../node-ffi/compiled/sunos/ia32/ffi_bindings.node | Bin .../node-ffi/compiled/win32/ia32/ffi_bindings.node | Bin 8 files changed, 8 insertions(+), 10 deletions(-) mode change 100644 => 100755 node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node mode change 100644 => 100755 node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node mode change 100644 => 100755 node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node mode change 100644 => 100755 node_modules/node-ffi/compiled/sunos/ia32/ffi_bindings.node mode change 100644 => 100755 node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node diff --git a/gpii/node_modules/registrySettingsHandler/README.md b/gpii/node_modules/registrySettingsHandler/README.md index 6287b32c6..a05344109 100644 --- a/gpii/node_modules/registrySettingsHandler/README.md +++ b/gpii/node_modules/registrySettingsHandler/README.md @@ -4,6 +4,6 @@ Run basic self-test with node node_modules\registrySettingsHandler -Run a settingsHandler payload with, for example, +Run a settingsHandler payload with, for example, from directory "gpii", node node_modules\registrySettingsHandler ../test/testPayload.json \ No newline at end of file diff --git a/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js b/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js index b4ac53728..50a316dc5 100644 --- a/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js +++ b/gpii/node_modules/registrySettingsHandler/src/RegistrySettingsHandler.js @@ -295,12 +295,12 @@ windows.deleteRegistryKey = function (baseKey, path) { }; windows.registrySettingsHandler = function (payload) { - var key = payload.options.hKey; + var baseKey = payload.options.hKey; var path = payload.options.path; - var results = fluid.transform(payload.settings, function(value) { - var oldValue = windows.readRegistryKey(key, path, value.path, value.dataType); - var writeStatus = windows.writeRegistryKey(key, path, value.path, value.value, value.dataType); - var newValue = windows.readRegistryKey(key, path, value.path, value.dataType); + var results = fluid.transform(payload.settings, function(value, key) { + var oldValue = windows.readRegistryKey(baseKey, path, key, value.dataType); + var writeStatus = windows.writeRegistryKey(baseKey, path, key, value.value, value.dataType); + var newValue = windows.readRegistryKey(baseKey, path, key, value.dataType); return { oldValue: oldValue.value, statusCode: writeStatus.statusCode, diff --git a/gpii/node_modules/registrySettingsHandler/test/testPayload.json b/gpii/node_modules/registrySettingsHandler/test/testPayload.json index 880bb8091..97e73cfac 100644 --- a/gpii/node_modules/registrySettingsHandler/test/testPayload.json +++ b/gpii/node_modules/registrySettingsHandler/test/testPayload.json @@ -4,13 +4,11 @@ "path": "Software\\GPIIMagnifierTest" }, "settings": { - "magnification": { - "path": "Magnification", + "Magnification": { "value": 100, "dataType": "REG_DWORD" }, - "magnificationString": { - "path": "MagnificationString", + "MagnificationString": { "value": "Very Big", "dataType": "REG_SZ" } diff --git a/node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node b/node_modules/node-ffi/compiled/darwin/x64/ffi_bindings.node old mode 100644 new mode 100755 diff --git a/node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node b/node_modules/node-ffi/compiled/linux/ia32/ffi_bindings.node old mode 100644 new mode 100755 diff --git a/node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node b/node_modules/node-ffi/compiled/linux/x64/ffi_bindings.node old mode 100644 new mode 100755 diff --git a/node_modules/node-ffi/compiled/sunos/ia32/ffi_bindings.node b/node_modules/node-ffi/compiled/sunos/ia32/ffi_bindings.node old mode 100644 new mode 100755 diff --git a/node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node b/node_modules/node-ffi/compiled/win32/ia32/ffi_bindings.node old mode 100644 new mode 100755 From 07273e143e0ff6b52428cfd3d70fe3a0f6fa0b8c Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Fri, 27 Apr 2012 19:00:27 +0300 Subject: [PATCH 04/11] Initial sketch of SPI settings handler. --- .../node_modules/spiSettingsHandler/README.md | 1 + gpii/node_modules/spiSettingsHandler/index.js | 5 ++ .../spiSettingsHandler/package.json | 19 ++++ .../src/SpiSettingsHandler.js | 87 +++++++++++++++++++ .../spiSettingsHandler/test/testPayload.json | 11 +++ 5 files changed, 123 insertions(+) create mode 100644 gpii/node_modules/spiSettingsHandler/README.md create mode 100644 gpii/node_modules/spiSettingsHandler/index.js create mode 100644 gpii/node_modules/spiSettingsHandler/package.json create mode 100644 gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js create mode 100644 gpii/node_modules/spiSettingsHandler/test/testPayload.json diff --git a/gpii/node_modules/spiSettingsHandler/README.md b/gpii/node_modules/spiSettingsHandler/README.md new file mode 100644 index 000000000..fe2b00083 --- /dev/null +++ b/gpii/node_modules/spiSettingsHandler/README.md @@ -0,0 +1 @@ +The GPII personalisation infrastructure's "settings handler" which uses the SystemParametersInfo Win API function. \ No newline at end of file diff --git a/gpii/node_modules/spiSettingsHandler/index.js b/gpii/node_modules/spiSettingsHandler/index.js new file mode 100644 index 000000000..caf10aed8 --- /dev/null +++ b/gpii/node_modules/spiSettingsHandler/index.js @@ -0,0 +1,5 @@ +var fluid = require("universal"); + +var loader = fluid.getLoader(__dirname); + +loader.require("./src/SpiSettingsHandler.js"); \ No newline at end of file diff --git a/gpii/node_modules/spiSettingsHandler/package.json b/gpii/node_modules/spiSettingsHandler/package.json new file mode 100644 index 000000000..6f7c14539 --- /dev/null +++ b/gpii/node_modules/spiSettingsHandler/package.json @@ -0,0 +1,19 @@ +{ + "name": "spiSettingsHandler", + "description": "A \"settings handler\" for the GPII architecture which uses the SystemParametersInfo Win API function.", + "version": "0.1", + "author": "GPII", + "bugs": "http://wiki.gpii.net/index.php/Main_Page", + "homepage": "http://gpii.net/", + "dependencies": {}, + "licenses": [ + { + "type": "BSD-3-Clause", + "url": "http://www.opensource.org/licenses/BSD-3-Clause" + } + ], + "keywords": ["gpii", "accessibility", "settings", "fluid", "IoC", "Inversion of Control", "configuration", "evented"], + "repository": "git://github.com/GPII/windows.git", + "main": "./index.js", + "engines": { "node" : ">=0.1.9" } +} \ No newline at end of file diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js new file mode 100644 index 000000000..93293997b --- /dev/null +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -0,0 +1,87 @@ +/*! +Windows SystemParametersInfo Settings Handler + +Copyright 2012 Antranig Basman +Copyright 2012 Astea Solutions AD + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/gpii/universal/LICENSE.txt +*/ + +"use strict"; + +var ffi = require("node-ffi"); +var fluid = require("universal"); + +var gpii = fluid.registerNamespace("gpii"); +var windows = fluid.registerNamespace("gpii.windows"); + +var NULL = new ffi.Pointer(0); + +// Guide to node_ffi types and conversions: +// https://github.com/rbranson/node-ffi/wiki/Node-FFI-Tutorial + +var user32 = new ffi.Library('user32', { + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx + // UINT, UINT, PVOID, UINT; return type: BOOL + 'SystemParametersInfoW': [ + 'int32', [ 'uint32', "uint32", 'pointer', 'uint32' ] + ] +}); + +// TODO Define all structures that we will use as parameters to the SPI function. +// See http://wiki.gpii.net/index.php/WSIS_-_Setup_details#Profiles + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd318112(v=vs.85).aspx +windows.HighContrast = ffi.Struct([ + ["uint32", "cbSize"], + ["uint32", "dwFlags"], + ["pointer", "lpszDefaultScheme"] +]); + +// TODO Add all actions that we will use for the SystemParametersInfo function. +windows.actionConstants = { + "SPI_GETHIGHCONTRAST": 0x0042, + "SPI_SETHIGHCONTRAST": 0x0043 +}; + +// TODO Add all flags used across different structures that we need. +windows.flagConstants = { + "HCF_HIGHCONTRASTON": 0x00000001, + "HCF_HOTKEYACTIVE": 0x00000004 +}; + +// TODO Add pointers to all structures that we need to instantiate. +windows.structures = { + "HighContrast": windows.HighContrast +} + +// TODO Haven't tested whether this one works. +windows.callSPI = function(payload) { + var action = windows.actionConstants[payload.action]; + var uiParam = payload.uiParam; + var pvParam; + // TODO Handle other types of pvParams, such as arrays and output parameters. + if (payload.pvParam.type === "struct") { + pvParam = new windows.structures[payload.pvParam.name](); + // TODO Iterate over the options in the payload and set them. + } + // TODO Decide what we need to pass as a last parameter, make it configurable if necessary. + var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam, 0); +} + +// XXX This is just a test function that needs to be removed. +windows.setHCOptions = function() { + var hc = new windows.structures["HighContrast"](); + hc.cbSize = 12; // TODO This shouldn't be hardcoded. ffi.sizeOf doesn't seem to work correctly. + hc.dwFlags = windows.flagConstants["HCF_HOTKEYACTIVE"]; + return hc; +} + +var hc = windows.setHCOptions(); +var returnCode = user32.SystemParametersInfoW(windows.actionConstants["SPI_SETHIGHCONTRAST"], 12, hc.ref(), 0); +// returnCode is 0 if the function fails, 1 if it succeeds. +console.log(returnCode); diff --git a/gpii/node_modules/spiSettingsHandler/test/testPayload.json b/gpii/node_modules/spiSettingsHandler/test/testPayload.json new file mode 100644 index 000000000..4ae7ed7fd --- /dev/null +++ b/gpii/node_modules/spiSettingsHandler/test/testPayload.json @@ -0,0 +1,11 @@ +{ + "action": "SPI_SETHIGHCONTRAST", + "uiParam": 12, // This is the size of the HIGHCONTRAST structure, should not be hardcoded. + "pvParam": { + "type": "struct", + "name": "HighContrast", + "options": { + // TODO: Name-value pairs for the structure contents should go here. + } + } +} From 5d096088496bf056c22450f7f55315eb57934a55 Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Wed, 9 May 2012 17:05:24 +0300 Subject: [PATCH 05/11] Improved version of the SystemParametersInfo settings handler. Now everything is read from JSON. --- .../src/SpiSettingsHandler.js | 95 ++++++++++++++----- .../spiSettingsHandler/test/testPayload.json | 4 +- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 93293997b..5ed2876a3 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -8,7 +8,7 @@ Licensed under the New BSD license. You may not use this file except in compliance with this License. You may obtain a copy of the License at -https://github.com/gpii/universal/LICENSE.txt +https://github.com/GPII/universal/blob/master/LICENSE.txt */ "use strict"; @@ -21,7 +21,7 @@ var windows = fluid.registerNamespace("gpii.windows"); var NULL = new ffi.Pointer(0); -// Guide to node_ffi types and conversions: +// Guide to node-ffi types and conversions: // https://github.com/rbranson/node-ffi/wiki/Node-FFI-Tutorial var user32 = new ffi.Library('user32', { @@ -32,9 +32,6 @@ var user32 = new ffi.Library('user32', { ] }); -// TODO Define all structures that we will use as parameters to the SPI function. -// See http://wiki.gpii.net/index.php/WSIS_-_Setup_details#Profiles - // http://msdn.microsoft.com/en-us/library/windows/desktop/dd318112(v=vs.85).aspx windows.HighContrast = ffi.Struct([ ["uint32", "cbSize"], @@ -42,24 +39,68 @@ windows.HighContrast = ffi.Struct([ ["pointer", "lpszDefaultScheme"] ]); -// TODO Add all actions that we will use for the SystemParametersInfo function. +// TODO Define additional structures used in calls to SystemParametersInfo here. + +/** + * Contains actions that can be used as the first argument of the SystemParametersInfo function. + */ windows.actionConstants = { "SPI_GETHIGHCONTRAST": 0x0042, "SPI_SETHIGHCONTRAST": 0x0043 + // TODO Define additional actions used in calls to SystemParametersInfo here. }; -// TODO Add all flags used across different structures that we need. +/** + * Contains flags used in the "dwFlags" field of various structures + * that are used in calls to the SystemParametersInfo function. + */ windows.flagConstants = { + // HIGHCONTRAST flags + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd318112(v=vs.85).aspx + "HCF_AVAILABLE": 0x00000002, + "HCF_CONFIRMHOTKEY": 0x00000008, "HCF_HIGHCONTRASTON": 0x00000001, - "HCF_HOTKEYACTIVE": 0x00000004 + "HCF_HOTKEYACTIVE": 0x00000004, + "HCF_HOTKEYAVAILABLE": 0x00000040, + "HCF_HOTKEYSOUND": 0x00000010, + "HCF_INDICATOR": 0x00000020 + // TODO Define additional flags that we need here. }; -// TODO Add pointers to all structures that we need to instantiate. +/** + * Contains structures that are used in calls to the SystemParametersInfo function, + * accessible by their names. Used to dynamically instantiate the appropriate structure. + */ windows.structures = { "HighContrast": windows.HighContrast + // TODO Add additional structures that we need to instantiate here. +} + +/** + * Takes an array of flag names, applies binary OR among them and returns the result. + * Used to supply the "dwFlags" argument of some structures. + * + * @param {Array} flagNamesArray An array of flag names. + * These should be predefined in windows.flagConstants. + */ +windows.combineFlags = function(flagNamesArray) { + var combinedFlags = 0; + if (!(flagNamesArray instanceof Array)) { + return 0; + } + for (var index in flagNamesArray) { + combinedFlags = combinedFlags || windows.flagConstants[flagNamesArray[index]]; + } + return combinedFlags; } -// TODO Haven't tested whether this one works. +/** + * Calls the SystemParametersInfo Windows API function with parameters specified in the payload + * input parameter. + * + * @param {Object} payload A JSON string, containing the necessary options for the call. + */ +// TODO Perform input data validation - e.g. whether the options correspond to the structure. windows.callSPI = function(payload) { var action = windows.actionConstants[payload.action]; var uiParam = payload.uiParam; @@ -67,21 +108,31 @@ windows.callSPI = function(payload) { // TODO Handle other types of pvParams, such as arrays and output parameters. if (payload.pvParam.type === "struct") { pvParam = new windows.structures[payload.pvParam.name](); - // TODO Iterate over the options in the payload and set them. + if (pvParam.cbSize !== undefined) { + pvParam.cbSize = ffi.sizeof(windows.structures[payload.pvParam.name]); + } + for (var option in payload.pvParam.options) { + if (option === "dwFlags") { + var combinedFlags = windows.combineFlags(payload.pvParam.options[option]); + pvParam.dwFlags = combinedFlags; + //console.log(combinedFlags); + } else { + pvParam[option] = payload.pvParam.options[option]; + } + } } // TODO Decide what we need to pass as a last parameter, make it configurable if necessary. - var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam, 0); + var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); + console.log("Calling SystemParametersInfo returned " + returnCode); + return returnCode; } -// XXX This is just a test function that needs to be removed. -windows.setHCOptions = function() { - var hc = new windows.structures["HighContrast"](); - hc.cbSize = 12; // TODO This shouldn't be hardcoded. ffi.sizeOf doesn't seem to work correctly. - hc.dwFlags = windows.flagConstants["HCF_HOTKEYACTIVE"]; - return hc; +// TODO Write a test +if (process.argv.length === 3) { + var filename = __dirname + "/" + process.argv[2]; + // Note that this is now possible! + // http://blog.gvm-it.eu/post/8175813806/node-js-0-5-2-load-json-files-with-require + var jsonFile = require(filename); + var returnCode = windows.callSPI(jsonFile); } -var hc = windows.setHCOptions(); -var returnCode = user32.SystemParametersInfoW(windows.actionConstants["SPI_SETHIGHCONTRAST"], 12, hc.ref(), 0); -// returnCode is 0 if the function fails, 1 if it succeeds. -console.log(returnCode); diff --git a/gpii/node_modules/spiSettingsHandler/test/testPayload.json b/gpii/node_modules/spiSettingsHandler/test/testPayload.json index 4ae7ed7fd..647017c31 100644 --- a/gpii/node_modules/spiSettingsHandler/test/testPayload.json +++ b/gpii/node_modules/spiSettingsHandler/test/testPayload.json @@ -1,11 +1,11 @@ { "action": "SPI_SETHIGHCONTRAST", - "uiParam": 12, // This is the size of the HIGHCONTRAST structure, should not be hardcoded. + "uiParam": 12, "pvParam": { "type": "struct", "name": "HighContrast", "options": { - // TODO: Name-value pairs for the structure contents should go here. + "dwFlags": ["HCF_HIGHCONTRASTON", "HCF_HOTKEYACTIVE"] } } } From 771e1e367b7ed0981a98031f1c2df4630c083516 Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Thu, 10 May 2012 18:44:16 +0300 Subject: [PATCH 06/11] Now the SPI settings handler works with payloads from the wiki. --- .../src/SpiSettingsHandler.js | 150 ++++++++++++++---- .../spiSettingsHandler/test/testPayload.json | 28 ++-- 2 files changed, 137 insertions(+), 41 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 5ed2876a3..0649e2863 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -36,7 +36,7 @@ var user32 = new ffi.Library('user32', { windows.HighContrast = ffi.Struct([ ["uint32", "cbSize"], ["uint32", "dwFlags"], - ["pointer", "lpszDefaultScheme"] + ["string", "lpszDefaultScheme"] ]); // TODO Define additional structures used in calls to SystemParametersInfo here. @@ -72,9 +72,9 @@ windows.flagConstants = { * accessible by their names. Used to dynamically instantiate the appropriate structure. */ windows.structures = { - "HighContrast": windows.HighContrast + "HIGHCONTRAST": windows.HighContrast // TODO Add additional structures that we need to instantiate here. -} +}; /** * Takes an array of flag names, applies binary OR among them and returns the result. @@ -92,7 +92,7 @@ windows.combineFlags = function(flagNamesArray) { combinedFlags = combinedFlags || windows.flagConstants[flagNamesArray[index]]; } return combinedFlags; -} +}; /** * Calls the SystemParametersInfo Windows API function with parameters specified in the payload @@ -100,32 +100,121 @@ windows.combineFlags = function(flagNamesArray) { * * @param {Object} payload A JSON string, containing the necessary options for the call. */ +// XXX Remove this code. +// windows.callSPI = function(payload) { + // var action = windows.actionConstants[payload.action]; + // var uiParam = payload.uiParam; + // var pvParam; + // // TODO Handle other types of pvParams, such as arrays and output parameters. + // if (payload.pvParam.type === "struct") { + // pvParam = windows.createEmptyStructure(payload.pvParam.name); + // for (var option in payload.pvParam.options) { + // if (option === "dwFlags") { + // var combinedFlags = windows.combineFlags(payload.pvParam.options[option]); + // pvParam.dwFlags = combinedFlags; + // //console.log(combinedFlags); + // } else { + // pvParam[option] = payload.pvParam.options[option]; + // } + // } + // } + // // TODO Decide what we need to pass as a last parameter, make it configurable if necessary. + // var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); + // console.log("Calling SystemParametersInfo returned " + returnCode); + // //console.log("Name: " + pvParam.lpszDefaultScheme.getCString()); + // for (var member in pvParam) { + // console.log("pvParam member is: " + member); + // } + // var flag = windows.getFlagValue(action, uiParam, "HIGHCONTRAST", "HCF_HOTKEYACTIVE"); + // console.log(flag); + // return returnCode; +// }; + // TODO Perform input data validation - e.g. whether the options correspond to the structure. -windows.callSPI = function(payload) { - var action = windows.actionConstants[payload.action]; - var uiParam = payload.uiParam; - var pvParam; - // TODO Handle other types of pvParams, such as arrays and output parameters. - if (payload.pvParam.type === "struct") { - pvParam = new windows.structures[payload.pvParam.name](); - if (pvParam.cbSize !== undefined) { - pvParam.cbSize = ffi.sizeof(windows.structures[payload.pvParam.name]); - } - for (var option in payload.pvParam.options) { - if (option === "dwFlags") { - var combinedFlags = windows.combineFlags(payload.pvParam.options[option]); - pvParam.dwFlags = combinedFlags; - //console.log(combinedFlags); - } else { - pvParam[option] = payload.pvParam.options[option]; - } - } +windows.spiSettingsHandler = function(payload) { + var getAction = windows.actionConstants[payload.options.getAction]; + var setAction = windows.actionConstants[payload.options.setAction]; + var uiParam = payload.options.uiParam; + var structName = payload.options.pvParamName; + var results = {}; + for (var setting in payload["settings"]) { + results[setting] = {}; + var oldValue = windows.getFlagValue(getAction, uiParam, structName, setting); + results[setting].oldValue = oldValue; + } + // TODO Check what the value is in the payload before setting it ? + windows.setFlagValues(setAction, uiParam, structName, Object.keys(payload.settings)); + for (var setting in payload.settings) { + var newValue = windows.getFlagValue(getAction, uiParam, structName, setting); + results[setting].newValue = newValue; + } + console.log(results); + return results; +}; + +/** + * Creates an empty structure with a given name. It should be registered in the + * windows.structures map with that name. If the structure has a cbSize + * member, the function sets that member to the size of the structure using ffi.sizeof. + * + * @param {String} structName The name of the structure to create. Should be registered in the + * windows.structures map. + * @return {Object} The newly created structure with its cbSize member initialized. + */ +windows.createEmptyStructure = function(structName) { + var struct = new windows.structures[structName](); + if (struct.cbSize !== undefined) { + struct.cbSize = ffi.sizeof(windows.structures[structName]); + } + return struct; +}; + +/** + * Gets the value of a setting stored in the dwFlags member of a given structure. + * Fails if the call to SystemParametersInfo does not succeed. + * + * @param {Integer} action Corresponds to the uiAction parameter of the + * SystemParametersInfo function. + * @param {Integer} uiParam Corresponds to the uiParam parameter of the + * SystemParametersInfo function. + * @param {String} structName The name of the structure where the settings are stored. + * @param {String} flagName The name of the flag whose value we need. + * @return {Boolean} True if the flag is set in the dwFlags member of the structure, + * false otherwise. + */ +windows.getFlagValue = function(action, uiParam, structName, flagName) { + var pvParam = windows.createEmptyStructure(structName); + var callSuccessful = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); + if (!callSuccessful) { + fluid.fail("Could not get flag value with name " + flagName); + } + var flagValue = windows.flagConstants[flagName]; + if (flagValue === undefined) { + fluid.fail("Structure " + structName + " does not contain a flag with name " + flagName + "."); } - // TODO Decide what we need to pass as a last parameter, make it configurable if necessary. - var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); - console.log("Calling SystemParametersInfo returned " + returnCode); - return returnCode; -} + return (pvParam.dwFlags & flagValue) === flagValue; +}; + +/** + * Sets the values of settings stored in the dwFlags member of a given structure. + * + * @param {Integer} action Corresponds to the uiAction parameter of the + * SystemParametersInfo function. + * @param {Integer} uiParam Corresponds to the uiParam parameter of the + * SystemParametersInfo function. + * @param {String} structName The name of the structure where the settings are stored. + * @param {Array} flagNames An array of flag names that need to be set to true. + */ +windows.setFlagValues = function(action, uiParam, structName, flagNames) { + var pvParam = windows.createEmptyStructure(structName); + var combinedFlags = windows.combineFlags(flagNames); + pvParam.dwFlags = combinedFlags; + var callSuccessful = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); + if (!callSuccessful) { + fluid.fail("Could not set flag names " + flagNames + " in structure " + structName); + } + return pvParam; +}; // TODO Write a test if (process.argv.length === 3) { @@ -133,6 +222,5 @@ if (process.argv.length === 3) { // Note that this is now possible! // http://blog.gvm-it.eu/post/8175813806/node-js-0-5-2-load-json-files-with-require var jsonFile = require(filename); - var returnCode = windows.callSPI(jsonFile); -} - + var returnCode = windows.spiSettingsHandler(jsonFile); +}; diff --git a/gpii/node_modules/spiSettingsHandler/test/testPayload.json b/gpii/node_modules/spiSettingsHandler/test/testPayload.json index 647017c31..4ed8ca09b 100644 --- a/gpii/node_modules/spiSettingsHandler/test/testPayload.json +++ b/gpii/node_modules/spiSettingsHandler/test/testPayload.json @@ -1,11 +1,19 @@ { - "action": "SPI_SETHIGHCONTRAST", - "uiParam": 12, - "pvParam": { - "type": "struct", - "name": "HighContrast", - "options": { - "dwFlags": ["HCF_HIGHCONTRASTON", "HCF_HOTKEYACTIVE"] - } - } -} + "options": { + "getAction": "SPI_GETHIGHCONTRAST", + "setAction": "SPI_SETHIGHCONTRAST", + "uiParam": 12, + "pvParamType": "struct", + "pvParamName": "HIGHCONTRAST" + }, + "settings": { + "HCF_HIGHCONTRASTON": { + "value": true, + "dataType": "dwFlags" + }, + "HCF_HOTKEYACTIVE": { + "value": true, + "dataType": "dwFlags" + } + } +} \ No newline at end of file From b9a6edb1ecf6cc9e4d9e8b091df155c70507ad52 Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Fri, 11 May 2012 16:38:46 +0300 Subject: [PATCH 07/11] JSLint-ed the code, removed some unnecessary lines. --- .../node_modules/spiSettingsHandler/README.md | 4 +- .../src/SpiSettingsHandler.js | 71 ++++++------------- 2 files changed, 23 insertions(+), 52 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/README.md b/gpii/node_modules/spiSettingsHandler/README.md index fe2b00083..86afa0be7 100644 --- a/gpii/node_modules/spiSettingsHandler/README.md +++ b/gpii/node_modules/spiSettingsHandler/README.md @@ -1 +1,3 @@ -The GPII personalisation infrastructure's "settings handler" which uses the SystemParametersInfo Win API function. \ No newline at end of file +The GPII personalisation infrastructure's "settings handler" which uses the SystemParametersInfo Win API function. + +Call node node_modules/spiSettingsHandler ../test/testPayload.json from the gpii folder and see high contrast applied. \ No newline at end of file diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 0649e2863..a538bbb85 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -81,9 +81,9 @@ windows.structures = { * Used to supply the "dwFlags" argument of some structures. * * @param {Array} flagNamesArray An array of flag names. - * These should be predefined in windows.flagConstants. + * These should be predefined in windows.flagConstants. */ -windows.combineFlags = function(flagNamesArray) { +windows.combineFlags = function (flagNamesArray) { var combinedFlags = 0; if (!(flagNamesArray instanceof Array)) { return 0; @@ -95,56 +95,25 @@ windows.combineFlags = function(flagNamesArray) { }; /** - * Calls the SystemParametersInfo Windows API function with parameters specified in the payload - * input parameter. - * - * @param {Object} payload A JSON string, containing the necessary options for the call. + * Entry point function of the component. Takes a payload as an input and sets the corresponding + * settings using SystemParametersInfo. Returns an obejct containing the old and new values for each + * of the settings. */ -// XXX Remove this code. -// windows.callSPI = function(payload) { - // var action = windows.actionConstants[payload.action]; - // var uiParam = payload.uiParam; - // var pvParam; - // // TODO Handle other types of pvParams, such as arrays and output parameters. - // if (payload.pvParam.type === "struct") { - // pvParam = windows.createEmptyStructure(payload.pvParam.name); - // for (var option in payload.pvParam.options) { - // if (option === "dwFlags") { - // var combinedFlags = windows.combineFlags(payload.pvParam.options[option]); - // pvParam.dwFlags = combinedFlags; - // //console.log(combinedFlags); - // } else { - // pvParam[option] = payload.pvParam.options[option]; - // } - // } - // } - // // TODO Decide what we need to pass as a last parameter, make it configurable if necessary. - // var returnCode = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); - // console.log("Calling SystemParametersInfo returned " + returnCode); - // //console.log("Name: " + pvParam.lpszDefaultScheme.getCString()); - // for (var member in pvParam) { - // console.log("pvParam member is: " + member); - // } - // var flag = windows.getFlagValue(action, uiParam, "HIGHCONTRAST", "HCF_HOTKEYACTIVE"); - // console.log(flag); - // return returnCode; -// }; - -// TODO Perform input data validation - e.g. whether the options correspond to the structure. -windows.spiSettingsHandler = function(payload) { +windows.spiSettingsHandler = function (payload) { + // TODO Perform input data validation - e.g. whether the options correspond to the structure. var getAction = windows.actionConstants[payload.options.getAction]; var setAction = windows.actionConstants[payload.options.setAction]; var uiParam = payload.options.uiParam; var structName = payload.options.pvParamName; var results = {}; - for (var setting in payload["settings"]) { + for (var setting in payload.settings) { results[setting] = {}; var oldValue = windows.getFlagValue(getAction, uiParam, structName, setting); results[setting].oldValue = oldValue; } - // TODO Check what the value is in the payload before setting it ? + // TODO Check what the value is in the payload before setting it. If it is false, it shouldn't be set. windows.setFlagValues(setAction, uiParam, structName, Object.keys(payload.settings)); - for (var setting in payload.settings) { + for (setting in payload.settings) { var newValue = windows.getFlagValue(getAction, uiParam, structName, setting); results[setting].newValue = newValue; } @@ -158,10 +127,10 @@ windows.spiSettingsHandler = function(payload) { * member, the function sets that member to the size of the structure using ffi.sizeof. * * @param {String} structName The name of the structure to create. Should be registered in the - * windows.structures map. + * windows.structures map. * @return {Object} The newly created structure with its cbSize member initialized. */ -windows.createEmptyStructure = function(structName) { +windows.createEmptyStructure = function (structName) { var struct = new windows.structures[structName](); if (struct.cbSize !== undefined) { struct.cbSize = ffi.sizeof(windows.structures[structName]); @@ -174,15 +143,15 @@ windows.createEmptyStructure = function(structName) { * Fails if the call to SystemParametersInfo does not succeed. * * @param {Integer} action Corresponds to the uiAction parameter of the - * SystemParametersInfo function. + * SystemParametersInfo function. * @param {Integer} uiParam Corresponds to the uiParam parameter of the - * SystemParametersInfo function. + * SystemParametersInfo function. * @param {String} structName The name of the structure where the settings are stored. * @param {String} flagName The name of the flag whose value we need. * @return {Boolean} True if the flag is set in the dwFlags member of the structure, - * false otherwise. + * false otherwise. */ -windows.getFlagValue = function(action, uiParam, structName, flagName) { +windows.getFlagValue = function (action, uiParam, structName, flagName) { var pvParam = windows.createEmptyStructure(structName); var callSuccessful = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); if (!callSuccessful) { @@ -199,13 +168,13 @@ windows.getFlagValue = function(action, uiParam, structName, flagName) { * Sets the values of settings stored in the dwFlags member of a given structure. * * @param {Integer} action Corresponds to the uiAction parameter of the - * SystemParametersInfo function. + * SystemParametersInfo function. * @param {Integer} uiParam Corresponds to the uiParam parameter of the - * SystemParametersInfo function. + * SystemParametersInfo function. * @param {String} structName The name of the structure where the settings are stored. * @param {Array} flagNames An array of flag names that need to be set to true. */ -windows.setFlagValues = function(action, uiParam, structName, flagNames) { +windows.setFlagValues = function (action, uiParam, structName, flagNames) { var pvParam = windows.createEmptyStructure(structName); var combinedFlags = windows.combineFlags(flagNames); pvParam.dwFlags = combinedFlags; @@ -223,4 +192,4 @@ if (process.argv.length === 3) { // http://blog.gvm-it.eu/post/8175813806/node-js-0-5-2-load-json-files-with-require var jsonFile = require(filename); var returnCode = windows.spiSettingsHandler(jsonFile); -}; +} From bf5387d5753af5dc95942fa33e2e2ea056fb735f Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Wed, 30 May 2012 15:40:32 +0300 Subject: [PATCH 08/11] Added NONCLIENTMETRICS and LOGFONT structures to the settings handler. --- .../src/SpiSettingsHandler.js | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index a538bbb85..7fde2c4f5 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -39,6 +39,44 @@ windows.HighContrast = ffi.Struct([ ["string", "lpszDefaultScheme"] ]); +// http://msdn.microsoft.com/en-us/library/windows/desktop/ff729175(v=vs.85).aspx +windows.NonClientMetrics = ffi.Struct([ + ["uint32", "cbSize"], + ["int32", "iBorderWidth"], + ["int32", "iScrollWidth"], + ["int32", "iScrollHeight"], + ["int32", "iCaptionWidth"], + ["int32", "iCaptionHeight"], + ["pointer", "lfCaptionFont"], + ["int32", "iSmCaptionWidth"], + ["int32", "iSmCaptionHeight"], + ["pointer", "lfSmCaptionFont"], + ["int32", "iMenuWidth"], + ["int32", "iMenuHeight"], + ["pointer", "lfMenuFont"], + ["pointer", "lfStatusFont"], + ["pointer", "lfMessageFont"], + ["int32", "iPaddedBorderWidth"] +]); + +// http://msdn.microsoft.com/en-us/library/windows/desktop/dd145037(v=vs.85).aspx +windows.LogFont = ffi.Struct([ + ["int32", "lfHeight"], + ["int32", "lfWidth"], + ["int32", "lfEscapement"], + ["int32", "lfOrientation"], + ["int32", "lfWeight"], + ["uchar", "lfItalic"], + ["uchar", "lfUnderline"], + ["uchar", "lfStrikeOut"], + ["uchar", "lfCharSet"], + ["uchar", "lfOutPrecision"], + ["uchar", "lfClipPrecision"], + ["uchar", "lfQuality"], + ["uchar", "lfPitchAndFamily"], + ["string", "lfFaceName"] +]); + // TODO Define additional structures used in calls to SystemParametersInfo here. /** @@ -46,7 +84,9 @@ windows.HighContrast = ffi.Struct([ */ windows.actionConstants = { "SPI_GETHIGHCONTRAST": 0x0042, - "SPI_SETHIGHCONTRAST": 0x0043 + "SPI_SETHIGHCONTRAST": 0x0043, + "SPI_GETNONCLIENTMETRICS": 0x0029, + "SPI_SETNONCLIENTMETRICS": 0x002A, // TODO Define additional actions used in calls to SystemParametersInfo here. }; @@ -64,7 +104,7 @@ windows.flagConstants = { "HCF_HOTKEYAVAILABLE": 0x00000040, "HCF_HOTKEYSOUND": 0x00000010, "HCF_INDICATOR": 0x00000020 - // TODO Define additional flags that we need here. + // TODO Define additional flags used across various structures here. }; /** @@ -72,7 +112,9 @@ windows.flagConstants = { * accessible by their names. Used to dynamically instantiate the appropriate structure. */ windows.structures = { - "HIGHCONTRAST": windows.HighContrast + "HIGHCONTRAST": windows.HighContrast, + "NONCLIENTMETRICS": windows.NonClientMetrics, + "LOGFONT": windows.LogFont // TODO Add additional structures that we need to instantiate here. }; From a1cf9b1362b3dde918d57b96d02cbfad895828f1 Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Fri, 22 Jun 2012 16:43:38 +0300 Subject: [PATCH 09/11] SPI Settings Handler now returning correct output JSON. First step towards a more generic implementation that support various kinds of settings. --- .../src/SpiSettingsHandler.js | 133 +++++++++++------- 1 file changed, 86 insertions(+), 47 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 7fde2c4f5..45b7482b9 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -83,10 +83,10 @@ windows.LogFont = ffi.Struct([ * Contains actions that can be used as the first argument of the SystemParametersInfo function. */ windows.actionConstants = { - "SPI_GETHIGHCONTRAST": 0x0042, - "SPI_SETHIGHCONTRAST": 0x0043, + "SPI_GETHIGHCONTRAST": 0x0042, + "SPI_SETHIGHCONTRAST": 0x0043, "SPI_GETNONCLIENTMETRICS": 0x0029, - "SPI_SETNONCLIENTMETRICS": 0x002A, + "SPI_SETNONCLIENTMETRICS": 0x002A // TODO Define additional actions used in calls to SystemParametersInfo here. }; @@ -97,13 +97,13 @@ windows.actionConstants = { windows.flagConstants = { // HIGHCONTRAST flags // http://msdn.microsoft.com/en-us/library/windows/desktop/dd318112(v=vs.85).aspx - "HCF_AVAILABLE": 0x00000002, - "HCF_CONFIRMHOTKEY": 0x00000008, - "HCF_HIGHCONTRASTON": 0x00000001, - "HCF_HOTKEYACTIVE": 0x00000004, + "HCF_AVAILABLE": 0x00000002, + "HCF_CONFIRMHOTKEY": 0x00000008, + "HCF_HIGHCONTRASTON": 0x00000001, + "HCF_HOTKEYACTIVE": 0x00000004, "HCF_HOTKEYAVAILABLE": 0x00000040, - "HCF_HOTKEYSOUND": 0x00000010, - "HCF_INDICATOR": 0x00000020 + "HCF_HOTKEYSOUND": 0x00000010, + "HCF_INDICATOR": 0x00000020 // TODO Define additional flags used across various structures here. }; @@ -131,38 +131,11 @@ windows.combineFlags = function (flagNamesArray) { return 0; } for (var index in flagNamesArray) { - combinedFlags = combinedFlags || windows.flagConstants[flagNamesArray[index]]; + combinedFlags = combinedFlags | windows.flagConstants[flagNamesArray[index]]; } return combinedFlags; }; -/** - * Entry point function of the component. Takes a payload as an input and sets the corresponding - * settings using SystemParametersInfo. Returns an obejct containing the old and new values for each - * of the settings. - */ -windows.spiSettingsHandler = function (payload) { - // TODO Perform input data validation - e.g. whether the options correspond to the structure. - var getAction = windows.actionConstants[payload.options.getAction]; - var setAction = windows.actionConstants[payload.options.setAction]; - var uiParam = payload.options.uiParam; - var structName = payload.options.pvParamName; - var results = {}; - for (var setting in payload.settings) { - results[setting] = {}; - var oldValue = windows.getFlagValue(getAction, uiParam, structName, setting); - results[setting].oldValue = oldValue; - } - // TODO Check what the value is in the payload before setting it. If it is false, it shouldn't be set. - windows.setFlagValues(setAction, uiParam, structName, Object.keys(payload.settings)); - for (setting in payload.settings) { - var newValue = windows.getFlagValue(getAction, uiParam, structName, setting); - results[setting].newValue = newValue; - } - console.log(results); - return results; -}; - /** * Creates an empty structure with a given name. It should be registered in the * windows.structures map with that name. If the structure has a cbSize @@ -181,29 +154,41 @@ windows.createEmptyStructure = function (structName) { }; /** - * Gets the value of a setting stored in the dwFlags member of a given structure. - * Fails if the call to SystemParametersInfo does not succeed. + * Makes a call to SystemParametersInfo when the pvParam parameter is a structure. + * Returns the structure populated with the system-wide settings. This should be used for getting + * values in a structure only. * * @param {Integer} action Corresponds to the uiAction parameter of the * SystemParametersInfo function. * @param {Integer} uiParam Corresponds to the uiParam parameter of the * SystemParametersInfo function. * @param {String} structName The name of the structure where the settings are stored. - * @param {String} flagName The name of the flag whose value we need. - * @return {Boolean} True if the flag is set in the dwFlags member of the structure, - * false otherwise. + * @return {Object} The populated structure as a JSON object. */ -windows.getFlagValue = function (action, uiParam, structName, flagName) { +windows.getSettingsInStructure = function (action, uiParam, structName) { var pvParam = windows.createEmptyStructure(structName); var callSuccessful = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); if (!callSuccessful) { - fluid.fail("Could not get flag value with name " + flagName); + fluid.fail("Could not get " + action + " settings in structure " + structName); } + return pvParam; +}; + +/** + * Gets the value of a setting stored in the dwFlags member of a given structure. + * Fails if the call to SystemParametersInfo does not succeed. + * + * @param {Object} settingsStruct The structure that contains the flag specified. + * @param {String} flagName The name of the flag whose value we need. + * @return {Boolean} True if the flag is set in the dwFlags member of the structure, + * false otherwise. + */ +windows.getFlagValue = function (settingsStruct, flagName) { var flagValue = windows.flagConstants[flagName]; if (flagValue === undefined) { - fluid.fail("Structure " + structName + " does not contain a flag with name " + flagName + "."); + fluid.fail("Structure does not contain a flag with name " + flagName); } - return (pvParam.dwFlags & flagValue) === flagValue; + return (settingsStruct.dwFlags & flagValue) === flagValue; }; /** @@ -227,7 +212,61 @@ windows.setFlagValues = function (action, uiParam, structName, flagNames) { return pvParam; }; -// TODO Write a test +/** + * Populates the results payload that is returned from the SpiSettingsHandler. These + * results contain the old and new values for each setting in the input payload. + * + * @param {Object} payload The input that is passed to the SPI Settings Handler + * @param {Boolean} isNewValue True if the updated values of the settings are populated, false + * otherwise. + * @param {Object} results The results object to be populated with the old and new settings values. + */ +windows.populateResults = function (payload, isNewValue, results) { + var getAction = windows.actionConstants[payload.options.getAction]; + var uiParam = payload.options.uiParam; + var structName = payload.options.pvParamName; + var settingsStruct = windows.getSettingsInStructure(getAction, uiParam, structName); + for (var currentSetting in payload.settings) { + if (!isNewValue) { + results[currentSetting] = {}; + } + var valueToSet; + var settingValue = payload.settings[currentSetting]; + if (settingValue.dataType === "dwFlags") { + valueToSet = windows.getFlagValue(settingsStruct, currentSetting); + } else { + valueToSet = settingsStruct[currentSetting]; + } + if (isNewValue) { + results[currentSetting].newValue = valueToSet; + } else { + results[currentSetting].oldValue = valueToSet; + } + + } +}; + +/** + * Entry point function of the component. Takes a payload as an input and sets the corresponding + * settings using the SystemParametersInfoW Windows API function. Returns an object containing + * the old and new values for each of the settings. + */ +windows.spiSettingsHandler = function (payload) { + // TODO Add support for settings that are not stored in structures. + var setAction = windows.actionConstants[payload.options.setAction]; + var uiParam = payload.options.uiParam; + var structName = payload.options.pvParamName; + + var results = {}; + windows.populateResults(payload, false, results); + // FIXME Filter flag values from non-flag values. Support setting non-flag values. + windows.setFlagValues(setAction, uiParam, structName, Object.keys(payload.settings)); + windows.populateResults(payload, true, results); + console.log(results); + return results; +}; + +// FIXME Write a test if (process.argv.length === 3) { var filename = __dirname + "/" + process.argv[2]; // Note that this is now possible! From 2afc40833da5f1b2b82b82b10aafe6f9c66e643a Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Mon, 25 Jun 2012 19:26:31 +0300 Subject: [PATCH 10/11] Supporting dwFlags settings together with regular ones. Changed payload to reflect that. --- .../src/SpiSettingsHandler.js | 22 ++++++++++++------- .../spiSettingsHandler/test/testPayload.json | 4 ++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 45b7482b9..1216287bc 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -11,7 +11,9 @@ You may obtain a copy of the License at https://github.com/GPII/universal/blob/master/LICENSE.txt */ -"use strict"; +"use strict"; + +var FLAG_DATA_TYPE = "dwFlags"; var ffi = require("node-ffi"); var fluid = require("universal"); @@ -232,7 +234,7 @@ windows.populateResults = function (payload, isNewValue, results) { } var valueToSet; var settingValue = payload.settings[currentSetting]; - if (settingValue.dataType === "dwFlags") { + if (settingValue.dataType === FLAG_DATA_TYPE) { valueToSet = windows.getFlagValue(settingsStruct, currentSetting); } else { valueToSet = settingsStruct[currentSetting]; @@ -242,7 +244,6 @@ windows.populateResults = function (payload, isNewValue, results) { } else { results[currentSetting].oldValue = valueToSet; } - } }; @@ -252,15 +253,22 @@ windows.populateResults = function (payload, isNewValue, results) { * the old and new values for each of the settings. */ windows.spiSettingsHandler = function (payload) { - // TODO Add support for settings that are not stored in structures. + // TODO Add support for settings that are not stored in structures when such are needed. var setAction = windows.actionConstants[payload.options.setAction]; var uiParam = payload.options.uiParam; var structName = payload.options.pvParamName; var results = {}; windows.populateResults(payload, false, results); - // FIXME Filter flag values from non-flag values. Support setting non-flag values. - windows.setFlagValues(setAction, uiParam, structName, Object.keys(payload.settings)); + + var flagNames = []; + for (var currentSetting in payload.settings) { + if (payload.settings[currentSetting].dataType === FLAG_DATA_TYPE) { + flagNames.push(currentSetting); + } + } + windows.setFlagValues(setAction, uiParam, structName, flagNames); + windows.populateResults(payload, true, results); console.log(results); return results; @@ -269,8 +277,6 @@ windows.spiSettingsHandler = function (payload) { // FIXME Write a test if (process.argv.length === 3) { var filename = __dirname + "/" + process.argv[2]; - // Note that this is now possible! - // http://blog.gvm-it.eu/post/8175813806/node-js-0-5-2-load-json-files-with-require var jsonFile = require(filename); var returnCode = windows.spiSettingsHandler(jsonFile); } diff --git a/gpii/node_modules/spiSettingsHandler/test/testPayload.json b/gpii/node_modules/spiSettingsHandler/test/testPayload.json index 4ed8ca09b..4655e8dcd 100644 --- a/gpii/node_modules/spiSettingsHandler/test/testPayload.json +++ b/gpii/node_modules/spiSettingsHandler/test/testPayload.json @@ -14,6 +14,10 @@ "HCF_HOTKEYACTIVE": { "value": true, "dataType": "dwFlags" + }, + "lpszDefaultScheme": { + "value": "High Contrast Black", + "dataType": "pointer" } } } \ No newline at end of file From 06ab7b0e1ba2b9696ae193e81b253fed551730f4 Mon Sep 17 00:00:00 2001 From: Boyan Sheytanov Date: Tue, 26 Jun 2012 18:48:36 +0300 Subject: [PATCH 11/11] Trying to get the settings handler running for NONCLIENTMETRICS --- .../src/SpiSettingsHandler.js | 17 ++++++++++++++--- .../spiSettingsHandler/test/testPayload.json | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js index 1216287bc..916b30a3b 100644 --- a/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js +++ b/gpii/node_modules/spiSettingsHandler/src/SpiSettingsHandler.js @@ -34,11 +34,20 @@ var user32 = new ffi.Library('user32', { ] }); +var kernel32 = new ffi.Library('kernel32', { + 'GetLastError': [ + 'int32', [] + ], + 'SetLastError': [ + 'void', [ 'int32' ] + ] +}); + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd318112(v=vs.85).aspx windows.HighContrast = ffi.Struct([ ["uint32", "cbSize"], ["uint32", "dwFlags"], - ["string", "lpszDefaultScheme"] + ["pointer", "lpszDefaultScheme"] ]); // http://msdn.microsoft.com/en-us/library/windows/desktop/ff729175(v=vs.85).aspx @@ -58,7 +67,7 @@ windows.NonClientMetrics = ffi.Struct([ ["pointer", "lfMenuFont"], ["pointer", "lfStatusFont"], ["pointer", "lfMessageFont"], - ["int32", "iPaddedBorderWidth"] + ["int32", "iPaddedBorderWidth"] ]); // http://msdn.microsoft.com/en-us/library/windows/desktop/dd145037(v=vs.85).aspx @@ -171,7 +180,9 @@ windows.getSettingsInStructure = function (action, uiParam, structName) { var pvParam = windows.createEmptyStructure(structName); var callSuccessful = user32.SystemParametersInfoW(action, uiParam, pvParam.ref(), 0); if (!callSuccessful) { - fluid.fail("Could not get " + action + " settings in structure " + structName); + var errorCode = kernel32.GetLastError(); + console.log("Last error: " + errorCode); + fluid.fail("Could not get settings in structure " + structName); } return pvParam; }; diff --git a/gpii/node_modules/spiSettingsHandler/test/testPayload.json b/gpii/node_modules/spiSettingsHandler/test/testPayload.json index 4655e8dcd..f69225c5e 100644 --- a/gpii/node_modules/spiSettingsHandler/test/testPayload.json +++ b/gpii/node_modules/spiSettingsHandler/test/testPayload.json @@ -20,4 +20,17 @@ "dataType": "pointer" } } -} \ No newline at end of file +} + +// { + // "options": { + // "getAction": "SPI_GETNONCLIENTMETRICS", + // "setAction": "SPI_SETNONCLIENTMETRICS", + // "uiParam": 64, + // "pvParamType": "struct", + // "pvParamName": "NONCLIENTMETRICS" + // }, + // "settings": { +// + // } +// } \ No newline at end of file