@teradataprebuilt/fastcall-linux-node-v59
Version:
fastcall - Very fast, asynchronous, dyncall based foreign function interface library for Node.js
322 lines (287 loc) • 9.27 kB
JavaScript
/**
* Module dependencies.
*/
var _ref = require('./ref');
var assert = require('assert');
var debug = require('debug')('ref:array');
var ArrayIndex = require('./ArrayIndex');
var isArray = Array.isArray;
/**
* The Array "type" constructor.
* The returned constructor's API is highly influenced by the WebGL
* TypedArray API.
*/
module.exports = function Array(_type, _length) {
debug('defining new array "type"');
var type = _ref.coerceType(_type);
var fixedLength = _length | 0;
/**
* This is the ArrayType "constructor" that gets returned.
*/
function ArrayType(data, length) {
if (!(this instanceof ArrayType)) {
return new ArrayType(data, length);
}
debug('creating new array instance');
ArrayIndex.call(this);
var item_size = ArrayType.BYTES_PER_ELEMENT;
if (0 === arguments.length) {
// new IntArray()
// use the "fixedLength" if provided, otherwise throw an Error
if (fixedLength > 0) {
this.length = fixedLength;
this.buffer = new Buffer(this.length * item_size);
} else {
throw new Error('A "length", "array" or "buffer" must be passed as the first argument');
}
} else if ('number' == typeof data) {
// new IntArray(69)
this.length = data;
this.buffer = new Buffer(this.length * item_size);
} else if (isArray(data)) {
// new IntArray([ 1, 2, 3, 4, 5 ], {len})
// use optional "length" if provided, otherwise use "fixedLength, otherwise
// use the Array's .length
var len = 0;
if (null != length) {
len = length;
} else if (fixedLength > 0) {
len = fixedLength;
} else {
len = data.length;
}
if (data.length < len) {
throw new Error('array length must be at least ' + len + ', got ' + data.length);
}
this.length = len;
this.buffer = new Buffer(len * item_size);
for (var i = 0; i < len; i++) {
this[ArrayIndex.set](i, data[i]);
}
} else if (Buffer.isBuffer(data)) {
// new IntArray(Buffer(8))
var len = 0;
if (null != length) {
len = length;
} else if (fixedLength > 0) {
len = fixedLength;
} else {
len = data.length / item_size | 0;
}
var expectedLength = item_size * len;
this.length = len;
if (data.length != expectedLength) {
if (data.length < expectedLength) {
throw new Error('buffer length must be at least ' + expectedLength + ', got ' + data.length);
} else {
debug('resizing buffer from %d to %d', data.length, expectedLength);
data = data.slice(0, expectedLength);
}
}
this.buffer = data;
}
}
// make array instances inherit from our `ArrayIndex.prototype`
ArrayType.prototype = Object.create(ArrayIndex.prototype, {
constructor: {
value: ArrayType,
enumerable: false,
writable: true,
configurable: true
},
// "buffer" is the backing buffer instance
buffer: {
value: _ref.NULL,
enumerable: true,
writable: true,
configurable: true
},
// "node-ffi" calls this when passed an array instance to an ffi'd function
ref: {
value: ref,
enumerable: true,
writable: true,
configurable: true
},
// "slice" implementation
slice: {
value: slice,
enumerable: true,
writable: true,
configurable: true
}
});
// part of the "array-index" interface
ArrayType.prototype[ArrayIndex.get] = getter;
ArrayType.prototype[ArrayIndex.set] = setter;
ArrayType.prototype[ArrayIndex.lengthChanged] = lengthChanged;
// save down the "fixedLength" if specified. "ref-struct" needs this value
if (fixedLength > 0) {
ArrayType.fixedLength = fixedLength;
}
// keep a reference to the base "type"
ArrayType.type = type;
ArrayType.BYTES_PER_ELEMENT = type.indirection == 1 ? type.size : _ref.sizeof.pointer;
assert(ArrayType.BYTES_PER_ELEMENT > 0);
// the ref "type" interface
if (fixedLength > 0) {
// this "type" is probably going in a ref-struct or being used manually
ArrayType.size = ArrayType.BYTES_PER_ELEMENT * fixedLength;
ArrayType.alignment = type.alignment;
ArrayType.indirection = 1;
ArrayType.get = get;
ArrayType.set = set;
} else {
// this "type" is probably an argument/return value for a node-ffi function
ArrayType.size = _ref.sizeof.pointer;
ArrayType.alignment = _ref.alignof.pointer;
ArrayType.indirection = 1;
ArrayType.get = getRef;
ArrayType.set = setRef;
}
// untilZeros() function
ArrayType.untilZeros = untilZeros;
return ArrayType;
};
/**
* The "get" function of the Array "type" interface.
* Most likely invoked when accessing within a "ref-struct" type.
*/
function get(buffer, offset) {
debug('Array "type" getter for buffer at offset', offset);
if (offset > 0) {
buffer = buffer.slice(offset);
}
return new this(buffer);
}
/**
* The "set" function of the Array "type" interface.
* Most likely invoked when setting within a "ref-struct" type.
*/
function set(buffer, offset, value) {
debug('Array "type" setter for buffer at offset', buffer, offset, value);
var array = this.get(buffer, offset);
var isInstance = value instanceof this;
if (isInstance) {
for (var i = 0; i < value.length; i++) {
array[ArrayIndex.set](i, value[ArrayIndex.get](i));
}
} else if (isArray(value)) {
for (var i = 0; i < value.length; i++) {
array[ArrayIndex.set](i, value[i]);
}
} else {
throw new Error('not sure how to set into Array: ' + value);
}
}
/**
* Reads a pointer from the given offset and returns a new "array" instance of
* this type.
* Most likely invoked when getting an array instance back as a return value from
* an FFI'd function.
*/
function getRef(buffer, offset) {
debug('Array reference "type" getter for buffer at offset', offset);
return new this(buffer.readPointer(offset));
}
/**
* Most likely invoked when passing an array instance as an argument to an FFI'd
* function.
*/
function setRef(buffer, offset, value) {
debug('Array reference "type" setter for buffer at offset', offset);
var ptr;
if (value instanceof this) {
ptr = value.buffer;
} else {
ptr = new this(value).buffer;
}
_ref.writePointer(buffer, offset, ptr);
}
/**
* Returns a reference to the backing buffer of this Array instance.
*
* i.e. if the array represents `int[]` (a.k.a. `int *`),
* then the returned Buffer represents `int (*)[]` (a.k.a. `int **`)
*/
function ref() {
debug('ref()');
var type = this.constructor;
var origSize = this.buffer.length;
var r = _ref.ref(this.buffer);
r.type = Object.create(_ref.types.CString);
r.type.get = function (buf, offset) {
return new type(_ref.readPointer(buf, offset | 0, origSize));
};
r.type.set = function () {
assert(0, 'implement!!!');
};
return r;
}
/**
* The "getter" implementation for the "array-index" interface.
*/
function getter(index) {
debug('getting array[%d]', index);
var size = this.constructor.BYTES_PER_ELEMENT;
var baseType = this.constructor.type;
var offset = size * index;
var end = offset + size;
var buffer = this.buffer;
if (buffer.length < end) {
debug('reinterpreting buffer from %d to %d', buffer.length, end);
buffer = _ref.reinterpret(buffer, end);
this._length = end / size;
}
return _ref.get(buffer, offset, baseType);
}
/**
* The "setter" implementation for the "array-index" interface.
*/
function setter(index, value) {
debug('setting array[%d]', index);
var size = this.constructor.BYTES_PER_ELEMENT;
var baseType = this.constructor.type;
var offset = size * index;
var end = offset + size;
var buffer = this.buffer;
if (buffer.length < end) {
debug('reinterpreting buffer from %d to %d', buffer.length, end);
buffer = _ref.reinterpret(buffer, end);
}
// TODO: DRY with getter()
_ref.set(buffer, offset, value, baseType);
return value;
}
function lengthChanged(newLength) {
if (this.buffer && !_ref.isNull(this.buffer) && this.buffer.length < newLength) {
debug('reinterpreting buffer from %d to %d', this.buffer.length, newLength);
this.buffer = _ref.reinterpret(this.buffer, newLength);
}
}
/**
* The "slice" implementation.
*/
function slice(start, end) {
var data;
if (end) {
debug('slicing array from %d to %d', start, end);
data = this.buffer.slice(start * this.constructor.BYTES_PER_ELEMENT, end * this.constructor.BYTES_PER_ELEMENT);
} else {
debug('slicing array from %d', start);
data = this.buffer.slice(start * this.constructor.BYTES_PER_ELEMENT);
}
return new this.constructor(data);
}
/**
* Accepts a Buffer instance that should be an already-populated with data for the
* ArrayType. The "length" of the Array is determined by searching through the
* buffer's contents until an aligned NULL pointer is encountered.
*
* @param {Buffer} buffer the null-terminated buffer to convert into an Array
* @api public
*/
function untilZeros(buffer) {
return new this(_ref.reinterpretUntilZeros(buffer, this.type.size));
}
//# sourceMappingURL=array.js.map