UNPKG

reified

Version:

JS Binary Data API. Structs, arrays, bitfields, and numbers. Reify and Reference like nobody's business.

1,851 lines (1,532 loc) 52.3 kB
var reified = function(global, imports){ !function(module, require){ imports['./utility'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./utility'] }, set: function(v){ imports['./utility'] = v } }); "use strict"; module.exports = { isObject: isObject, bytes: bytes, bits: bits, indent: indent, pad: pad, maxLength: maxLength, unique: unique, strlen: strlen }; function isObject(o){ return Object(o) === o } function bits(n){ return Math.log(n) / Math.LN2 } function bytes(n){ return ((bits(n) / 8) | 0) + 1 } function indent(str, amount){ var space = Array((amount||2)+1).join(' '); return str.split('\n').map(function(line){ return space+line }).join('\n'); } function pad(str, len){ len -= strlen(str||'') + 1; return str + Array(len > 1 ? len : 1).join(' '); } function strlen(str){ return str.replace(/\033\[(?:\d+;)*\d+m/g, '').length; } function maxLength(array){ if (!Array.isArray(array)) { if (!isObject(array)) throw new TypeError('Max length called on non-object ' + array); array = Object.keys(array); } return array.reduce(function(max, item){ return Math.max(max, strlen(''+item)) }, 0); } function unique(a){ return Object.keys(a.reduce(function(r,s){ return r[s]=1,r },{})); } }({}, function(n){ return imports[n] }); !function(module, require){ imports['./buffer'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./buffer'] }, set: function(v){ imports['./buffer'] = v } }); "use strict"; module.exports = DataBuffer; var types = ['Int8', 'Int16', 'Int32', 'Uint8', 'Uint16', 'Uint32', 'Float32', 'Float64']; // Basic stand-in for Buffer in browsers that defers to ArrayBuffer var Buffer = function(global){ if ('Buffer' in global) return global.Buffer; function Buffer(subject, offset, length){ return new ArrayBuffer(subject, offset, length); } Buffer.isBuffer = function isBuffer(o){ return o instanceof ArrayBuffer; } return Buffer; }(Function('return this')()); var ArrayBuffers = { ArrayBuffer: ArrayBuffer }; function isArrayBuffer(o){ return o instanceof ArrayBuffer || !!(o && o.constructor && o.constructor.name in ArrayBuffers); } function DataBuffer(subject, offset, length){ if (!DataBuffer.prototype.isPrototypeOf(this)) return new DataBuffer(subject, offset, length); if (!subject) throw new Error('Tried to initialize with no usable length or subject'); if (isArrayBuffer(subject)) { this.array = subject; } if (subject) { if (subject.buffer) { offset = (subject.offset || subject.byteOffset || 0) + (offset || 0); while (subject.buffer) subject = subject.buffer; } if (typeof offset === 'undefined') { offset = subject.offset || subject.byteOffset; } if (typeof length === 'undefined') { length = subject.length || subject.byteLength; } } if (typeof subject === 'number') { this.view = new DataView(new Buffer(subject)); } else if (Buffer.isBuffer(subject)) { this.view = new DataView(subject, offset, length); } else if (subject instanceof DataView) { this.view = new DataView(subject.buffer, offset, length); } else if (DataBuffer.isDataBuffer(subject)) { this.view = new DataView(subject.buffer, subject.offset + offset, length || subject.length); } this.length = this.view.byteLength; this.buffer = this.view.buffer; this.offset = this.view.byteOffset; } DataBuffer.isBuffer = Buffer.isBuffer; DataBuffer.isDataBuffer = function isDataBuffer(o){ return DataBuffer.prototype.isPrototypeOf(o) } function toNum(n){ return isFinite(n) ? +n : 0 } function toNumOrUndef(n){ if (isFinite(n)) return +n } function toUint8(x) { return (x >>> 0) & 0xff } DataBuffer.prototype = { constructor: DataBuffer, endianness: 'LE', subarray: function(start, end){ start = toNum(start); end = toNumOrUndef(end); return new DataBuffer(this.view, start, end); }, typed: function(type, offset, length){ type = ArrayBuffers[type+'Array']; offset = toNum(offset); length = toNum(length) || this.length / type.BYTES_PER_ELEMENT | 0; return new type(this.view, offset, length) }, copy: function(target, targetOffset, start, end){ if (isFinite(target)) { end = start, start = targetOffset, targetOffset = target; target = null; } targetOffset = toNum(targetOffset); start = toNum(start); end = end ? +end : this.length - 1; if (start > end) throw new Error('End less than start'); if (start < 0) throw new RangeError('Start less than zero'); if (end >= this.length) throw new RangeError('End greater than length'); var length = end - start; if (!target) { target = new Buffer(length); } else if (targetOffset + length > target.length) { length = target.length; } target = new DataBuffer(target, targetOffset, length).typed('Uint8'); var source = this.subarray(start, end).typed('Uint8'); for (var i=0; i<length; i++) { target[i] = source[i]; } return target; }, clone: function(){ var buffer = new DataBuffer(new Buffer(this.length)); for (var i=0; i < this.length; i++) { buffer.writeUint8(i, this.readUint8(i)); } return buffer; }, fill: function(v){ v = toNum(v); var buff = this.typed('Uint8'); for (var i=0; i < this.length; i++) { buff[i] = v; } }, write: function(source, offset, length){ length = isFinite(length) ? +length : source.length; offset = isFinite(offset) ? +offset : 0; length = Math.min(this.length, length+offset, source.length); var target = this.subarray(offset, offset.length).typed('Uint8'); for (var i=0; i<length; i++) { target[i] = source[i]; } return this; }, map: function(){ return [].map.apply(this.typed('Uint8'), arguments); }, slice: function(start, end, encoding){ return this.subarray(start, end).toString(encoding || 'ascii'); }, toArray: function(type){ return [].map.call(this.typed(type || 'Uint8'), function(x){ return x }); }, toString: function(encoding){ switch (encoding) { case 'ascii': return this.map(function(val){ return String.fromCharCode(val); }).join(''); default: return this.map(function(v){ return ('000'+v.toString(10)).slice(-3) }) .join(' ') .split(/((?:\d\d\d ?){10}(?: ))/) .filter(Boolean) .map(Function.call.bind(''.trim)) .join('\n') } } } types.forEach(function(type){ ArrayBuffers[type+'Array'] = global[type+'Array']; DataBuffer.prototype['read'+type] = function(offset){ return this.view['get'+type](toNum(offset), this.endianness === 'LE'); } DataBuffer.prototype['write'+type] = function(offset, value){ return this.view['set'+type](toNum(offset), toNum(value), this.endianness === 'LE'); } }); Array.apply(null, Array(20)).forEach(function(n, index){ Object.defineProperty(DataBuffer.prototype, index, { configurable: true, get: function(){ return this.readUint8(index) }, set: function(v){ return this.writeUint8(index, v) } }) }); }({}, function(n){ return imports[n] }); !function(module, require){ imports['./genesis'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./genesis'] }, set: function(v){ imports['./genesis'] = v } }); "use strict"; var utility = require('./utility'); var isObject = utility.isObject; var hasProto = !!Function.__proto__; var types = {}; var exp = exports = module.exports = { Type: Type, Subtype: Subtype, OpaqueType: OpaqueType, lookupType: lookupType, registerType: registerType, DataBuffer: require('./buffer'), types: types, isBuffer: isBuffer, api: api, nullable: function(object, key){ Object.defineProperty(object, key, nullable); delete object[key]; } }; var nullable = { value: undefined, writable: true, configurable: true }; var hidden = { configurable: true, writable: true, value: 0 }; function isBuffer(o){ return exp.DataBuffer.isBuffer(o) || exp.DataBuffer.isDataBuffer(o); } function api(o, n, v){ if (Object(n) === n) { Object.keys(n).forEach(function(k){ api(o, k, n[k]); }); } else { hidden.value = v; Object.defineProperty(o, n, hidden); } } function registerType(name, type){ if (name in types) return types[name]; if (name.length) return types[name] = type; return type; } function lookupType(name, label){ if (typeof name !== 'string') { return name; } // pointer type if (name[0] === '*') { name = name.slice(1); var type = lookupType(name); if (typeof type !== 'string') { if (typeof label === 'string') { name = label; } return createType('pointer', name, type); } } if (name[name.length-1] === ']') { var count = name.match(/(.*)\[(\d+)\]$/); if (!count) { return name in types ? types[name] : name; } name = count[1]; count = +count[2]; if (name === 'Char') { return new superTypes['CharArray'](count); } if (typeof label === 'string') { return createType('array', label, lookupType(name), count); } else { if (type === 'Char') { return createType('string', count); } var type = lookupType(name); if (type === name) { return createType('array', name, count); } else { name = type.name + 'x' + count; return createType('array', name, type, count); } } } return name in types ? types[name] : name; } function createType(name, a, b, c){ var type = require('./'+name); return new type(a, b, c); } // ######################## // ### Genesis for Type ### // ######################## function Type(ctor, proto){ ctor.prototype = Super(eval('(function Empty'+ctor.name.replace(/Type$/,'T')+'(){})'), Type); ctor.prototype.Type = ctor.name; ctor.prototype.prototype = copy(proto, Object.create(Data)); types[ctor.name.replace(/Type$/,'')] = ctor; inspectors(ctor.prototype, ctor.name); } function Super(ctor, superctor, proto){ if (hasProto) { ctor.__proto__ = superctor; } else { copy(superctor, ctor); } ctor.prototype = proto || Object.create(superctor.prototype); ctor.prototype.constructor = ctor; //ctor.prototype.super = superctor; return ctor; } function inspectors(obj, name){ if (typeof imports === 'undefined') { obj.inspect = require('./inspect')('Type', name); obj.prototype.inspect = require('./inspect')('Data', name); } } copy({ Class: 'Type', //toString: function toString(){ return '[object '+this.name+'Type]' }, isInstance: function isInstance(o){ return this.prototype.isPrototypeOf(o) }, array: function array(n){ return createType('array', this, n) }, typeDef: function typeDef(name){ var iface = createInterface(name, ifaceMap.get(this)); return Super(iface, this); }, reifier: function reifier(handler){ var oldReifier = this.prototype.reify; if (!handler) { return oldReifier; } else { this.prototype.reify = function reify(){ var self = this; return handler.call(this, function(){ return oldReifier.call(self) }); } } } }, Type); Array.apply(null, Array(20)).forEach(function(n, i){ Object.defineProperty(Type, i, { configurable: true, get: function(){ return this.array(i) } }); }); Object.defineProperty(Type, 'ptr', { configurable: true, get: function(){ return createType('pointer', this.displayName, this) } }); var ifaceMap = function(){ var ifaces = []; var ctors = []; return { set: function(iface, ctor){ ifaces.push(iface); ctors.push(ctor); return iface; }, get: function(iface){ var index = ifaces.indexOf(iface); return ~index ? ctors[index] : null; } } }(); function createInterface(name, ctor, type){ var fnName = name.replace(/[^\w0-9_$]/g, ''); if (name[0] === '*') { var count = name.match(/^[*]+/)[0].length; fnName = Array(count + 1).join('Ptr_') + fnName; } var src = 'return function '+fnName+'(data, offset, values){ return Ctor.call(Object.create('+fnName+'.prototype), data, offset, values) }'; var iface = Function('Ctor', src)(ctor); ifaceMap.set(iface, ctor); if (type) { if (hasProto) iface.__proto__ = type.prototype; else copy(type.prototype, iface); } iface.prototype = ctor.prototype; api(iface, 'displayName', name); if (name) registerType(name, iface); return copy(ctor, iface); } function Subtype(name, bytes, ctor){ ctor.bytes = bytes; ctor.prototype.bytes = bytes; ctor.prototype = copy(ctor.prototype, Object.create(this.prototype.prototype)); return ctor.prototype.constructor = createInterface(name, ctor, this); } // ######################## // ### Genesis for Data ### // ######################## var Data = Type.prototype = { //__proto__: EventEmitter.prototype, Class: 'Data', toString: function toString(){ return '[object '+this.constructor.displayName+']' }, rebase: function rebase(data){ if (data == null) { data = new exp.DataBuffer(this.bytes); data.fill(0); } else if (data._data) { data = new exp.DataBuffer(data._data); } else { data = new exp.DataBuffer(data); } api(this, '_data', data); }, realign: function realign(offset){ this._offset = +offset || 0; }, clone: function clone(){ return new this.constructor(this._data, this._offset); }, copy: function copy(data, offset){ return new this.constructor(this._data.clone()); }, cast: function cast(type, align){ if (typeof (type = lookupType(type)) === 'string') throw new TypeError('Unknown type "'+type+'"'); if (this instanceof Opaque) { return new type(this._data, this._offset); } if (type.bytes < this.bytes) throw new RangeError('Tried to cast to a smaller size "'+type.name+'"'); if (this._data.length < type.bytes) throw new RangeError('Type is bigger than this buffer: "'+type.name+'"'); align = (type.bytes === this.bytes || !align) ? 0 : align < 0 ? this.bytes-type.bytes : +align; return new type(this._data, this._offset + align); }, pointer: function pointer(){ var PtrType = lookupType('*'+this.constructor.displayName); return new PtrType(this); } }; function Opaque(data, offset, size){ if (isFinite(data)) { size = data; data = null; if (!isFinite(size)) { throw new Error('Opaque types must be given a size or buffer'); } } this.bytes = size || 0; this.rebase(data); api(this, '_offset', +offset || 0); return this; } Super(Opaque, Type); ifaceMap.set(Opaque, Opaque); registerType('Opaque', Opaque); inspectors(Opaque, 'Opaque'); api(Opaque, 'displayName', 'Opaque'); Opaque.bytes = Opaque.prototype.bytes = 0; Opaque.prototype.DataType = 'opaque'; Opaque.prototype.reify = function reify(){ //return this._data; return null; } Opaque.prototype.write = function write(){ throw new Error('Opaque data must be cast to a specific type before it can be written to'); } Opaque.prototype.rebase = function rebase(buffer){ if (buffer != null) { Data.rebase.call(this, buffer); } } function OpaqueType(size){ function OpaqueT(data, offset){ Opaque.call(this, data, offset, size); } Super(OpaqueT, Opaque); OpaqueT.bytes = size; return OpaqueT; } function copy(from, to, hidden){ Object[hidden ? 'getOwnPropertyNames' : 'keys'](from).forEach(function(key){ var desc = Object.getOwnPropertyDescriptor(from, key); desc.enumerable = false; Object.defineProperty(to, key, desc); }); return to; } }({}, function(n){ return imports[n] }); !function(module, require){ imports['./numeric'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./numeric'] }, set: function(v){ imports['./numeric'] = v } }); "use strict"; var bits = require('./utility').bits; var genesis = require('./genesis'); var NumericSubtype = genesis.Subtype.bind(NumericType); module.exports = NumericType; var types = { Int8: 1, Uint8: 1, Int16: 2, Uint16: 2, Int32: 4, Uint32: 4, Float32: 4, Float64: 8 }; /** * Coerce to number when appropriate and verify number against type storage */ function checkType(type, val){ if (val && val.DataType) { if (val.DataType === 'numeric' && val.Subtype === 'Int64' || val.Subtype === 'Uint64') { if (type === 'Int64' || type === 'Uint64') { return val._data; } else { throw new RangeError(val + ' exceeds '+type+' capacity'); } } else if (val.DataType === 'array' || val.DataType === 'struct') { if (val.bytes > types[type][0]) { throw new RangeError(val + ' exceeds '+type+' capacity'); } else { val = val.reify(); } } else { val = val.reify(); } } if (!val) val = 0; if (typeof val === 'undefined') val = 0; if (isFinite(val)) { val = +val; } else { throw new TypeError('Invalid value for ' + type + ': ' + val.DataType); } if (val && bits(val) / 8 > types[type][0]) { throw new RangeError(val + ' exceeds '+type+' capacity'); } return val; } // ############################### // ### NumericType Constructor ### // ############################### function NumericType(name, bytes){ // ############################ // ### NumericT Constructor ### // ############################ function NumericT(data, offset, value){ if (typeof data === 'number' || !data) { value = data; data = null; } this.rebase(data); genesis.api(this, '_offset', +offset || 0); if (value != null) { this.write(value); } //this.emit('construct'); return this; } // ##################### // ### NumericT Data ### // ##################### NumericT.prototype = { Subtype: name, }; return NumericSubtype(name, bytes, NumericT); } // ######################## // ### NumericType Data ### // ######################## function reify(deallocate){ return this._data['read'+this.Subtype](this._offset); } function write(v){ this._data['write'+this.Subtype](this._offset, checkType(this.Subtype, v)); return this; } genesis.Type(NumericType, { DataType: 'numeric', fill: function fill(v){ write.call(this, v || 0); }, reify: reify, write: write, valueOf: function valueOf(){ return reify.call(this); }, toString: function toString(){ return String(reify.call(this)); }, }); Object.keys(types).forEach(function(name){ NumericType[name] = new NumericType(name, types[name]); }); }({}, function(n){ return imports[n] }); !function(module, require){ imports['./struct'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./struct'] }, set: function(v){ imports['./struct'] = v } }); "use strict"; var isObject = require('./utility').isObject; var genesis = require('./genesis'); var StructSubtype = genesis.Subtype.bind(StructType); module.exports = StructType; // ############################## // ### StructType Constructor ### // ############################## function StructType(name, fields){ if (!fields) { fields = name; name = ''; } var bytes = 0; var offsets = {}; var keys = []; fields = Object.keys(fields).reduce(function(ret, name){ ret[name] = genesis.lookupType(fields[name]); keys.push(name); offsets[name] = bytes; bytes += ret[name].bytes; return ret; }, {}); // ########################### // ### StructT Constructor ### // ########################### function StructT(data, offset, values){ if (!genesis.isBuffer(data)) { values = data; data = null; } this.rebase(data); genesis.api(this, '_offset', +offset || 0); if (values) { Object.keys(values).forEach(function(field){ if (!field in fields) throw new Error('Invalid field "'+field+'"'); field in fields && initField(this, StructT, field).write(values[field]); }, this); } return this; //this.emit('construct'); } StructT.fields = fields; StructT.offsets = offsets; StructT.keys = keys; return defineFields(StructSubtype(name, bytes, StructT)); } function initField(target, ctor, field){ var block = new ctor.fields[field](target._data, target._offset + ctor.offsets[field]) ; Object.defineProperty(target, field, { enumerable: true, configurable: true, get: function(){ return block }, set: function(v){ if (v === null) { //this.emit('deallocate', field); genesis.nullable(this, field); block = null; } else { block.write(v); } } }); return block; } function defineFields(target){ target.keys.forEach(function(field){ Object.defineProperty(target.prototype, field, { enumerable: true, configurable: true, get: function(){ return initField(this, target, field) }, set: function(v){ initField(this, target, field).write(v) } }); }); return target; } // ####################### // ### StructType Data ### // ####################### genesis.Type(StructType, { DataType: 'struct', reify: function reify(deallocate){ this.reified = this.constructor.keys.reduce(function(ret, field){ ret[field] = this[field] == null ? initField(this, this.constructor, field).reify(deallocate) : this[field].reify(deallocate); if (deallocate) this[field] = null; return ret; }.bind(this), {}); //this.emit('reify', this.reified); var val = this.reified; delete this.reified; return val; }, write: function write(o){ if (isObject(o)) { if (o.reify) o = o.reify(); Object.keys(o).forEach(function(field, current){ current = o[field]; if (current != null) { this[field] = current.reify ? current.reify() : current; } else if (current === null) { this[field] = null; } }, this); } }, realign: function realign(offset, deallocate){ this._offset = offset = +offset || 0; // use realiagn as a chance to DEALLOCATE since everything is being reset essentially Object.keys(this).forEach(function(field){ if (deallocate) this[field] = null; else this[field].realign(offset); }, this); }, fill: function fill(val){ val = val || 0; this.constructor.keys.forEach(function(field){ this[field] = val; }, this); }, }); }({}, function(n){ return imports[n] }); !function(module, require){ imports['./array'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./array'] }, set: function(v){ imports['./array'] = v } }); "use strict"; var genesis = require('./genesis'); var ArraySubtype = genesis.Subtype.bind(ArrayType); module.exports = ArrayType; // ############################# // ### ArrayType Constructor ### // ############################# function ArrayType(name, MemberType, length) { if (typeof name !== 'string' || typeof MemberType === 'number') { length = MemberType || 0; MemberType = genesis.lookupType(name); name = MemberType.name + 'x'+length; } else { MemberType = genesis.lookupType(MemberType); } if (genesis.lookupType(name) !== name) return genesis.lookupType(name); var bytes = MemberType.bytes * length; // ########################## // ### ArrayT Constructor ### // ########################## function ArrayT(data, offset, values){ if (!genesis.isBuffer(data)) { values = data; data = null; } this.rebase(data); genesis.api(this, '_offset', offset || 0); values && Object.keys(values).forEach(function(i){ initIndex(this, this.constructor.memberType, i).write(values[i]); }, this); return this; //this.emit('construct'); } ArrayT.memberType = MemberType; ArrayT.keys = Array.apply(null, Array(length)).map(Function.call.bind(String)); ArrayT.count = length; ArrayT.prototype.length = length; return defineIndices(ArraySubtype(name, bytes, ArrayT)); } function initIndex(target, MemberType, index){ var block = new MemberType(target._data, target._offset + index * MemberType.bytes); Object.defineProperty(target, index, { enumerable: true, configurable: true, get: function(){ return block }, set: function(v){ if (v === null) { //this.emit('deallocate', index); genesis.nullable(this, index); block = null; } else { block.write(v) } } }); return block; } function defineIndices(target){ Array.apply(null, Array(target.count)).forEach(function(n, index){ Object.defineProperty(target.prototype, index, { enumerable: true, configurable: true, get: function(){ return initIndex(this, target.memberType, index) }, set: function(v){ initIndex(this, target.memberType, index).write(v) } }); }); return target; } // ###################### // ### ArrayType Data ### // ###################### genesis.Type(ArrayType, { DataType: 'array', forEach: Array.prototype.forEach, reduce: Array.prototype.reduce, map: Array.prototype.map, join: Array.prototype.join, reify: function reify(deallocate){ this.reified = []; for (var i=0; i < this.length; i++) { this.reified[i] = this[i].reify(deallocate); if (deallocate) this[i] = null; } //this.emit('reify', this.reified); var output = this.reified; delete this.reified; return output; }, write: function write(value, index, offset){ if (value == null) throw new TypeError('Tried to write nothing'); if (isFinite(index) && typeof offset === 'undefined' && !value.length) { // writing to specific index return this[index] = value; } index = +index || 0; offset = +offset || 0; // reify if needed, direct buffer copying doesn't happen here value = value.reify ? value.reify() : value; if (value && value.length) { // arrayish and offset + index are already good to go for (var index; index < this.length && index+offset < value.length; index++) { if (value[offset+index] === null) { this[index] = null; } else { this[index] = value[offset+index]; } } } else { // last ditch, something will throw an error if this isn't an acceptable type this[index] = offset ? value[offset] : value; } }, fill: function fill(val){ val = val || 0; for (var i=0; i < this.length; i++) { this[i] = val; } }, realign: function realign(offset, deallocate){ this._offset = offset = +offset || 0; // use realiagn as a chance to deallocate since everything is being reset essentially Object.keys(this).forEach(function(i){ if (isFinite(i)) { if (deallocate) this[i] = null; else this[i].realign(offset); } }, this); }, }); }({}, function(n){ return imports[n] }); !function(module, require){ imports['./bitfield'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./bitfield'] }, set: function(v){ imports['./bitfield'] = v } }); "use strict"; var utility = require('./utility'); var genesis = require('./genesis'); var bytesFor = utility.bytes; var bits = utility.bits; var BitfieldSubtype = genesis.Subtype.bind(BitfieldType); var powers = Array.apply(null, Array(32)).map(Function.call.bind(Number)).map(Math.pow.bind(null, 2)); module.exports = BitfieldType; // ################################ // ### BitfieldType Constructor ### // ################################ function BitfieldType(name, flags, bytes){ if (typeof name !== 'string') { bytes = flags; flags = name; name = ''; } if (typeof flags === 'number') { bytes = flags; flags = []; } if (Array.isArray(flags)) { flags = flags.reduce(function(ret, name, index){ ret[name] = 1 << index; return ret; }, {}); } if (!(bytes > 0)) { bytes = bytesFor(max(flags)) ; } // ############################# // ### BitfieldT Constructor ### // ############################# function BitfieldT(data, offset, values) { if (!genesis.isBuffer(data)) { values = data || 0; data = null; } this.rebase(data); genesis.api(this, '_offset', +offset || 0); if (Array.isArray(values)) { values.forEach(function(flag){ this[flag] = true }, this); } else if (typeof values === 'number') { this.write(values); } else if (Object(values) === values){ Object.keys(values).forEach(function(key){ this[key] = values[key] }, this); } return this; //this.emit('construct'); }; BitfieldT.keys = flags; // ###################### // ### BitfieldT Data ### // ###################### BitfieldT.prototype = { flags: flags, length: bytes * 8, toString: function toString(){ return this === BitfieldT.prototype ? '[object '+name+']' : this.map(function(v){ return +v }).join(''); } }; var out = BitfieldSubtype(name, bytes, BitfieldT); return defineFlags(out); } function defineFlags(target) { var largest = 0; Object.keys(target.keys).forEach(function(flag){ var val = target.keys[flag]; largest = Math.max(largest, val); Object.defineProperty(target.prototype, flag, { configurable: true, enumerable: true, get: function( ){ return (this.read() & val) > 0 }, set: function(v){ this.write(v ? this.read() | val : this.read() & ~val) } }) }); Array.apply(null, Array(target.bytes * 8)).forEach(function(n, i){ var power = powers[i]; if (power > largest) return; Object.defineProperty(target.prototype, i, { configurable: true, enumerable: true, get: function( ){ return (this.read() & power) > 0 }, set: function(v){ this.write(v ? this.read() | power : this.read() & ~power) } }); }); return target; } // ######################### // ### BitfieldType Data ### // ######################### genesis.Type(BitfieldType, { DataType: 'bitfield', forEach: Array.prototype.forEach, reduce: Array.prototype.reduce, map: Array.prototype.map, get: function get(i){ return (this.read() & powers[i]) > 0; }, set: function get(i){ this.write(this.read() | powers[i]); return this; }, unset: function unset(i){ this.write(this.read() & ~powers[i]); return this; }, write: function write(v){ this._data['writeUint'+this.bytes*8](this._offset, v); return this; }, read: function read(){ return this._data['readUint'+this.bytes*8](this._offset); }, reify: function reify(deallocate){ var flags = Object.keys(this.flags); if (flags.length) { var val = this.reified = flags.reduce(function(ret, flag, i){ if (this[flag]) ret.push(flag); return ret; }.bind(this), []); } else { var val = this.map(function(v){ return v }); } //this.emit('reify', val); val = this.reified; delete this.reified; if (deallocate) { //this.emit('deallocate'); delete this._data; delete this._offset; } return val; } }); function max(arr){ if (Array.isArray(arr)) return arr.reduce(function(r,s){ return Math.max(s, r) }, 0); else return Object.keys(arr).reduce(function(r,s){ return Math.max(arr[s], r) }, 0); } }({}, function(n){ return imports[n] }); !function(module, require){ imports['./pointer'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./pointer'] }, set: function(v){ imports['./pointer'] = v } }); "use strict"; var genesis = require('./genesis'); var numeric = require('./numeric'); var PointerSubtype = genesis.Subtype.bind(PointerType); module.exports = PointerType; var Address = numeric.Uint32.typeDef('Address'); // ############################### // ### PointerType Constructor ### // ############################### function PointerType(name, pointeeType, addressType){ if (typeof name !== 'string') { addressType = pointeeType; pointeeType = name; name = pointeeType.displayName; } if (typeof pointeeType === 'string') { pointeeType = genesis.lookupType(pointeeType); } else if (typeof pointeeType === 'undefined') { pointeeType = genesis.lookupType(name); } if (typeof addressType === 'string') { addressType = genesis.lookupType(addressType); } else if (typeof addressType === 'undefined') { addressType = Address; } name = '*'+name; // ############################ // ### PointerT Constructor ### // ############################ function PointerT(data, offset, values){ if (!genesis.isBuffer(data)) { values = data; data = null; } this.rebase(data); genesis.api(this, '_offset', +offset || 0); this.address = new PointerT.addressType(this._data, this._offset); if (typeof values === 'number') { this.memory = this._data; this.address.write(values); } else if (values && values._data) { this.pointTo(values); } else { this.memory = this._data; } return this; //this.emit('construct'); } PointerT.pointeeType = pointeeType; PointerT.addressType = addressType; Object.defineProperty(PointerT.prototype, 'pointee', { enumerable: true, configurable: true, get: function( ){ return initPointee(this, pointeeType) }, set: function(v){ initPointee(this, pointeeType, v) } }); return PointerSubtype(name, addressType.bytes, PointerT); } function initPointee(target, Type, pointee){ var address; if (!pointee) { if (target.memory) { pointee = new Type(target.memory, address = target.address.reify()); } else { pointee = new Type; target.memory = pointee._data; target.address.write(address = pointee._offset); } } else { address = target.address.reify(); pointee = new Type(target.memory); } Object.defineProperty(target, 'pointee', { enumerable: true, configurable: true, get: function(){ var newAddr = target.address.reify(); if (newAddr !== address) { pointee.realign(newAddr); address = newAddr; } return pointee; }, set: function(v){ var newAddr = target.address.reify(); if (newAddr !== address) { pointee.realign(newAddr); address = newAddr; } pointee.write(v); } }); return pointee; } // ######################## // ### PointerType Data ### // ######################## genesis.Type(PointerType, { DataType: 'pointer', get bytes(){ return this.constructor.addressType.bytes; }, reify: function reify(deallocate){ return this.pointee.reify(deallocate); }, write: function write(o){ this.pointee.write(o); }, fill: function fill(val){ this.pointee.fill(val); }, pointTo: function pointTo(data){ if (!data._data) throw new TypeError('Must point to reified <Data>'); this.pointee = data; this.address.write(data._offset); this.memory = data._data; }, cast: function cast(type){ if (typeof type === 'string') { type = genesis.lookupType(type); } genesis.nullable(this, 'pointee'); this.__proto__ = type.ptr.prototype; return this; } }); Object.defineProperty(PointerType.prototype, 'bytes', { configurable: true, enumerable: true, get: function(){ return this.addressType.bytes; } }); }({}, function(n){ return imports[n] }); !function(module, require){ imports['./string'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./string'] }, set: function(v){ imports['./string'] = v } }); "use strict"; var genesis = require('./genesis'); var numeric = require('./numeric'); var ArrayType = require('./array'); var CharSubtype = genesis.Subtype.bind(CharType); module.exports = CharType; var char = String.fromCharCode; var code = Function.call.bind(''.charCodeAt); var ucs2 = function(){ var a = 0x3ff, o = 0x10000, x = o - 0x800, y = o - 0x2400, z = o - 0x2800; return function ucs2(v) { if (typeof v === 'string') { var r = code(v, 0); return r & x === z ? o + ((r & a) << 10) + (code(v, 1) & a) : r; } else if (isFinite(v)) { return v >= o ? char((v -= o) >>> 10 & a | z) + char(y | v & a) : char(v); } } }(); var cache = []; function CharType(data, offset, value){ var length; if (typeof data === 'number') { length = data; data = null; } else if (typeof data === 'string') { length = data.length; value = data; data = null; } else if (data.bytes || data.byteLength) { length = data.bytes || data.byteLength; } var bytes = length; if (!(bytes in cache)) { var CharArray = cache[bytes] = CharSubtype('CharArray'+bytes, bytes, function CharArray(data, offset, value){ if (!data) data = new genesis.DataBuffer(this.bytes); this.rebase(data); genesis.api(this, '_offset', offset || 0); value && this.write(value); //this.emit('construct'); return this; }); CharArray.bytes = bytes; CharArray.prototype.bytes = bytes; CharArray.prototype.length = bytes; } else { var CharArray = cache[bytes]; } if (data || value) { if (!data) data = new genesis.DataBuffer(bytes || value); return new CharArray(data, offset, value); } else { return CharArray; } } genesis.Type(CharType, { DataType: 'string', Subtype: 'CharArray', bits: 8, join: Array.prototype.join, map: Array.prototype.map, fill: function fill(v){ this.write(0, v || 0) }, reify: function reify(){ return this._data.subarray(this._offset, this.bytes).map(function(str){ return ucs2(str); }).join(''); }, write: function write(value, index){ var isString = typeof value === 'string'; if (isFinite(index)) { if (isString) value = ucs2(value); this._data['writeUint'+this.bits](index, value); } else if (typeof value === 'string' || value && 'length' in value) { var bytesPer = this.bits / 8; for (var i=0; i<value.length && i<this.length; i++) { this._data['writeUint'+this.bits](this._offset+i*bytesPer, ucs2(value[i])); } } }, }); function Char(data, offset, value){ if (typeof data === 'string' || typeof data === 'number' || !data) { value = data; data = null; } this.rebase(data); genesis.api(this, '_offset', +offset || 0); if (value != null) { this.write(value); } return this; //this.emit('construct'); } Char.prototype = { length: 1, Subtype: 'CharArray', bytes: 1, write: function write(v, i){ this._data['writeUint'+this.bits](this._offset, typeof v === 'string' ? ucs2(v[i || 0]) : v); return this; }, reify: function reify(deallocate){ var val = this.reified = ucs2(this._data['readUint'+this.bits](this._offset, 1)); //this.emit('reify', val); val = this.reified; delete this.reified; return val; }, toNumber: function toNumber(v){ return this._data['readUint'+this.bits](this._offset); } }; Char.__proto__ = CharType.prototype; Char.constructor = CharType; Char.prototype.__proto__ = CharType.prototype.prototype; Char.bytes = Char.prototype.bytes = Char.prototype.length = 1 Char.prototype.constructor = Char; cache[1] = Char; }({}, function(n){ return imports[n] }); !function(module, require){ imports['./index'] = {}; Object.defineProperty(module, 'exports', { get: function(){ return imports['./index'] }, set: function(v){ imports['./index'] = v } }); "use strict"; var genesis = require('./genesis'); var NumericType = require('./numeric'); var StructType = require('./struct'); var ArrayType = require('./array'); var BitfieldType = require('./bitfield'); var CharType = require('./string'); var PointerType = require('./pointer'); var OpaqueType = genesis.OpaqueType; module.exports = reified; function reified(type, subject, size, values){ type = genesis.lookupType(type); if (reified.prototype.isPrototypeOf(this)) { return new type(subject, size, values); } else { subject = genesis.lookupType(subject); if (!subject || type.Class === 'Type' && !subject) { return type } if (subject === 'Char') return new CharType(type); if (typeof subject === 'string' || subject.Class === 'Type') { return new reified.ArrayType(type, subject, size); } else if (typeof type === 'undefined') { } else if (Array.isArray(subject) || typeof subject === 'number') { return new reified.BitfieldType(type, subject, size); } else { if (typeof type !== 'string' && typeof subject === 'undefined') { subject = type; type = ''; } subject = Object.keys(subject).reduce(function(ret, key){ if (subject[key].Class !== 'Type') { var fieldType = reified(subject[key]); if (!fieldType) return ret; if (typeof fieldType === 'string' || fieldType.Class !== 'Type') { ret[key] = reified(key, subject[key]); } else { ret[key] = fieldType; } } else { ret[key] = subject[key]; } return ret; }, {}); return new reified.StructType(type, subject); } } } // ## static functions reified.data = function data(type, buffer, offset, values){ type = genesis.lookupType(type); if (typeof type === 'string') throw new TypeError('Type not found "'+type+'"'); return new type(buffer, offset, values); } reified.reify = function reify(data){ if (data.Class === 'Data') { var proto = Object.getPrototypeOf(data.constructor).prototype; } return proto.reify.call(data); } reified.reifier = function reifier(type, handler){ type = reified(type); type.reifier(handler); return type; } reified.isType = function isType(o){ return genesis.Type.isPrototypeOf(o) } reified.isData = function isData(o){ return genesis.Type.prototype.isPrototypeOf(o) } Object.defineProperty(reified, 'defaultEndian', { enumerable: true, configurable: true, get: function(){ return genesis.DataBuffer.prototype.endianness; }, set: function(v){ if (v !== 'LE' && v !== 'BE') throw new Error('Endianness must be "BE" or "LE"'); genesis.DataBuffer.prototype.endianness = v; } }); // pointer that points to an unknown structure, which can be later cast to something var VoidPtr = reified('Opaque').ptr.typeDef('VoidPtr'); VoidPtr.prototype.reify = function(){ return { type: VoidPtr, address: this.address.reify() }; } // ## structures genesis.api(reified, { Type: genesis.Type, NumericType: NumericType, StructType: StructType, ArrayType: ArrayType, BitfieldType: BitfieldType, DataBuffer: genesis.DataBuffer, CharType: CharType, PointerType: PointerType, OpaqueType: OpaqueType, VoidPtr: VoidPtr, toString: function toString(){ return '◤▼▼▼▼▼▼▼◥\n▶reified◀\n◣▲▲▲▲▲▲▲◢' }, }); function isSame(arr1, arr2){ return !diff(arr1, arr2).length; } function diff(arr1, arr2){ return arr1.filter(function(item){ return !~arr2.indexOf(item); }); } NumericType.Uint64 = new ArrayType('Uint64', 'Uint32', 2); NumericType.Int64 = new ArrayType('Int64', 'Int32', 2); var OctetString = new ArrayType('EightByteOctetString', 'Uint8', 8); function octets(){ return new OctetString(this._data, this._offset) } NumericType.Uint64.prototype.octets = octets; NumericType.Int64.prototype.octets = octets; }({}, function(n){ return imports[n] }); return imports["./index"]; }(this, {}); if (typeof module !=="undefined") module.exports = reified /* * DataView.js: * An implementation of the DataView class on top of typed arrays. * Useful for Firefox 4 which implements TypedArrays but not DataView. * * Copyright 2011, David Flanagan * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ "use strict"; (function(global) { // If DataView already exists, do nothing if (global.DataView) return; // If ArrayBuffer is not supported, fail with an error if (!global.ArrayBuffer) fail("ArrayBuffer not supported"); // If ES5 is not supported, fail if (!Object.defineProperties) fail("This module requires ECMAScript 5"); // Figure if the platform is natively little-endian. // If the integer 0x00000001 is arranged in memory as 01 00 00 00 then // we're on a little endian platform. On a big-endian platform we'd get // get bytes 00 00 00 01 instead. var nativele = new Int8Array(new Int32Array([1]).buffer)[0] === 1; // A temporary array for copying or reversing bytes into. // Since js is single-threaded, we only need this one static copy var temp = new Uint8Array(8); // The DataView() constructor global.DataView = function DataView(buffer, offset, length) { if (!(buffer instanceof ArrayBuffer)) fail("Bad ArrayBuffer"); // Default values for omitted arguments offset = offset || 0; length = length || (buffer.byteLength - offset); if (offset < 0 || length < 0 || offset+length > buffer.byteLength) fail("Illegal offset and/or length"); // Define the 3 read-only, non-enumerable ArrayBufferView properties Object.defineProperties(this, { buffer: { value: buffer, enumerable:false, writable: false, configurable: false }, byteOffset: { value: offset, enumerable:false, writable: false, configurable: false }, byteLength: { value: length, enumerable:false, writable: false, configurable: false }, _bytes: { value: new Uint8Array(buffer, offset, length), enumerable:false, writable: false, configurable: false } }); } // The DataView prototype object global.DataView.prototype = { constructor: DataView, getInt8: function getInt8(offset) { return get(this, Int8Array, 1, offset); }, getUint8: function getUint8(offset) { return get(this, Uint8Array, 1, offset); }, getInt16: function getInt16(offset, le) { return get(this, Int16Array, 2, offset, le); }, getUint16: function getUint16(offset, le) { return get(this, Uint16Array, 2, offset, le); }, getInt32: function getInt32(offset, le) { return get(this, Int32Array, 4, offset, le); }, getUint32: function getUint32(offset, le) { return get(this, Uint32Array, 4, offset, le); }, getFloat32: function getFloat32(offset, le) { return get(this, Float32Array, 4, offset, le); }, getFloat64: function getFloat32(offset, le) { return get(this, Float64Array, 8, offset, le); }, setInt8: function setInt8(offset, value) { set(this, Int8Array, 1, offset, value); }, setUint8: function setUint8(offset, value) { set(this, Uint8Array, 1, offset, value); }, setInt16: function setInt16(offset, value, le) { set(this, Int16Array, 2, offset, value, le); }, setUint16: function setUint16(offset, value, le) { set(this, Uint16Array, 2, offset, value, le); }, setInt32: function setInt32(offset, value, le) { set(this, Int32Array, 4, offset, value, le); }, setUint32: function setUint32(offset, value, le) { set(this, Uint32Array, 4, offset, value, le); }, setFloat32: function setFloat32(offset, value, le) { set(this, Float32Array, 4, offset, value, le); }, setFloat64: function setFloat64(offset, value, le) { set(this, Float64Array, 8, offset, value, le); } }; // The get() utility function used by the get methods function get(view, type, size, offset, le) { if (offset === undefined) fail("Missing required offset argument"); if (offset < 0 || offset + size > view.byteLength) fail("Invalid index: " + offset); if (size === 1 || !!le === nativele) { // This is the easy case: the desired endianness // matches the native endianness. // Typed arrays require proper alignment.