UNPKG

@teradataprebuilt/fastcall-linux-node-v59

Version:

fastcall - Very fast, asynchronous, dyncall based foreign function interface library for Node.js

217 lines (178 loc) 5.47 kB
/** * Module dependencies. */ var ref = require('ref') var assert = require('assert') var debug = require('debug')('ref-union') /** * Module exports. */ module.exports = Union /** * The "Union" type constructor. */ function Union () { debug('defining new union "type"') function UnionType (arg, data) { if (!(this instanceof UnionType)) { return new UnionType(arg, data) } debug('creating new union instance') var store if (Buffer.isBuffer(arg)) { debug('using passed-in Buffer instance to back the union', arg) assert(arg.length >= UnionType.size, 'Buffer instance must be at least ' + UnionType.size + ' bytes to back this untion type') store = arg arg = data } else { debug('creating new Buffer instance to back the union (size: %d)', UnionType.size) store = new Buffer(UnionType.size) } // set the backing Buffer store store.type = UnionType this['ref.buffer'] = store // initialise the union with values supplied if (arg) { //TODO: Sanity check - e.g. (Object.keys(arg).length == 1) for (var key in arg) { // hopefully hit the union setters this[key] = arg[key] } } UnionType._instanceCreated = true } // make instances inherit from `proto` UnionType.prototype = Object.create(proto, { constructor: { value: UnionType , enumerable: false , writable: true , configurable: true } }) UnionType.defineProperty = defineProperty UnionType.toString = toString UnionType.fields = {} // comply with ref's "type" interface UnionType.size = 0 UnionType.alignment = 0 UnionType.indirection = 1 UnionType.get = get UnionType.set = set // Read the fields list var arg = arguments[0] if (typeof arg === 'object') { Object.keys(arg).forEach(function (name) { var type = arg[name]; UnionType.defineProperty(name, type); }) } return UnionType } function get (buffer, offset) { debug('Union "type" getter for buffer at offset', buffer, offset) if (offset > 0) { buffer = buffer.slice(offset) } return new this(buffer) } function set (buffer, offset, value) { debug('Union "type" setter for buffer at offset', buffer, offset, value) if (offset > 0) { buffer = buffer.slice(offset) } var union = new this(buffer) var isUnion = value instanceof this if (isUnion) { // TODO: optimize - use Buffer#copy() Object.keys(this.fields).forEach(function (name) { // hopefully hit the setters union[name] = value[name] }) } else { for (var name in value) { // hopefully hit the setters union[name] = value[name] } } } function toString () { return '[UnionType]' } /** * Adds a new field to the union instance with the given name and type. * Note that this function will throw an Error if any instances of the union * type have already been created, therefore this function must be called at the * beginning, before any instances are created. */ function defineProperty (name, type) { debug('defining new union type field', name) // allow string types for convenience type = ref.coerceType(type) assert(!this._instanceCreated, 'an instance of this Union type has already ' + 'been created, cannot add new data members anymore') assert.equal('string', typeof name, 'expected a "string" field name') assert(type && /object|function/i.test(typeof type) && 'size' in type && 'indirection' in type , 'expected a "type" object describing the field type: "' + type + '"') assert(!(name in this.prototype), 'the field "' + name + '" already exists in this Union type') // define the getter/setter property Object.defineProperty(this.prototype, name, { enumerable: true , configurable: true , get: get , set: set }); var field = { type: type } this.fields[name] = field // calculate the new size and alignment recalc(this); function get () { debug('getting "%s" union field (length: %d)', name, type.size) return ref.get(this['ref.buffer'], 0, type) } function set (value) { debug('setting "%s" union field (length: %d)', name, type.size, value) return ref.set(this['ref.buffer'], 0, value, type) } } function recalc (union) { // reset size and alignment union.size = 0 union.alignment = 0 var fieldNames = Object.keys(union.fields) // loop through to set the size of the union of the largest member field // and the alignment to the requirements of the largest member fieldNames.forEach(function (name) { var field = union.fields[name] var type = field.type var size = type.indirection === 1 ? type.size : ref.sizeof.pointer var alignment = type.alignment || ref.alignof.pointer if (type.indirection > 1) { alignment = ref.alignof.pointer } union.alignment = Math.max(union.alignment, alignment) union.size = Math.max(union.size, size) }) // any padding var left = union.size % union.alignment if (left > 0) { debug('additional padding to the end of union:', union.alignment - left) union.size += union.alignment - left } } /** * the base prototype that union type instances will inherit from. */ var proto = {} proto['ref.buffer'] = ref.NULL /** * returns a Buffer pointing to this union data structure. */ proto.ref = function ref () { return this['ref.buffer'] }