├── .gitignore ├── test ├── binding.gyp ├── native_tests.cc └── array.js ├── .travis.yml ├── package.json ├── appveyor.yml ├── index.d.ts ├── History.md ├── README.md └── lib └── array.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /test/build 3 | /?.js 4 | /package-lock.json 5 | -------------------------------------------------------------------------------- /test/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'native_tests', 5 | 'sources': [ 'native_tests.cc' ], 6 | 'include_dirs': [ 7 | ' 2 | #include /* abs */ 3 | #include "nan.h" 4 | 5 | using namespace node; 6 | 7 | namespace { 8 | 9 | NAN_METHOD(ArrayAbs) { 10 | int *arr = reinterpret_cast(Buffer::Data(info[0].As())); 11 | uint32_t length = info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust(); 12 | for (uint32_t i = 0; i < length; i++) { 13 | *(arr + i) = abs(arr[i]); 14 | } 15 | } 16 | 17 | void Initialize(v8::Local target) { 18 | Nan::HandleScope scope; 19 | Nan::SetMethod(target, "arrayAbs", ArrayAbs); 20 | } 21 | 22 | } // anonymous namespace 23 | 24 | NODE_MODULE(native_tests, Initialize); 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | env: 4 | - CXX=g++-4.8 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-4.8 11 | 12 | language: node_js 13 | 14 | node_js: 15 | - "6" 16 | - "7" 17 | - "8" 18 | - "10" 19 | - "12" 20 | - "14" 21 | 22 | install: 23 | - PATH="`npm bin`:`npm bin -g`:$PATH" 24 | # Node 0.8 comes with a too obsolete npm 25 | - if [[ "`node --version`" =~ ^v0\.8\. ]]; then npm install -g npm@1.4.28 ; fi 26 | # Install dependencies and build 27 | - npm install 28 | 29 | script: 30 | # Output useful info for debugging 31 | - node --version 32 | - npm --version 33 | # Run tests 34 | - npm test 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ref-array-napi", 3 | "description": "Create C \"array\" instances on top of Buffers. Supports Node 6, 7, 8, 10, 12.", 4 | "keywords": [ 5 | "array", 6 | "ref" 7 | ], 8 | "version": "1.2.2", 9 | "author": "Nathan Rajlich (http://tootallnate.net) & Janealter", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/Janealter/ref-array-napi.git" 13 | }, 14 | "main": "./lib/array.js", 15 | "license": "MIT", 16 | "scripts": { 17 | "test": "node-gyp rebuild --directory test && mocha -gc --reporter spec" 18 | }, 19 | "dependencies": { 20 | "array-index": "1", 21 | "debug": "2", 22 | "@breush/ref-napi": "^4.0.9" 23 | }, 24 | "devDependencies": { 25 | "bindings": "1", 26 | "mocha": "3", 27 | "nan": "2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # http://www.appveyor.com/docs/appveyor-yml 2 | 3 | # Test against these versions of Node.js. 4 | environment: 5 | # Visual Studio Version 6 | MSVS_VERSION: 2015 7 | # Test against these versions of Node.js and io.js 8 | matrix: 9 | - nodejs_version: "6" 10 | - nodejs_version: "7" 11 | - nodejs_version: "8" 12 | - nodejs_version: "10" 13 | - nodejs_version: "12" 14 | - nodejs_version: "14" 15 | 16 | platform: 17 | - x86 18 | - x64 19 | 20 | # Install scripts. (runs after repo cloning) 21 | install: 22 | # Get the latest stable version of Node 0.STABLE.latest 23 | - ps: if($env:nodejs_version -eq "0.8") {Install-Product node $env:nodejs_version} 24 | - ps: if($env:nodejs_version -ne "0.8") {Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)} 25 | # Node 0.8 comes with a too obsolete npm 26 | - IF %nodejs_version% == 0.8 (npm install -g npm@1.4.28) 27 | # Install latest NPM only for node.js versions until built in node-gyp adds io.js support 28 | # Update is required for node.js 0.8 because built in npm(node-gyp) does not know VS2013 29 | - IF %nodejs_version% LSS 1 (npm install -g npm@2) 30 | - IF %nodejs_version% LSS 1 set PATH=%APPDATA%\npm;%PATH% 31 | # Typical npm stuff. 32 | - npm install --msvs_version=%MSVS_VERSION% 33 | 34 | # Post-install test scripts. 35 | test_script: 36 | # Output useful info for debugging. 37 | - node --version 38 | - npm --version 39 | # run tests 40 | - npm test 41 | 42 | # Don't actually build. 43 | build: off 44 | 45 | # Set build version format here instead of in the admin panel. 46 | version: "{build}" 47 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for ref-array-napi 2 | // Project: https://github.com/Janealter/ref-array-napi 3 | // Definitions by: goooseman 4 | // Used definitions by: Paul Loyd 5 | // TypeScript Version: 3.7 6 | 7 | import ref = require('@breush/ref-napi'); 8 | 9 | interface ArrayTypeInstance { 10 | [i: number]: T; 11 | length: number; 12 | toArray(): T[]; 13 | toJSON(): T[]; 14 | inspect(): string; 15 | buffer: Buffer; 16 | ref(): Buffer; 17 | } 18 | 19 | interface ArrayType extends ref.Type { 20 | BYTES_PER_ELEMENT: number; 21 | fixedLength: number; 22 | /** The reference to the base type. */ 23 | type: ref.Type; 24 | 25 | /** 26 | * Accepts a Buffer instance that should be an already-populated with data 27 | * for the ArrayType. The "length" of the Array is determined by searching 28 | * through the buffer's contents until an aligned NULL pointer is encountered. 29 | */ 30 | untilZeros(buffer: Buffer): ArrayTypeInstance; 31 | 32 | new (length?: number): ArrayTypeInstance; 33 | new (data: number[], length?: number): ArrayTypeInstance; 34 | new (data: Buffer, length?: number): ArrayTypeInstance; 35 | (length?: number): ArrayTypeInstance; 36 | (data: number[], length?: number): ArrayTypeInstance; 37 | (data: Buffer, length?: number): ArrayTypeInstance; 38 | } 39 | 40 | /** 41 | * The array type meta-constructor. 42 | * The returned constructor's API is highly influenced by the WebGL 43 | * TypedArray API. 44 | */ 45 | declare const ArrayType: { 46 | (type: ref.Type, length?: number): ArrayType; 47 | (type: string, length?: number): ArrayType; 48 | }; 49 | 50 | export = ArrayType; 51 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 1.2.0 / 2016-10-14 2 | ================== 3 | 4 | * [[`ba418e8fb3`](https://github.com/TooTallNate/ref-array/commit/ba418e8fb3)] - update "array-index" to v1 (Nathan Rajlich) 5 | * [[`991e894d75`](https://github.com/TooTallNate/ref-array/commit/991e894d75)] - **package**: add "license" field (Nathan Rajlich) 6 | * [[`d0565662a3`](https://github.com/TooTallNate/ref-array/commit/d0565662a3)] - **package**: update dev deps (Nathan Rajlich) 7 | * [[`cce5fb960c`](https://github.com/TooTallNate/ref-array/commit/cce5fb960c)] - **array**: s/slicer/slice/ (Nathan Rajlich) 8 | * [[`8050a81587`](https://github.com/TooTallNate/ref-array/commit/8050a81587)] - **appveyor**: fix node v0.8, test io.js v2.5 and v3 (Nathan Rajlich) 9 | * [[`c636c37e80`](https://github.com/TooTallNate/ref-array/commit/c636c37e80)] - **test**: add a test for passing Arrays to a native addon function (Nathan Rajlich) 10 | 11 | 1.1.2 / 2015-08-27 12 | ================== 13 | 14 | * appveyor: test node v0.12, and x64 platform 15 | * package: allow any "ref" v1 16 | * package: update "nan" to v2 for tests 17 | * package: update "array-index" to v0.1.1 18 | * travis: fix node v0.8 and add iojs v2.5 and v3 19 | * travis: remove node v0.6, v0.11, add v0.12 20 | * README: use SVG appveyor badge 21 | 22 | 1.1.1 / 2015-03-24 23 | ================== 24 | 25 | * Update package.json deps (nan, ref) (#7) 26 | 27 | 1.1.0 / 2015-01-02 28 | ================== 29 | 30 | * Add slice API (#4, @toots) 31 | 32 | 1.0.0 / 2014-11-03 33 | ================== 34 | 35 | * gitignore: ignore top-level single letter ?.js test files 36 | * package: allow any "debug" v2 37 | * bumping to v1.0.0 for better-defined semver semantics 38 | 39 | 0.0.4 / 2014-06-19 40 | ================== 41 | 42 | * package: update "debug" to v1.0.2 and "ref" to v0.3.2 43 | * README: add appveyor badge 44 | * add appveyor.yml file 45 | * test: nan-ify the tests, for node v0.11.x 46 | * package: loosely pin the deps, update array-index to v0.1.0 47 | * package: remove "engines" field 48 | * package: beautify 49 | * README: use svg for Travis badge 50 | * travis: test node v0.8, v0.10, and v0.11 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ref-array-napi 2 | ========= 3 | ### Create C typed "array" instances on top of Buffers 4 | [![Build Status](https://travis-ci.org/Janealter/ref-array-napi.svg)](https://travis-ci.org/Janealter/ref-array-napi) 5 | [![Build Status](https://ci.appveyor.com/api/projects/status/gibqc0mg6dkdn9o4?svg=true)](https://ci.appveyor.com/project/Janealter/ref-array-napi) 6 | 7 | 8 | This module offers an "array" implementation on top of Node.js Buffers using 9 | the ref "type" interface. Supports Node 6, 7, 8, 10, 12. 10 | 11 | Installation 12 | ------------ 13 | 14 | Install with `npm`: 15 | 16 | ``` bash 17 | $ npm install ref-array-napi 18 | ``` 19 | 20 | 21 | Examples 22 | -------- 23 | 24 | #### Basic usage 25 | 26 | ``` js 27 | var ref = require('@breush/ref-napi') 28 | var ArrayType = require('ref-array-napi') 29 | 30 | // typedef 31 | var int = ref.types.int 32 | 33 | // define the "int[]" type 34 | var IntArray = ArrayType(int) 35 | 36 | // now we can create array instances; the constructor takes the same arguments 37 | // the native JS Array class 38 | 39 | var a = new IntArray(5) // by length 40 | a.length // 5 41 | a[0] = 0 42 | a[1] = 1 43 | a[2] = -1 44 | a[3] = 2 45 | a[4] = -2 46 | 47 | var b = new IntArray([1, 2, 3, 4, 5]) // with an existing Array 48 | b.length // 5 49 | b[0] // 1 50 | b[1] // 2 51 | b[2] // 3 52 | b[3] // 4 53 | b[4] // 5 54 | ``` 55 | 56 | #### Reading a `NULL`-terminated Array 57 | 58 | ``` js 59 | // sometimes you get a variable length array that is terminated by a NULL byte. 60 | var buf = new Buffer(int.size * 3) 61 | int.set(buf, int.size * 0, 5) 62 | int.set(buf, int.size * 1, 8) 63 | int.set(buf, int.size * 2, 0) // <- terminate with 0s 64 | 65 | // you can create an array instance with the length automatically determined 66 | var array = IntArray.untilZeros(buf) 67 | console.log(array.length) 68 | // 2 69 | console.log(array) 70 | // [ 5, 8 ] 71 | ``` 72 | 73 | #### With `node-ffi` 74 | 75 | ``` js 76 | var ffi = require('ffi') 77 | 78 | // the "int[]" type may be used as a "type" in FFI'd functions or callbacks 79 | var func = ffi.ForeignFunction(funcPointer, int, [ IntArray, int ]) 80 | 81 | var arg = new IntArray(3) 82 | arg[0] = 1234 83 | arg[1] = -9999 84 | arg[2] = 1 85 | 86 | var rtn = func(arg, arg.length) 87 | ``` 88 | 89 | 90 | License 91 | ------- 92 | 93 | (The MIT License) 94 | 95 | Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net> 96 | 97 | Permission is hereby granted, free of charge, to any person obtaining 98 | a copy of this software and associated documentation files (the 99 | 'Software'), to deal in the Software without restriction, including 100 | without limitation the rights to use, copy, modify, merge, publish, 101 | distribute, sublicense, and/or sell copies of the Software, and to 102 | permit persons to whom the Software is furnished to do so, subject to 103 | the following conditions: 104 | 105 | The above copyright notice and this permission notice shall be 106 | included in all copies or substantial portions of the Software. 107 | 108 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 109 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 110 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 111 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 112 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 113 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 114 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 115 | -------------------------------------------------------------------------------- /test/array.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | , ref = require('@breush/ref-napi') 4 | , ArrayType = require('../') 5 | , bindings = require('bindings')({ module_root: __dirname, bindings: 'native_tests' }) 6 | 7 | describe('Array', function () { 8 | 9 | afterEach(gc) 10 | 11 | it('should be a function', function () { 12 | assert.equal('function', typeof ArrayType) 13 | }) 14 | 15 | describe('char[]', function () { 16 | var CharArray = ArrayType('char') 17 | 18 | it('should map directly to a "string"', function () { 19 | var b = new Buffer('hello', 'ascii') 20 | var a = new CharArray(b) 21 | assert.equal(b.length, a.length) 22 | for (var i = 0; i < b.length; i++) { 23 | assert.equal(a[i], b[i]) 24 | } 25 | }) 26 | 27 | it('should throw an Error when invoked with no arguments', function () { 28 | assert.throws(function () { 29 | new CharArray() 30 | }) 31 | }) 32 | 33 | }) 34 | 35 | describe('int32[]', function () { 36 | var Int32Array = ArrayType('int32') 37 | 38 | it('should have the type\'s size be pointer-sized by default', function () { 39 | assert.equal(ref.sizeof.pointer, Int32Array.size) 40 | }) 41 | 42 | it('should act like an Int32Array with a number', function () { 43 | var a = new Int32Array(5) 44 | assert.equal(5, a.length) 45 | assert.equal(20, a.buffer.length) 46 | a[0] = 0 47 | a[1] = 10 48 | a[2] = 234 49 | a[3] = 69 50 | a[4] = 1410214 51 | assert.deepEqual([0, 10, 234, 69, 1410214], a.toArray()) 52 | }) 53 | 54 | it('should act like an Int32Array with an array', function () { 55 | var input = [ 1, 4, 91, 123123, 5123512, 0, -1 ] 56 | var a = new Int32Array(input) 57 | assert.equal(input.length, a.length) 58 | assert.deepEqual(input, a.toArray()) 59 | }) 60 | 61 | it('should throw an Error when invoked with no arguments', function () { 62 | assert.throws(function () { 63 | new Int32Array() 64 | }) 65 | }) 66 | 67 | }) 68 | 69 | describe('void *[]', function () { 70 | var VoidPtrArray = ArrayType('void *') 71 | 72 | it('should have each element be "pointer" sized', function () { 73 | assert.equal(ref.sizeof.pointer, VoidPtrArray.BYTES_PER_ELEMENT) 74 | }) 75 | 76 | it('should accept arbitrary pointers', function () { 77 | var a = new VoidPtrArray(5) 78 | assert.equal(5, a.length) 79 | assert.equal(a.length * ref.sizeof.pointer, a.buffer.length) 80 | var ptr1 = Buffer(1) 81 | var ptr2 = Buffer(1) 82 | var ptr3 = Buffer(1) 83 | a[0] = ref.NULL 84 | a[1] = ref.NULL_POINTER 85 | a[2] = ptr1 86 | a[3] = ptr2 87 | a[4] = ptr3 88 | 89 | assert.equal(a[0].address(), ref.NULL.address()) 90 | assert.equal(a[1].address(), ref.NULL_POINTER.address()) 91 | assert.equal(a[2].address(), ptr1.address()) 92 | assert.equal(a[3].address(), ptr2.address()) 93 | assert.equal(a[4].address(), ptr3.address()) 94 | }) 95 | 96 | it('should throw an Error when invoked with no arguments', function () { 97 | assert.throws(function () { 98 | new VoidPtrArray() 99 | }) 100 | }) 101 | 102 | }) 103 | 104 | describe('fixed size arrays', function () { 105 | 106 | describe('int[10]', function () { 107 | var int = ref.types.int 108 | var IntArrayTen = ArrayType(int, 10) 109 | 110 | it('should have the type\'s "size" property match the default size', function () { 111 | assert.equal(int.size * 10, IntArrayTen.size) 112 | }) 113 | 114 | it('should be created when invoked with no arguments', function () { 115 | var array = new IntArrayTen() 116 | assert.equal(10, array.length) 117 | }) 118 | 119 | }) 120 | 121 | }) 122 | 123 | describe('.untilZeros(buffer)', function () { 124 | 125 | it('should read a Buffer until a NULL pointer is found', function () { 126 | var int = ref.types.int 127 | var IntArray = ArrayType(int) 128 | 129 | // manually create a NULL-terminated int[] 130 | var buf = new Buffer(int.size * 3) 131 | int.set(buf, int.size * 0, 5) 132 | int.set(buf, int.size * 1, 8) 133 | int.set(buf, int.size * 2, 0) // <- terminate with 0s 134 | 135 | var array = IntArray.untilZeros(buf) 136 | assert.equal(2, array.length) 137 | assert.equal(5, array[0]) 138 | assert.equal(8, array[1]) 139 | }) 140 | 141 | }) 142 | 143 | describe('.ref()', function () { 144 | var IntArray = ArrayType('int') 145 | 146 | it('should return a Buffer that points to the base "buffer"', function () { 147 | var a = new IntArray(1) 148 | var r = a.ref() 149 | assert.equal(r.readPointer(0).address(), a.buffer.address()) 150 | }) 151 | 152 | it('should return a Buffer with "indirection" equal to 1', function () { 153 | var a = new IntArray(1) 154 | var r = a.ref() 155 | assert.equal(1, r.type.indirection) 156 | }) 157 | 158 | it('should .deref() back into an instance of the ArrayType type', function () { 159 | var a = new IntArray([ 69 ]) 160 | var r = a.ref() 161 | var _a = r.deref() 162 | assert(_a instanceof IntArray) 163 | assert.equal(a.buffer.address(), _a.buffer.address()) 164 | assert.equal(1, a.length) 165 | assert.equal(69, a[0]) 166 | }) 167 | 168 | }) 169 | 170 | describe('.slice()', function () { 171 | var IntArray = ArrayType('int') 172 | 173 | it('should be able to return a slice of the base array', function () { 174 | var x = new IntArray([12, 34, 56, 78]) 175 | var y = x.slice(1,3) 176 | assert.equal(y.length, 2) 177 | assert.equal(y[0], x[1]) 178 | assert.equal(y[1], x[2]) 179 | }) 180 | 181 | it('should be able to use only start parameter', function () { 182 | var x = new IntArray([12, 34, 56, 78]) 183 | var y = x.slice(1) 184 | assert.equal(y.length, 3) 185 | assert.equal(y[0], x[1]) 186 | assert.equal(y[1], x[2]) 187 | assert.equal(y[2], x[3]) 188 | }) 189 | }) 190 | 191 | describe('native tests', function () { 192 | var IntArray = ArrayType('int') 193 | 194 | it('should be pass an Array to a native function', function () { 195 | var input = [-1, -25, -3111, -41234, -5] 196 | var array = new IntArray(input) 197 | 198 | bindings.arrayAbs(array.buffer, array.length) 199 | 200 | assert.deepEqual(array.toJSON(), input.map(Math.abs)) 201 | }) 202 | 203 | }) 204 | 205 | }) 206 | -------------------------------------------------------------------------------- /lib/array.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var _ref = require('@breush/ref-napi') 7 | var assert = require('assert') 8 | var debug = require('debug')('ref:array') 9 | var ArrayIndex = require('array-index') 10 | var isArray = Array.isArray 11 | 12 | /** 13 | * The Array "type" constructor. 14 | * The returned constructor's API is highly influenced by the WebGL 15 | * TypedArray API. 16 | */ 17 | 18 | module.exports = function Array (_type, _length) { 19 | debug('defining new array "type"') 20 | var type = _ref.coerceType(_type) 21 | var fixedLength = _length | 0 22 | 23 | /** 24 | * This is the ArrayType "constructor" that gets returned. 25 | */ 26 | 27 | function ArrayType (data, length) { 28 | if (!(this instanceof ArrayType)) { 29 | return new ArrayType(data, length) 30 | } 31 | debug('creating new array instance') 32 | ArrayIndex.call(this) 33 | var item_size = ArrayType.BYTES_PER_ELEMENT 34 | if (0 === arguments.length) { 35 | // new IntArray() 36 | // use the "fixedLength" if provided, otherwise throw an Error 37 | if (fixedLength > 0) { 38 | this.length = fixedLength 39 | this.buffer = new Buffer(this.length * item_size) 40 | } else { 41 | throw new Error('A "length", "array" or "buffer" must be passed as the first argument') 42 | } 43 | } else if ('number' == typeof data) { 44 | // new IntArray(69) 45 | this.length = data 46 | this.buffer = new Buffer(this.length * item_size) 47 | } else if (isArray(data)) { 48 | // new IntArray([ 1, 2, 3, 4, 5 ], {len}) 49 | // use optional "length" if provided, otherwise use "fixedLength, otherwise 50 | // use the Array's .length 51 | var len = 0 52 | if (null != length) { 53 | len = length 54 | } else if (fixedLength > 0) { 55 | len = fixedLength 56 | } else { 57 | len = data.length 58 | } 59 | if (data.length < len) { 60 | throw new Error('array length must be at least ' + len + ', got ' + data.length) 61 | } 62 | this.length = len 63 | this.buffer = new Buffer(len * item_size) 64 | for (var i = 0; i < len; i++) { 65 | this[i] = data[i] 66 | } 67 | } else if (Buffer.isBuffer(data)) { 68 | // new IntArray(Buffer(8)) 69 | var len = 0 70 | if (null != length) { 71 | len = length 72 | } else if (fixedLength > 0) { 73 | len = fixedLength 74 | } else { 75 | len = data.length / item_size | 0 76 | } 77 | var expectedLength = item_size * len 78 | this.length = len 79 | if (data.length != expectedLength) { 80 | if (data.length < expectedLength) { 81 | throw new Error('buffer length must be at least ' + expectedLength + ', got ' + data.length) 82 | } else { 83 | debug('resizing buffer from %d to %d', data.length, expectedLength) 84 | data = data.slice(0, expectedLength) 85 | } 86 | } 87 | this.buffer = data 88 | } 89 | } 90 | 91 | // make array instances inherit from our `ArrayIndex.prototype` 92 | ArrayType.prototype = Object.create(ArrayIndex.prototype, { 93 | constructor: { 94 | value: ArrayType, 95 | enumerable: false, 96 | writable: true, 97 | configurable: true 98 | }, 99 | // "buffer" is the backing buffer instance 100 | buffer: { 101 | value: _ref.NULL, 102 | enumerable: true, 103 | writable: true, 104 | configurable: true 105 | }, 106 | // "node-ffi" calls this when passed an array instance to an ffi'd function 107 | ref: { 108 | value: ref, 109 | enumerable: true, 110 | writable: true, 111 | configurable: true 112 | }, 113 | // "slice" implementation 114 | slice: { 115 | value: slice, 116 | enumerable: true, 117 | writable: true, 118 | configurable: true 119 | } 120 | }) 121 | 122 | // part of the "array-index" interface 123 | ArrayType.prototype[ArrayIndex.get] = getter 124 | ArrayType.prototype[ArrayIndex.set] = setter 125 | 126 | // save down the "fixedLength" if specified. "ref-struct" needs this value 127 | if (fixedLength > 0) { 128 | ArrayType.fixedLength = fixedLength 129 | } 130 | 131 | // keep a reference to the base "type" 132 | ArrayType.type = type 133 | ArrayType.BYTES_PER_ELEMENT = type.indirection == 1 ? type.size : _ref.sizeof.pointer 134 | assert(ArrayType.BYTES_PER_ELEMENT > 0) 135 | 136 | // the ref "type" interface 137 | if (fixedLength > 0) { 138 | // this "type" is probably going in a ref-struct or being used manually 139 | ArrayType.size = ArrayType.BYTES_PER_ELEMENT * fixedLength 140 | ArrayType.alignment = type.alignment 141 | ArrayType.indirection = 1 142 | ArrayType.get = get 143 | ArrayType.set = set 144 | } else { 145 | // this "type" is probably an argument/return value for a node-ffi function 146 | ArrayType.size = _ref.sizeof.pointer 147 | ArrayType.alignment = _ref.alignof.pointer 148 | ArrayType.indirection = 1 149 | ArrayType.get = getRef 150 | ArrayType.set = setRef 151 | } 152 | 153 | // untilZeros() function 154 | ArrayType.untilZeros = untilZeros 155 | 156 | ArrayType.toString = function() { return '[ArrayType]';} 157 | 158 | return ArrayType 159 | } 160 | 161 | /** 162 | * The "get" function of the Array "type" interface. 163 | * Most likely invoked when accessing within a "ref-struct" type. 164 | */ 165 | 166 | function get (buffer, offset) { 167 | debug('Array "type" getter for buffer at offset', offset) 168 | if (offset > 0) { 169 | buffer = buffer.slice(offset) 170 | } 171 | return new this(buffer) 172 | } 173 | 174 | /** 175 | * The "set" function of the Array "type" interface. 176 | * Most likely invoked when setting within a "ref-struct" type. 177 | */ 178 | 179 | function set (buffer, offset, value) { 180 | debug('Array "type" setter for buffer at offset', buffer, offset, value) 181 | var array = this.get(buffer, offset) 182 | var isInstance = value instanceof this 183 | if (isInstance || isArray(value)) { 184 | for (var i = 0; i < value.length; i++) { 185 | array[i] = value[i] 186 | } 187 | } else { 188 | throw new Error('not sure how to set into Array: ' + value) 189 | } 190 | } 191 | 192 | /** 193 | * Reads a pointer from the given offset and returns a new "array" instance of 194 | * this type. 195 | * Most likely invoked when getting an array instance back as a return value from 196 | * an FFI'd function. 197 | */ 198 | 199 | function getRef (buffer, offset) { 200 | debug('Array reference "type" getter for buffer at offset', offset) 201 | return new this(buffer.readPointer(offset)) 202 | } 203 | 204 | /** 205 | * Most likely invoked when passing an array instance as an argument to an FFI'd 206 | * function. 207 | */ 208 | 209 | function setRef (buffer, offset, value) { 210 | debug('Array reference "type" setter for buffer at offset', offset) 211 | var ptr 212 | if (value instanceof this) { 213 | ptr = value.buffer 214 | } else { 215 | ptr = new this(value).buffer 216 | } 217 | _ref.writePointer(buffer, offset, ptr) 218 | } 219 | 220 | /** 221 | * Returns a reference to the backing buffer of this Array instance. 222 | * 223 | * i.e. if the array represents `int[]` (a.k.a. `int *`), 224 | * then the returned Buffer represents `int (*)[]` (a.k.a. `int **`) 225 | */ 226 | 227 | function ref () { 228 | debug('ref()') 229 | var type = this.constructor 230 | var origSize = this.buffer.length 231 | var r = _ref.ref(this.buffer) 232 | r.type = Object.create(_ref.types.CString) 233 | r.type.get = function (buf, offset) { 234 | return new type(_ref.readPointer(buf, offset | 0, origSize)) 235 | } 236 | r.type.set = function () { 237 | assert(0, 'implement!!!') 238 | } 239 | return r 240 | } 241 | 242 | /** 243 | * The "getter" implementation for the "array-index" interface. 244 | */ 245 | 246 | function getter (index) { 247 | debug('getting array[%d]', index) 248 | var size = this.constructor.BYTES_PER_ELEMENT 249 | var baseType = this.constructor.type 250 | var offset = size * index 251 | var end = offset + size 252 | var buffer = this.buffer 253 | if (buffer.length < end) { 254 | debug('reinterpreting buffer from %d to %d', buffer.length, end) 255 | buffer = _ref.reinterpret(buffer, end) 256 | } 257 | return _ref.get(buffer, offset, baseType) 258 | } 259 | 260 | /** 261 | * The "setter" implementation for the "array-index" interface. 262 | */ 263 | 264 | function setter (index, value) { 265 | debug('setting array[%d]', index) 266 | var size = this.constructor.BYTES_PER_ELEMENT 267 | var baseType = this.constructor.type 268 | var offset = size * index 269 | var end = offset + size 270 | var buffer = this.buffer 271 | if (buffer.length < end) { 272 | debug('reinterpreting buffer from %d to %d', buffer.length, end) 273 | buffer = _ref.reinterpret(buffer, end) 274 | } 275 | // TODO: DRY with getter() 276 | 277 | _ref.set(buffer, offset, value, baseType) 278 | return value 279 | } 280 | 281 | /** 282 | * The "slice" implementation. 283 | */ 284 | 285 | function slice (start, end) { 286 | var data 287 | 288 | if (end) { 289 | debug('slicing array from %d to %d', start, end) 290 | data = this.buffer.slice(start*this.constructor.BYTES_PER_ELEMENT, end*this.constructor.BYTES_PER_ELEMENT) 291 | } else { 292 | debug('slicing array from %d', start) 293 | data = this.buffer.slice(start*this.constructor.BYTES_PER_ELEMENT) 294 | } 295 | 296 | return new this.constructor(data) 297 | } 298 | 299 | /** 300 | * Accepts a Buffer instance that should be an already-populated with data for the 301 | * ArrayType. The "length" of the Array is determined by searching through the 302 | * buffer's contents until an aligned NULL pointer is encountered. 303 | * 304 | * @param {Buffer} buffer the null-terminated buffer to convert into an Array 305 | * @api public 306 | */ 307 | 308 | function untilZeros (buffer) { 309 | return new this(_ref.reinterpretUntilZeros(buffer, this.type.size)) 310 | } 311 | --------------------------------------------------------------------------------