UNPKG

reified

Version:

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

193 lines (164 loc) 5.85 kB
"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']; if (arguments.length === 1) { return new type(this.view); } else if (arguments.length === 2) { return new type(this.view, toNum(offset)); } else { length = toNum(length) || (this.length / type.BYTES_PER_ELEMENT) | 0; return new type(this.view, toNum(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) } }) });