/* Copyright 2013-2014 Daniel Wirtz Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @license bytebuffer.js (c) 2015 Daniel Wirtz * Backing buffer / Accessor: node Buffer * Released under the Apache License, Version 2.0 * see: https://github.com/dcodeIO/bytebuffer.js for details */ module.exports = (function() { "use strict"; var buffer = require("buffer"), Buffer = buffer["Buffer"], Long = require("long"), memcpy = null; try { memcpy = require("memcpy"); } catch (e) {} /** * Constructs a new ByteBuffer. * @class The swiss army knife for binary data in JavaScript. * @exports ByteBuffer * @constructor * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @expose */ var ByteBuffer = function(capacity, littleEndian, noAssert) { if (typeof capacity === 'undefined') capacity = ByteBuffer.DEFAULT_CAPACITY; if (typeof littleEndian === 'undefined') littleEndian = ByteBuffer.DEFAULT_ENDIAN; if (typeof noAssert === 'undefined') noAssert = ByteBuffer.DEFAULT_NOASSERT; if (!noAssert) { capacity = capacity | 0; if (capacity < 0) throw RangeError("Illegal capacity"); littleEndian = !!littleEndian; noAssert = !!noAssert; } /** * Backing node Buffer. * @type {!Buffer} * @expose */ this.buffer = capacity === 0 ? EMPTY_BUFFER : new Buffer(capacity); /** * Absolute read/write offset. * @type {number} * @expose * @see ByteBuffer#flip * @see ByteBuffer#clear */ this.offset = 0; /** * Marked offset. * @type {number} * @expose * @see ByteBuffer#mark * @see ByteBuffer#reset */ this.markedOffset = -1; /** * Absolute limit of the contained data. Set to the backing buffer's capacity upon allocation. * @type {number} * @expose * @see ByteBuffer#flip * @see ByteBuffer#clear */ this.limit = capacity; /** * Whether to use little endian byte order, defaults to `false` for big endian. * @type {boolean} * @expose */ this.littleEndian = littleEndian; /** * Whether to skip assertions of offsets and values, defaults to `false`. * @type {boolean} * @expose */ this.noAssert = noAssert; }; /** * ByteBuffer version. * @type {string} * @const * @expose */ ByteBuffer.VERSION = "5.0.1"; /** * Little endian constant that can be used instead of its boolean value. Evaluates to `true`. * @type {boolean} * @const * @expose */ ByteBuffer.LITTLE_ENDIAN = true; /** * Big endian constant that can be used instead of its boolean value. Evaluates to `false`. * @type {boolean} * @const * @expose */ ByteBuffer.BIG_ENDIAN = false; /** * Default initial capacity of `16`. * @type {number} * @expose */ ByteBuffer.DEFAULT_CAPACITY = 16; /** * Default endianess of `false` for big endian. * @type {boolean} * @expose */ ByteBuffer.DEFAULT_ENDIAN = ByteBuffer.BIG_ENDIAN; /** * Default no assertions flag of `false`. * @type {boolean} * @expose */ ByteBuffer.DEFAULT_NOASSERT = false; /** * A `Long` class for representing a 64-bit two's-complement integer value. * @type {!Long} * @const * @see https://npmjs.org/package/long * @expose */ ByteBuffer.Long = Long; /** * @alias ByteBuffer.prototype * @inner */ var ByteBufferPrototype = ByteBuffer.prototype; /** * An indicator used to reliably determine if an object is a ByteBuffer or not. * @type {boolean} * @const * @expose * @private */ ByteBufferPrototype.__isByteBuffer__; Object.defineProperty(ByteBufferPrototype, "__isByteBuffer__", { value: true, enumerable: false, configurable: false }); // helpers /** * @type {!Buffer} * @inner */ var EMPTY_BUFFER = new Buffer(0); /** * String.fromCharCode reference for compile-time renaming. * @type {function(...number):string} * @inner */ var stringFromCharCode = String.fromCharCode; /** * Creates a source function for a string. * @param {string} s String to read from * @returns {function():number|null} Source function returning the next char code respectively `null` if there are * no more characters left. * @throws {TypeError} If the argument is invalid * @inner */ function stringSource(s) { var i=0; return function() { return i < s.length ? s.charCodeAt(i++) : null; }; } /** * Creates a destination function for a string. * @returns {function(number=):undefined|string} Destination function successively called with the next char code. * Returns the final string when called without arguments. * @inner */ function stringDestination() { var cs = [], ps = []; return function() { if (arguments.length === 0) return ps.join('')+stringFromCharCode.apply(String, cs); if (cs.length + arguments.length > 1024) ps.push(stringFromCharCode.apply(String, cs)), cs.length = 0; Array.prototype.push.apply(cs, arguments); }; } /** * Gets the accessor type. * @returns {Function} `Buffer` under node.js, `Uint8Array` respectively `DataView` in the browser (classes) * @expose */ ByteBuffer.accessor = function() { return Buffer; }; /** * Allocates a new ByteBuffer backed by a buffer of the specified capacity. * @param {number=} capacity Initial capacity. Defaults to {@link ByteBuffer.DEFAULT_CAPACITY}. * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} * @expose */ ByteBuffer.allocate = function(capacity, littleEndian, noAssert) { return new ByteBuffer(capacity, littleEndian, noAssert); }; /** * Concatenates multiple ByteBuffers into one. * @param {!Array.} buffers Buffers to concatenate * @param {(string|boolean)=} encoding String encoding if `buffers` contains a string ("base64", "hex", "binary", * defaults to "utf8") * @param {boolean=} littleEndian Whether to use little or big endian byte order for the resulting ByteBuffer. Defaults * to {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values for the resulting ByteBuffer. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} Concatenated ByteBuffer * @expose */ ByteBuffer.concat = function(buffers, encoding, littleEndian, noAssert) { if (typeof encoding === 'boolean' || typeof encoding !== 'string') { noAssert = littleEndian; littleEndian = encoding; encoding = undefined; } var capacity = 0; for (var i=0, k=buffers.length, length; i 0) capacity += length; } if (capacity === 0) return new ByteBuffer(0, littleEndian, noAssert); var bb = new ByteBuffer(capacity, littleEndian, noAssert), bi; i=0; while (i} buffer Anything that can be wrapped * @param {(string|boolean)=} encoding String encoding if `buffer` is a string ("base64", "hex", "binary", defaults to * "utf8") * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} A ByteBuffer wrapping `buffer` * @expose */ ByteBuffer.wrap = function(buffer, encoding, littleEndian, noAssert) { if (typeof encoding !== 'string') { noAssert = littleEndian; littleEndian = encoding; encoding = undefined; } if (typeof buffer === 'string') { if (typeof encoding === 'undefined') encoding = "utf8"; switch (encoding) { case "base64": return ByteBuffer.fromBase64(buffer, littleEndian); case "hex": return ByteBuffer.fromHex(buffer, littleEndian); case "binary": return ByteBuffer.fromBinary(buffer, littleEndian); case "utf8": return ByteBuffer.fromUTF8(buffer, littleEndian); case "debug": return ByteBuffer.fromDebug(buffer, littleEndian); default: throw Error("Unsupported encoding: "+encoding); } } if (buffer === null || typeof buffer !== 'object') throw TypeError("Illegal buffer"); var bb; if (ByteBuffer.isByteBuffer(buffer)) { bb = ByteBufferPrototype.clone.call(buffer); bb.markedOffset = -1; return bb; } var i = 0, k = 0, b; if (buffer instanceof Uint8Array) { // Extract bytes from Uint8Array b = new Buffer(buffer.length); if (memcpy) { // Fast memcpy(b, 0, buffer.buffer, buffer.byteOffset, buffer.byteOffset + buffer.length); } else { // Slow for (i=0, k=buffer.length; i 0) { // Avoid references to more than one EMPTY_BUFFER bb.buffer = buffer; bb.limit = buffer.length; } return bb; }; /** * Writes the array as a bitset. * @param {Array} value Array of booleans to write * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `length` if omitted. * @returns {!ByteBuffer} * @expose */ ByteBufferPrototype.writeBitSet = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (!(value instanceof Array)) throw TypeError("Illegal BitSet: Not an array"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var start = offset, bits = value.length, bytes = (bits >> 3), bit = 0, k; offset += this.writeVarint32(bits,offset); while(bytes--) { k = (!!value[bit++] & 1) | ((!!value[bit++] & 1) << 1) | ((!!value[bit++] & 1) << 2) | ((!!value[bit++] & 1) << 3) | ((!!value[bit++] & 1) << 4) | ((!!value[bit++] & 1) << 5) | ((!!value[bit++] & 1) << 6) | ((!!value[bit++] & 1) << 7); this.writeByte(k,offset++); } if(bit < bits) { var m = 0; k = 0; while(bit < bits) k = k | ((!!value[bit++] & 1) << (m++)); this.writeByte(k,offset++); } if (relative) { this.offset = offset; return this; } return offset - start; } /** * Reads a BitSet as an array of booleans. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `length` if omitted. * @returns {Array * @expose */ ByteBufferPrototype.readBitSet = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; var ret = this.readVarint32(offset), bits = ret.value, bytes = (bits >> 3), bit = 0, value = [], k; offset += ret.length; while(bytes--) { k = this.readByte(offset++); value[bit++] = !!(k & 0x01); value[bit++] = !!(k & 0x02); value[bit++] = !!(k & 0x04); value[bit++] = !!(k & 0x08); value[bit++] = !!(k & 0x10); value[bit++] = !!(k & 0x20); value[bit++] = !!(k & 0x40); value[bit++] = !!(k & 0x80); } if(bit < bits) { var m = 0; k = this.readByte(offset++); while(bit < bits) value[bit++] = !!((k >> (m++)) & 1); } if (relative) { this.offset = offset; } return value; } /** * Reads the specified number of bytes. * @param {number} length Number of bytes to read * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `length` if omitted. * @returns {!ByteBuffer} * @expose */ ByteBufferPrototype.readBytes = function(length, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + length > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.length); } var slice = this.slice(offset, offset + length); if (relative) this.offset += length; return slice; }; // types/ints/int8 /** * Writes an 8bit signed integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeInt8 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value |= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 1; var capacity0 = this.buffer.length; if (offset > capacity0) this.resize((capacity0 *= 2) > offset ? capacity0 : offset); offset -= 1; this.buffer[offset] = value; if (relative) this.offset += 1; return this; }; /** * Writes an 8bit signed integer. This is an alias of {@link ByteBuffer#writeInt8}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeByte = ByteBufferPrototype.writeInt8; /** * Reads an 8bit signed integer. * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readInt8 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } var value = this.buffer[offset]; if ((value & 0x80) === 0x80) value = -(0xFF - value + 1); // Cast to signed if (relative) this.offset += 1; return value; }; /** * Reads an 8bit signed integer. This is an alias of {@link ByteBuffer#readInt8}. * @function * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readByte = ByteBufferPrototype.readInt8; /** * Writes an 8bit unsigned integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeUint8 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value >>>= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 1; var capacity1 = this.buffer.length; if (offset > capacity1) this.resize((capacity1 *= 2) > offset ? capacity1 : offset); offset -= 1; this.buffer[offset] = value; if (relative) this.offset += 1; return this; }; /** * Writes an 8bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint8}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeUInt8 = ByteBufferPrototype.writeUint8; /** * Reads an 8bit unsigned integer. * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readUint8 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } var value = this.buffer[offset]; if (relative) this.offset += 1; return value; }; /** * Reads an 8bit unsigned integer. This is an alias of {@link ByteBuffer#readUint8}. * @function * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `1` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readUInt8 = ByteBufferPrototype.readUint8; // types/ints/int16 /** * Writes a 16bit signed integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @throws {TypeError} If `offset` or `value` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.writeInt16 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value |= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 2; var capacity2 = this.buffer.length; if (offset > capacity2) this.resize((capacity2 *= 2) > offset ? capacity2 : offset); offset -= 2; if (this.littleEndian) { this.buffer[offset+1] = (value & 0xFF00) >>> 8; this.buffer[offset ] = value & 0x00FF; } else { this.buffer[offset] = (value & 0xFF00) >>> 8; this.buffer[offset+1] = value & 0x00FF; } if (relative) this.offset += 2; return this; }; /** * Writes a 16bit signed integer. This is an alias of {@link ByteBuffer#writeInt16}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @throws {TypeError} If `offset` or `value` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.writeShort = ByteBufferPrototype.writeInt16; /** * Reads a 16bit signed integer. * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @returns {number} Value read * @throws {TypeError} If `offset` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.readInt16 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 2 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.length); } var value = 0; if (this.littleEndian) { value = this.buffer[offset ]; value |= this.buffer[offset+1] << 8; } else { value = this.buffer[offset ] << 8; value |= this.buffer[offset+1]; } if ((value & 0x8000) === 0x8000) value = -(0xFFFF - value + 1); // Cast to signed if (relative) this.offset += 2; return value; }; /** * Reads a 16bit signed integer. This is an alias of {@link ByteBuffer#readInt16}. * @function * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @returns {number} Value read * @throws {TypeError} If `offset` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.readShort = ByteBufferPrototype.readInt16; /** * Writes a 16bit unsigned integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @throws {TypeError} If `offset` or `value` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.writeUint16 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value >>>= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 2; var capacity3 = this.buffer.length; if (offset > capacity3) this.resize((capacity3 *= 2) > offset ? capacity3 : offset); offset -= 2; if (this.littleEndian) { this.buffer[offset+1] = (value & 0xFF00) >>> 8; this.buffer[offset ] = value & 0x00FF; } else { this.buffer[offset] = (value & 0xFF00) >>> 8; this.buffer[offset+1] = value & 0x00FF; } if (relative) this.offset += 2; return this; }; /** * Writes a 16bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint16}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @throws {TypeError} If `offset` or `value` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.writeUInt16 = ByteBufferPrototype.writeUint16; /** * Reads a 16bit unsigned integer. * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @returns {number} Value read * @throws {TypeError} If `offset` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.readUint16 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 2 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.length); } var value = 0; if (this.littleEndian) { value = this.buffer[offset ]; value |= this.buffer[offset+1] << 8; } else { value = this.buffer[offset ] << 8; value |= this.buffer[offset+1]; } if (relative) this.offset += 2; return value; }; /** * Reads a 16bit unsigned integer. This is an alias of {@link ByteBuffer#readUint16}. * @function * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `2` if omitted. * @returns {number} Value read * @throws {TypeError} If `offset` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @expose */ ByteBufferPrototype.readUInt16 = ByteBufferPrototype.readUint16; // types/ints/int32 /** * Writes a 32bit signed integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @expose */ ByteBufferPrototype.writeInt32 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value |= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 4; var capacity4 = this.buffer.length; if (offset > capacity4) this.resize((capacity4 *= 2) > offset ? capacity4 : offset); offset -= 4; if (this.littleEndian) { this.buffer[offset+3] = (value >>> 24) & 0xFF; this.buffer[offset+2] = (value >>> 16) & 0xFF; this.buffer[offset+1] = (value >>> 8) & 0xFF; this.buffer[offset ] = value & 0xFF; } else { this.buffer[offset ] = (value >>> 24) & 0xFF; this.buffer[offset+1] = (value >>> 16) & 0xFF; this.buffer[offset+2] = (value >>> 8) & 0xFF; this.buffer[offset+3] = value & 0xFF; } if (relative) this.offset += 4; return this; }; /** * Writes a 32bit signed integer. This is an alias of {@link ByteBuffer#writeInt32}. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @expose */ ByteBufferPrototype.writeInt = ByteBufferPrototype.writeInt32; /** * Reads a 32bit signed integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readInt32 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 4 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.length); } var value = 0; if (this.littleEndian) { value = this.buffer[offset+2] << 16; value |= this.buffer[offset+1] << 8; value |= this.buffer[offset ]; value += this.buffer[offset+3] << 24 >>> 0; } else { value = this.buffer[offset+1] << 16; value |= this.buffer[offset+2] << 8; value |= this.buffer[offset+3]; value += this.buffer[offset ] << 24 >>> 0; } value |= 0; // Cast to signed if (relative) this.offset += 4; return value; }; /** * Reads a 32bit signed integer. This is an alias of {@link ByteBuffer#readInt32}. * @param {number=} offset Offset to read from. Will use and advance {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readInt = ByteBufferPrototype.readInt32; /** * Writes a 32bit unsigned integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @expose */ ByteBufferPrototype.writeUint32 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value >>>= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 4; var capacity5 = this.buffer.length; if (offset > capacity5) this.resize((capacity5 *= 2) > offset ? capacity5 : offset); offset -= 4; if (this.littleEndian) { this.buffer[offset+3] = (value >>> 24) & 0xFF; this.buffer[offset+2] = (value >>> 16) & 0xFF; this.buffer[offset+1] = (value >>> 8) & 0xFF; this.buffer[offset ] = value & 0xFF; } else { this.buffer[offset ] = (value >>> 24) & 0xFF; this.buffer[offset+1] = (value >>> 16) & 0xFF; this.buffer[offset+2] = (value >>> 8) & 0xFF; this.buffer[offset+3] = value & 0xFF; } if (relative) this.offset += 4; return this; }; /** * Writes a 32bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint32}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @expose */ ByteBufferPrototype.writeUInt32 = ByteBufferPrototype.writeUint32; /** * Reads a 32bit unsigned integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readUint32 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 4 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.length); } var value = 0; if (this.littleEndian) { value = this.buffer[offset+2] << 16; value |= this.buffer[offset+1] << 8; value |= this.buffer[offset ]; value += this.buffer[offset+3] << 24 >>> 0; } else { value = this.buffer[offset+1] << 16; value |= this.buffer[offset+2] << 8; value |= this.buffer[offset+3]; value += this.buffer[offset ] << 24 >>> 0; } if (relative) this.offset += 4; return value; }; /** * Reads a 32bit unsigned integer. This is an alias of {@link ByteBuffer#readUint32}. * @function * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} Value read * @expose */ ByteBufferPrototype.readUInt32 = ByteBufferPrototype.readUint32; // types/ints/int64 if (Long) { /** * Writes a 64bit signed integer. * @param {number|!Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeInt64 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); else if (!(value && value instanceof Long)) throw TypeError("Illegal value: "+value+" (not an integer or Long)"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); offset += 8; var capacity6 = this.buffer.length; if (offset > capacity6) this.resize((capacity6 *= 2) > offset ? capacity6 : offset); offset -= 8; var lo = value.low, hi = value.high; if (this.littleEndian) { this.buffer[offset+3] = (lo >>> 24) & 0xFF; this.buffer[offset+2] = (lo >>> 16) & 0xFF; this.buffer[offset+1] = (lo >>> 8) & 0xFF; this.buffer[offset ] = lo & 0xFF; offset += 4; this.buffer[offset+3] = (hi >>> 24) & 0xFF; this.buffer[offset+2] = (hi >>> 16) & 0xFF; this.buffer[offset+1] = (hi >>> 8) & 0xFF; this.buffer[offset ] = hi & 0xFF; } else { this.buffer[offset ] = (hi >>> 24) & 0xFF; this.buffer[offset+1] = (hi >>> 16) & 0xFF; this.buffer[offset+2] = (hi >>> 8) & 0xFF; this.buffer[offset+3] = hi & 0xFF; offset += 4; this.buffer[offset ] = (lo >>> 24) & 0xFF; this.buffer[offset+1] = (lo >>> 16) & 0xFF; this.buffer[offset+2] = (lo >>> 8) & 0xFF; this.buffer[offset+3] = lo & 0xFF; } if (relative) this.offset += 8; return this; }; /** * Writes a 64bit signed integer. This is an alias of {@link ByteBuffer#writeInt64}. * @param {number|!Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeLong = ByteBufferPrototype.writeInt64; /** * Reads a 64bit signed integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!Long} * @expose */ ByteBufferPrototype.readInt64 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 8 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.length); } var lo = 0, hi = 0; if (this.littleEndian) { lo = this.buffer[offset+2] << 16; lo |= this.buffer[offset+1] << 8; lo |= this.buffer[offset ]; lo += this.buffer[offset+3] << 24 >>> 0; offset += 4; hi = this.buffer[offset+2] << 16; hi |= this.buffer[offset+1] << 8; hi |= this.buffer[offset ]; hi += this.buffer[offset+3] << 24 >>> 0; } else { hi = this.buffer[offset+1] << 16; hi |= this.buffer[offset+2] << 8; hi |= this.buffer[offset+3]; hi += this.buffer[offset ] << 24 >>> 0; offset += 4; lo = this.buffer[offset+1] << 16; lo |= this.buffer[offset+2] << 8; lo |= this.buffer[offset+3]; lo += this.buffer[offset ] << 24 >>> 0; } var value = new Long(lo, hi, false); if (relative) this.offset += 8; return value; }; /** * Reads a 64bit signed integer. This is an alias of {@link ByteBuffer#readInt64}. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!Long} * @expose */ ByteBufferPrototype.readLong = ByteBufferPrototype.readInt64; /** * Writes a 64bit unsigned integer. * @param {number|!Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeUint64 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); else if (!(value && value instanceof Long)) throw TypeError("Illegal value: "+value+" (not an integer or Long)"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); offset += 8; var capacity7 = this.buffer.length; if (offset > capacity7) this.resize((capacity7 *= 2) > offset ? capacity7 : offset); offset -= 8; var lo = value.low, hi = value.high; if (this.littleEndian) { this.buffer[offset+3] = (lo >>> 24) & 0xFF; this.buffer[offset+2] = (lo >>> 16) & 0xFF; this.buffer[offset+1] = (lo >>> 8) & 0xFF; this.buffer[offset ] = lo & 0xFF; offset += 4; this.buffer[offset+3] = (hi >>> 24) & 0xFF; this.buffer[offset+2] = (hi >>> 16) & 0xFF; this.buffer[offset+1] = (hi >>> 8) & 0xFF; this.buffer[offset ] = hi & 0xFF; } else { this.buffer[offset ] = (hi >>> 24) & 0xFF; this.buffer[offset+1] = (hi >>> 16) & 0xFF; this.buffer[offset+2] = (hi >>> 8) & 0xFF; this.buffer[offset+3] = hi & 0xFF; offset += 4; this.buffer[offset ] = (lo >>> 24) & 0xFF; this.buffer[offset+1] = (lo >>> 16) & 0xFF; this.buffer[offset+2] = (lo >>> 8) & 0xFF; this.buffer[offset+3] = lo & 0xFF; } if (relative) this.offset += 8; return this; }; /** * Writes a 64bit unsigned integer. This is an alias of {@link ByteBuffer#writeUint64}. * @function * @param {number|!Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeUInt64 = ByteBufferPrototype.writeUint64; /** * Reads a 64bit unsigned integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!Long} * @expose */ ByteBufferPrototype.readUint64 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 8 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.length); } var lo = 0, hi = 0; if (this.littleEndian) { lo = this.buffer[offset+2] << 16; lo |= this.buffer[offset+1] << 8; lo |= this.buffer[offset ]; lo += this.buffer[offset+3] << 24 >>> 0; offset += 4; hi = this.buffer[offset+2] << 16; hi |= this.buffer[offset+1] << 8; hi |= this.buffer[offset ]; hi += this.buffer[offset+3] << 24 >>> 0; } else { hi = this.buffer[offset+1] << 16; hi |= this.buffer[offset+2] << 8; hi |= this.buffer[offset+3]; hi += this.buffer[offset ] << 24 >>> 0; offset += 4; lo = this.buffer[offset+1] << 16; lo |= this.buffer[offset+2] << 8; lo |= this.buffer[offset+3]; lo += this.buffer[offset ] << 24 >>> 0; } var value = new Long(lo, hi, true); if (relative) this.offset += 8; return value; }; /** * Reads a 64bit unsigned integer. This is an alias of {@link ByteBuffer#readUint64}. * @function * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!Long} * @expose */ ByteBufferPrototype.readUInt64 = ByteBufferPrototype.readUint64; } // Long // types/floats/float32 /** * Writes a 32bit float. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeFloat32 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number') throw TypeError("Illegal value: "+value+" (not a number)"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 4; var capacity8 = this.buffer.length; if (offset > capacity8) this.resize((capacity8 *= 2) > offset ? capacity8 : offset); offset -= 4; this.littleEndian ? this.buffer.writeFloatLE(value, offset, true) : this.buffer.writeFloatBE(value, offset, true); if (relative) this.offset += 4; return this; }; /** * Writes a 32bit float. This is an alias of {@link ByteBuffer#writeFloat32}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeFloat = ByteBufferPrototype.writeFloat32; /** * Reads a 32bit float. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} * @expose */ ByteBufferPrototype.readFloat32 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 4 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.length); } var value = this.littleEndian ? this.buffer.readFloatLE(offset, true) : this.buffer.readFloatBE(offset, true); if (relative) this.offset += 4; return value; }; /** * Reads a 32bit float. This is an alias of {@link ByteBuffer#readFloat32}. * @function * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `4` if omitted. * @returns {number} * @expose */ ByteBufferPrototype.readFloat = ByteBufferPrototype.readFloat32; // types/floats/float64 /** * Writes a 64bit float. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeFloat64 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number') throw TypeError("Illegal value: "+value+" (not a number)"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } offset += 8; var capacity9 = this.buffer.length; if (offset > capacity9) this.resize((capacity9 *= 2) > offset ? capacity9 : offset); offset -= 8; this.littleEndian ? this.buffer.writeDoubleLE(value, offset, true) : this.buffer.writeDoubleBE(value, offset, true); if (relative) this.offset += 8; return this; }; /** * Writes a 64bit float. This is an alias of {@link ByteBuffer#writeFloat64}. * @function * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeDouble = ByteBufferPrototype.writeFloat64; /** * Reads a 64bit float. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {number} * @expose */ ByteBufferPrototype.readFloat64 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 8 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.length); } var value = this.littleEndian ? this.buffer.readDoubleLE(offset, true) : this.buffer.readDoubleBE(offset, true); if (relative) this.offset += 8; return value; }; /** * Reads a 64bit float. This is an alias of {@link ByteBuffer#readFloat64}. * @function * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by `8` if omitted. * @returns {number} * @expose */ ByteBufferPrototype.readDouble = ByteBufferPrototype.readFloat64; // types/varints/varint32 /** * Maximum number of bytes required to store a 32bit base 128 variable-length integer. * @type {number} * @const * @expose */ ByteBuffer.MAX_VARINT32_BYTES = 5; /** * Calculates the actual number of bytes required to store a 32bit base 128 variable-length integer. * @param {number} value Value to encode * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT32_BYTES} * @expose */ ByteBuffer.calculateVarint32 = function(value) { // ref: src/google/protobuf/io/coded_stream.cc value = value >>> 0; if (value < 1 << 7 ) return 1; else if (value < 1 << 14) return 2; else if (value < 1 << 21) return 3; else if (value < 1 << 28) return 4; else return 5; }; /** * Zigzag encodes a signed 32bit integer so that it can be effectively used with varint encoding. * @param {number} n Signed 32bit integer * @returns {number} Unsigned zigzag encoded 32bit integer * @expose */ ByteBuffer.zigZagEncode32 = function(n) { return (((n |= 0) << 1) ^ (n >> 31)) >>> 0; // ref: src/google/protobuf/wire_format_lite.h }; /** * Decodes a zigzag encoded signed 32bit integer. * @param {number} n Unsigned zigzag encoded 32bit integer * @returns {number} Signed 32bit integer * @expose */ ByteBuffer.zigZagDecode32 = function(n) { return ((n >>> 1) ^ -(n & 1)) | 0; // // ref: src/google/protobuf/wire_format_lite.h }; /** * Writes a 32bit base 128 variable-length integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written * @expose */ ByteBufferPrototype.writeVarint32 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value |= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var size = ByteBuffer.calculateVarint32(value), b; offset += size; var capacity10 = this.buffer.length; if (offset > capacity10) this.resize((capacity10 *= 2) > offset ? capacity10 : offset); offset -= size; value >>>= 0; while (value >= 0x80) { b = (value & 0x7f) | 0x80; this.buffer[offset++] = b; value >>>= 7; } this.buffer[offset++] = value; if (relative) { this.offset = offset; return this; } return size; }; /** * Writes a zig-zag encoded (signed) 32bit base 128 variable-length integer. * @param {number} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} this if `offset` is omitted, else the actual number of bytes written * @expose */ ByteBufferPrototype.writeVarint32ZigZag = function(value, offset) { return this.writeVarint32(ByteBuffer.zigZagEncode32(value), offset); }; /** * Reads a 32bit base 128 variable-length integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read * and the actual number of bytes read. * @throws {Error} If it's not a valid varint. Has a property `truncated = true` if there is not enough data available * to fully decode the varint. * @expose */ ByteBufferPrototype.readVarint32 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } var c = 0, value = 0 >>> 0, b; do { if (!this.noAssert && offset > this.limit) { var err = Error("Truncated"); err['truncated'] = true; throw err; } b = this.buffer[offset++]; if (c < 5) value |= (b & 0x7f) << (7*c); ++c; } while ((b & 0x80) !== 0); value |= 0; if (relative) { this.offset = offset; return value; } return { "value": value, "length": c }; }; /** * Reads a zig-zag encoded (signed) 32bit base 128 variable-length integer. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {number|!{value: number, length: number}} The value read if offset is omitted, else the value read * and the actual number of bytes read. * @throws {Error} If it's not a valid varint * @expose */ ByteBufferPrototype.readVarint32ZigZag = function(offset) { var val = this.readVarint32(offset); if (typeof val === 'object') val["value"] = ByteBuffer.zigZagDecode32(val["value"]); else val = ByteBuffer.zigZagDecode32(val); return val; }; // types/varints/varint64 if (Long) { /** * Maximum number of bytes required to store a 64bit base 128 variable-length integer. * @type {number} * @const * @expose */ ByteBuffer.MAX_VARINT64_BYTES = 10; /** * Calculates the actual number of bytes required to store a 64bit base 128 variable-length integer. * @param {number|!Long} value Value to encode * @returns {number} Number of bytes required. Capped to {@link ByteBuffer.MAX_VARINT64_BYTES} * @expose */ ByteBuffer.calculateVarint64 = function(value) { if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); // ref: src/google/protobuf/io/coded_stream.cc var part0 = value.toInt() >>> 0, part1 = value.shiftRightUnsigned(28).toInt() >>> 0, part2 = value.shiftRightUnsigned(56).toInt() >>> 0; if (part2 == 0) { if (part1 == 0) { if (part0 < 1 << 14) return part0 < 1 << 7 ? 1 : 2; else return part0 < 1 << 21 ? 3 : 4; } else { if (part1 < 1 << 14) return part1 < 1 << 7 ? 5 : 6; else return part1 < 1 << 21 ? 7 : 8; } } else return part2 < 1 << 7 ? 9 : 10; }; /** * Zigzag encodes a signed 64bit integer so that it can be effectively used with varint encoding. * @param {number|!Long} value Signed long * @returns {!Long} Unsigned zigzag encoded long * @expose */ ByteBuffer.zigZagEncode64 = function(value) { if (typeof value === 'number') value = Long.fromNumber(value, false); else if (typeof value === 'string') value = Long.fromString(value, false); else if (value.unsigned !== false) value = value.toSigned(); // ref: src/google/protobuf/wire_format_lite.h return value.shiftLeft(1).xor(value.shiftRight(63)).toUnsigned(); }; /** * Decodes a zigzag encoded signed 64bit integer. * @param {!Long|number} value Unsigned zigzag encoded long or JavaScript number * @returns {!Long} Signed long * @expose */ ByteBuffer.zigZagDecode64 = function(value) { if (typeof value === 'number') value = Long.fromNumber(value, false); else if (typeof value === 'string') value = Long.fromString(value, false); else if (value.unsigned !== false) value = value.toSigned(); // ref: src/google/protobuf/wire_format_lite.h return value.shiftRightUnsigned(1).xor(value.and(Long.ONE).toSigned().negate()).toSigned(); }; /** * Writes a 64bit base 128 variable-length integer. * @param {number|Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. * @expose */ ByteBufferPrototype.writeVarint64 = function(value, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof value === 'number') value = Long.fromNumber(value); else if (typeof value === 'string') value = Long.fromString(value); else if (!(value && value instanceof Long)) throw TypeError("Illegal value: "+value+" (not an integer or Long)"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } if (typeof value === 'number') value = Long.fromNumber(value, false); else if (typeof value === 'string') value = Long.fromString(value, false); else if (value.unsigned !== false) value = value.toSigned(); var size = ByteBuffer.calculateVarint64(value), part0 = value.toInt() >>> 0, part1 = value.shiftRightUnsigned(28).toInt() >>> 0, part2 = value.shiftRightUnsigned(56).toInt() >>> 0; offset += size; var capacity11 = this.buffer.length; if (offset > capacity11) this.resize((capacity11 *= 2) > offset ? capacity11 : offset); offset -= size; switch (size) { case 10: this.buffer[offset+9] = (part2 >>> 7) & 0x01; case 9 : this.buffer[offset+8] = size !== 9 ? (part2 ) | 0x80 : (part2 ) & 0x7F; case 8 : this.buffer[offset+7] = size !== 8 ? (part1 >>> 21) | 0x80 : (part1 >>> 21) & 0x7F; case 7 : this.buffer[offset+6] = size !== 7 ? (part1 >>> 14) | 0x80 : (part1 >>> 14) & 0x7F; case 6 : this.buffer[offset+5] = size !== 6 ? (part1 >>> 7) | 0x80 : (part1 >>> 7) & 0x7F; case 5 : this.buffer[offset+4] = size !== 5 ? (part1 ) | 0x80 : (part1 ) & 0x7F; case 4 : this.buffer[offset+3] = size !== 4 ? (part0 >>> 21) | 0x80 : (part0 >>> 21) & 0x7F; case 3 : this.buffer[offset+2] = size !== 3 ? (part0 >>> 14) | 0x80 : (part0 >>> 14) & 0x7F; case 2 : this.buffer[offset+1] = size !== 2 ? (part0 >>> 7) | 0x80 : (part0 >>> 7) & 0x7F; case 1 : this.buffer[offset ] = size !== 1 ? (part0 ) | 0x80 : (part0 ) & 0x7F; } if (relative) { this.offset += size; return this; } else { return size; } }; /** * Writes a zig-zag encoded 64bit base 128 variable-length integer. * @param {number|Long} value Value to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} `this` if offset is omitted, else the actual number of bytes written. * @expose */ ByteBufferPrototype.writeVarint64ZigZag = function(value, offset) { return this.writeVarint64(ByteBuffer.zigZagEncode64(value), offset); }; /** * Reads a 64bit base 128 variable-length integer. Requires Long.js. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and * the actual number of bytes read. * @throws {Error} If it's not a valid varint * @expose */ ByteBufferPrototype.readVarint64 = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } // ref: src/google/protobuf/io/coded_stream.cc var start = offset, part0 = 0, part1 = 0, part2 = 0, b = 0; b = this.buffer[offset++]; part0 = (b & 0x7F) ; if ( b & 0x80 ) { b = this.buffer[offset++]; part0 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part0 |= (b & 0x7F) << 14; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part0 |= (b & 0x7F) << 21; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part1 = (b & 0x7F) ; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part1 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part1 |= (b & 0x7F) << 14; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part1 |= (b & 0x7F) << 21; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part2 = (b & 0x7F) ; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { b = this.buffer[offset++]; part2 |= (b & 0x7F) << 7; if ((b & 0x80) || (this.noAssert && typeof b === 'undefined')) { throw Error("Buffer overrun"); }}}}}}}}}} var value = Long.fromBits(part0 | (part1 << 28), (part1 >>> 4) | (part2) << 24, false); if (relative) { this.offset = offset; return value; } else { return { 'value': value, 'length': offset-start }; } }; /** * Reads a zig-zag encoded 64bit base 128 variable-length integer. Requires Long.js. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {!Long|!{value: Long, length: number}} The value read if offset is omitted, else the value read and * the actual number of bytes read. * @throws {Error} If it's not a valid varint * @expose */ ByteBufferPrototype.readVarint64ZigZag = function(offset) { var val = this.readVarint64(offset); if (val && val['value'] instanceof Long) val["value"] = ByteBuffer.zigZagDecode64(val["value"]); else val = ByteBuffer.zigZagDecode64(val); return val; }; } // Long // types/strings/cstring /** * Writes a NULL-terminated UTF8 encoded string. For this to work the specified string must not contain any NULL * characters itself. * @param {string} str String to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * contained in `str` + 1 if omitted. * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written * @expose */ ByteBufferPrototype.writeCString = function(str, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; var i, k = str.length; if (!this.noAssert) { if (typeof str !== 'string') throw TypeError("Illegal str: Not a string"); for (i=0; i>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } // UTF8 strings do not contain zero bytes in between except for the zero character, so: k = Buffer.byteLength(str, "utf8"); offset += k+1; var capacity12 = this.buffer.length; if (offset > capacity12) this.resize((capacity12 *= 2) > offset ? capacity12 : offset); offset -= k+1; offset += this.buffer.write(str, offset, k, "utf8"); this.buffer[offset++] = 0; if (relative) { this.offset = offset; return this; } return k; }; /** * Reads a NULL-terminated UTF8 encoded string. For this to work the string read must not contain any NULL characters * itself. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string * read and the actual number of bytes read. * @expose */ ByteBufferPrototype.readCString = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } var start = offset, temp; // UTF8 strings do not contain zero bytes in between except for the zero character itself, so: do { if (offset >= this.buffer.length) throw RangeError("Index out of range: "+offset+" <= "+this.buffer.length); temp = this.buffer[offset++]; } while (temp !== 0); var str = this.buffer.toString("utf8", start, offset-1); if (relative) { this.offset = offset; return str; } else { return { "string": str, "length": offset - start }; } }; // types/strings/istring /** * Writes a length as uint32 prefixed UTF8 encoded string. * @param {string} str String to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written * @expose * @see ByteBuffer#writeVarint32 */ ByteBufferPrototype.writeIString = function(str, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof str !== 'string') throw TypeError("Illegal str: Not a string"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var start = offset, k; k = Buffer.byteLength(str, "utf8"); offset += 4+k; var capacity13 = this.buffer.length; if (offset > capacity13) this.resize((capacity13 *= 2) > offset ? capacity13 : offset); offset -= 4+k; if (this.littleEndian) { this.buffer[offset+3] = (k >>> 24) & 0xFF; this.buffer[offset+2] = (k >>> 16) & 0xFF; this.buffer[offset+1] = (k >>> 8) & 0xFF; this.buffer[offset ] = k & 0xFF; } else { this.buffer[offset ] = (k >>> 24) & 0xFF; this.buffer[offset+1] = (k >>> 16) & 0xFF; this.buffer[offset+2] = (k >>> 8) & 0xFF; this.buffer[offset+3] = k & 0xFF; } offset += 4; offset += this.buffer.write(str, offset, k, "utf8"); if (relative) { this.offset = offset; return this; } return offset - start; }; /** * Reads a length as uint32 prefixed UTF8 encoded string. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string * read and the actual number of bytes read. * @expose * @see ByteBuffer#readVarint32 */ ByteBufferPrototype.readIString = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 4 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.length); } var start = offset; var len = this.readUint32(offset); var str = this.readUTF8String(len, ByteBuffer.METRICS_BYTES, offset += 4); offset += str['length']; if (relative) { this.offset = offset; return str['string']; } else { return { 'string': str['string'], 'length': offset - start }; } }; // types/strings/utf8string /** * Metrics representing number of UTF8 characters. Evaluates to `c`. * @type {string} * @const * @expose */ ByteBuffer.METRICS_CHARS = 'c'; /** * Metrics representing number of bytes. Evaluates to `b`. * @type {string} * @const * @expose */ ByteBuffer.METRICS_BYTES = 'b'; /** * Writes an UTF8 encoded string. * @param {string} str String to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. * @expose */ ByteBufferPrototype.writeUTF8String = function(str, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var k; k = Buffer.byteLength(str, "utf8"); offset += k; var capacity14 = this.buffer.length; if (offset > capacity14) this.resize((capacity14 *= 2) > offset ? capacity14 : offset); offset -= k; offset += this.buffer.write(str, offset, k, "utf8"); if (relative) { this.offset = offset; return this; } return k; }; /** * Writes an UTF8 encoded string. This is an alias of {@link ByteBuffer#writeUTF8String}. * @function * @param {string} str String to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} if omitted. * @returns {!ByteBuffer|number} this if offset is omitted, else the actual number of bytes written. * @expose */ ByteBufferPrototype.writeString = ByteBufferPrototype.writeUTF8String; /** * Calculates the number of UTF8 characters of a string. JavaScript itself uses UTF-16, so that a string's * `length` property does not reflect its actual UTF8 size if it contains code points larger than 0xFFFF. * @param {string} str String to calculate * @returns {number} Number of UTF8 characters * @expose */ ByteBuffer.calculateUTF8Chars = function(str) { return utfx.calculateUTF16asUTF8(stringSource(str))[0]; }; /** * Calculates the number of UTF8 bytes of a string. * @param {string} str String to calculate * @returns {number} Number of UTF8 bytes * @expose */ ByteBuffer.calculateUTF8Bytes = function(str) { if (typeof str !== 'string') throw TypeError("Illegal argument: "+(typeof str)); return Buffer.byteLength(str, "utf8"); }; /** * Calculates the number of UTF8 bytes of a string. This is an alias of {@link ByteBuffer.calculateUTF8Bytes}. * @function * @param {string} str String to calculate * @returns {number} Number of UTF8 bytes * @expose */ ByteBuffer.calculateString = ByteBuffer.calculateUTF8Bytes; /** * Reads an UTF8 encoded string. * @param {number} length Number of characters or bytes to read. * @param {string=} metrics Metrics specifying what `length` is meant to count. Defaults to * {@link ByteBuffer.METRICS_CHARS}. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string * read and the actual number of bytes read. * @expose */ ByteBufferPrototype.readUTF8String = function(length, metrics, offset) { if (typeof metrics === 'number') { offset = metrics; metrics = undefined; } var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (typeof metrics === 'undefined') metrics = ByteBuffer.METRICS_CHARS; if (!this.noAssert) { if (typeof length !== 'number' || length % 1 !== 0) throw TypeError("Illegal length: "+length+" (not an integer)"); length |= 0; if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var i = 0, start = offset, temp, sd; if (metrics === ByteBuffer.METRICS_CHARS) { // The same for node and the browser sd = stringDestination(); utfx.decodeUTF8(function() { return i < length && offset < this.limit ? this.buffer[offset++] : null; }.bind(this), function(cp) { ++i; utfx.UTF8toUTF16(cp, sd); }); if (i !== length) throw RangeError("Illegal range: Truncated data, "+i+" == "+length); if (relative) { this.offset = offset; return sd(); } else { return { "string": sd(), "length": offset - start }; } } else if (metrics === ByteBuffer.METRICS_BYTES) { if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + length > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+length+") <= "+this.buffer.length); } temp = this.buffer.toString("utf8", offset, offset+length); if (relative) { this.offset += length; return temp; } else { return { 'string': temp, 'length': length }; } } else throw TypeError("Unsupported metrics: "+metrics); }; /** * Reads an UTF8 encoded string. This is an alias of {@link ByteBuffer#readUTF8String}. * @function * @param {number} length Number of characters or bytes to read * @param {number=} metrics Metrics specifying what `n` is meant to count. Defaults to * {@link ByteBuffer.METRICS_CHARS}. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string * read and the actual number of bytes read. * @expose */ ByteBufferPrototype.readString = ByteBufferPrototype.readUTF8String; // types/strings/vstring /** * Writes a length as varint32 prefixed UTF8 encoded string. * @param {string} str String to write * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer|number} `this` if `offset` is omitted, else the actual number of bytes written * @expose * @see ByteBuffer#writeVarint32 */ ByteBufferPrototype.writeVString = function(str, offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof str !== 'string') throw TypeError("Illegal str: Not a string"); if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } var start = offset, k, l; k = Buffer.byteLength(str, "utf8"); l = ByteBuffer.calculateVarint32(k); offset += l+k; var capacity15 = this.buffer.length; if (offset > capacity15) this.resize((capacity15 *= 2) > offset ? capacity15 : offset); offset -= l+k; offset += this.writeVarint32(k, offset); offset += this.buffer.write(str, offset, k, "utf8"); if (relative) { this.offset = offset; return this; } return offset - start; }; /** * Reads a length as varint32 prefixed UTF8 encoded string. * @param {number=} offset Offset to read from. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {string|!{string: string, length: number}} The string read if offset is omitted, else the string * read and the actual number of bytes read. * @expose * @see ByteBuffer#readVarint32 */ ByteBufferPrototype.readVString = function(offset) { var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 1 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.length); } var start = offset; var len = this.readVarint32(offset); var str = this.readUTF8String(len['value'], ByteBuffer.METRICS_BYTES, offset += len['length']); offset += str['length']; if (relative) { this.offset = offset; return str['string']; } else { return { 'string': str['string'], 'length': offset - start }; } }; /** * Appends some data to this ByteBuffer. This will overwrite any contents behind the specified offset up to the appended * data's length. * @param {!ByteBuffer|!Buffer|!ArrayBuffer|!Uint8Array|string} source Data to append. If `source` is a ByteBuffer, its * offsets will be modified according to the performed read operation. * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") * @param {number=} offset Offset to append at. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer} this * @expose * @example A relative `<01 02>03.append(<04 05>)` will result in `<01 02 04 05>, 04 05|` * @example An absolute `<01 02>03.append(04 05>, 1)` will result in `<01 04>05, 04 05|` */ ByteBufferPrototype.append = function(source, encoding, offset) { if (typeof encoding === 'number' || typeof encoding !== 'string') { offset = encoding; encoding = undefined; } var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } if (!(source instanceof ByteBuffer)) source = ByteBuffer.wrap(source, encoding); var length = source.limit - source.offset; if (length <= 0) return this; // Nothing to append offset += length; var capacity16 = this.buffer.length; if (offset > capacity16) this.resize((capacity16 *= 2) > offset ? capacity16 : offset); offset -= length; source.buffer.copy(this.buffer, offset, source.offset, source.limit); source.offset += length; if (relative) this.offset += length; return this; }; /** * Appends this ByteBuffer's contents to another ByteBuffer. This will overwrite any contents at and after the specified offset up to the length of this ByteBuffer's data. * @param {!ByteBuffer} target Target ByteBuffer * @param {number=} offset Offset to append to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * read if omitted. * @returns {!ByteBuffer} this * @expose * @see ByteBuffer#append */ ByteBufferPrototype.appendTo = function(target, offset) { target.append(this, offset); return this; }; /** * Writes a payload of bytes. This is an alias of {@link ByteBuffer#append}. * @function * @param {!ByteBuffer|!Buffer|!ArrayBuffer|!Uint8Array|string} source Data to write. If `source` is a ByteBuffer, its * offsets will be modified according to the performed read operation. * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") * @param {number=} offset Offset to write to. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.writeBytes = ByteBufferPrototype.append; /** * Enables or disables assertions of argument types and offsets. Assertions are enabled by default but you can opt to * disable them if your code already makes sure that everything is valid. * @param {boolean} assert `true` to enable assertions, otherwise `false` * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.assert = function(assert) { this.noAssert = !assert; return this; }; /** * Gets the capacity of this ByteBuffer's backing buffer. * @returns {number} Capacity of the backing buffer * @expose */ ByteBufferPrototype.capacity = function() { return this.buffer.length; }; /** * Clears this ByteBuffer's offsets by setting {@link ByteBuffer#offset} to `0` and {@link ByteBuffer#limit} to the * backing buffer's capacity. Discards {@link ByteBuffer#markedOffset}. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.clear = function() { this.offset = 0; this.limit = this.buffer.length; this.markedOffset = -1; return this; }; /** * Creates a cloned instance of this ByteBuffer, preset with this ByteBuffer's values for {@link ByteBuffer#offset}, * {@link ByteBuffer#markedOffset} and {@link ByteBuffer#limit}. * @param {boolean=} copy Whether to copy the backing buffer or to return another view on the same, defaults to `false` * @returns {!ByteBuffer} Cloned instance * @expose */ ByteBufferPrototype.clone = function(copy) { var bb = new ByteBuffer(0, this.littleEndian, this.noAssert); if (copy) { var buffer = new Buffer(this.buffer.length); this.buffer.copy(buffer); bb.buffer = buffer; } else { bb.buffer = this.buffer; } bb.offset = this.offset; bb.markedOffset = this.markedOffset; bb.limit = this.limit; return bb; }; /** * Compacts this ByteBuffer to be backed by a {@link ByteBuffer#buffer} of its contents' length. Contents are the bytes * between {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. Will set `offset = 0` and `limit = capacity` and * adapt {@link ByteBuffer#markedOffset} to the same relative position if set. * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.compact = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } if (begin === 0 && end === this.buffer.length) return this; // Already compacted var len = end - begin; if (len === 0) { this.buffer = EMPTY_BUFFER; if (this.markedOffset >= 0) this.markedOffset -= begin; this.offset = 0; this.limit = 0; return this; } var buffer = new Buffer(len); this.buffer.copy(buffer, 0, begin, end); this.buffer = buffer; if (this.markedOffset >= 0) this.markedOffset -= begin; this.offset = 0; this.limit = len; return this; }; /** * Creates a copy of this ByteBuffer's contents. Contents are the bytes between {@link ByteBuffer#offset} and * {@link ByteBuffer#limit}. * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. * @returns {!ByteBuffer} Copy * @expose */ ByteBufferPrototype.copy = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } if (begin === end) return new ByteBuffer(0, this.littleEndian, this.noAssert); var capacity = end - begin, bb = new ByteBuffer(capacity, this.littleEndian, this.noAssert); bb.offset = 0; bb.limit = capacity; if (bb.markedOffset >= 0) bb.markedOffset -= begin; this.copyTo(bb, 0, begin, end); return bb; }; /** * Copies this ByteBuffer's contents to another ByteBuffer. Contents are the bytes between {@link ByteBuffer#offset} and * {@link ByteBuffer#limit}. * @param {!ByteBuffer} target Target ByteBuffer * @param {number=} targetOffset Offset to copy to. Will use and increase the target's {@link ByteBuffer#offset} * by the number of bytes copied if omitted. * @param {number=} sourceOffset Offset to start copying from. Will use and increase {@link ByteBuffer#offset} by the * number of bytes copied if omitted. * @param {number=} sourceLimit Offset to end copying from, defaults to {@link ByteBuffer#limit} * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.copyTo = function(target, targetOffset, sourceOffset, sourceLimit) { var relative, targetRelative; if (!this.noAssert) { if (!ByteBuffer.isByteBuffer(target)) throw TypeError("Illegal target: Not a ByteBuffer"); } targetOffset = (targetRelative = typeof targetOffset === 'undefined') ? target.offset : targetOffset | 0; sourceOffset = (relative = typeof sourceOffset === 'undefined') ? this.offset : sourceOffset | 0; sourceLimit = typeof sourceLimit === 'undefined' ? this.limit : sourceLimit | 0; if (targetOffset < 0 || targetOffset > target.buffer.length) throw RangeError("Illegal target range: 0 <= "+targetOffset+" <= "+target.buffer.length); if (sourceOffset < 0 || sourceLimit > this.buffer.length) throw RangeError("Illegal source range: 0 <= "+sourceOffset+" <= "+this.buffer.length); var len = sourceLimit - sourceOffset; if (len === 0) return target; // Nothing to copy target.ensureCapacity(targetOffset + len); this.buffer.copy(target.buffer, targetOffset, sourceOffset, sourceLimit); if (relative) this.offset += len; if (targetRelative) target.offset += len; return this; }; /** * Makes sure that this ByteBuffer is backed by a {@link ByteBuffer#buffer} of at least the specified capacity. If the * current capacity is exceeded, it will be doubled. If double the current capacity is less than the required capacity, * the required capacity will be used instead. * @param {number} capacity Required capacity * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.ensureCapacity = function(capacity) { var current = this.buffer.length; if (current < capacity) return this.resize((current *= 2) > capacity ? current : capacity); return this; }; /** * Overwrites this ByteBuffer's contents with the specified value. Contents are the bytes between * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. * @param {number|string} value Byte value to fill with. If given as a string, the first character is used. * @param {number=} begin Begin offset. Will use and increase {@link ByteBuffer#offset} by the number of bytes * written if omitted. defaults to {@link ByteBuffer#offset}. * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. * @returns {!ByteBuffer} this * @expose * @example `someByteBuffer.clear().fill(0)` fills the entire backing buffer with zeroes */ ByteBufferPrototype.fill = function(value, begin, end) { var relative = typeof begin === 'undefined'; if (relative) begin = this.offset; if (typeof value === 'string' && value.length > 0) value = value.charCodeAt(0); if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof value !== 'number' || value % 1 !== 0) throw TypeError("Illegal value: "+value+" (not an integer)"); value |= 0; if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } if (begin >= end) return this; // Nothing to fill this.buffer.fill(value, begin, end); begin = end; if (relative) this.offset = begin; return this; }; /** * Makes this ByteBuffer ready for a new sequence of write or relative read operations. Sets `limit = offset` and * `offset = 0`. Make sure always to flip a ByteBuffer when all relative read or write operations are complete. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.flip = function() { this.limit = this.offset; this.offset = 0; return this; }; /** * Marks an offset on this ByteBuffer to be used later. * @param {number=} offset Offset to mark. Defaults to {@link ByteBuffer#offset}. * @returns {!ByteBuffer} this * @throws {TypeError} If `offset` is not a valid number * @throws {RangeError} If `offset` is out of bounds * @see ByteBuffer#reset * @expose */ ByteBufferPrototype.mark = function(offset) { offset = typeof offset === 'undefined' ? this.offset : offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } this.markedOffset = offset; return this; }; /** * Sets the byte order. * @param {boolean} littleEndian `true` for little endian byte order, `false` for big endian * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.order = function(littleEndian) { if (!this.noAssert) { if (typeof littleEndian !== 'boolean') throw TypeError("Illegal littleEndian: Not a boolean"); } this.littleEndian = !!littleEndian; return this; }; /** * Switches (to) little endian byte order. * @param {boolean=} littleEndian Defaults to `true`, otherwise uses big endian * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.LE = function(littleEndian) { this.littleEndian = typeof littleEndian !== 'undefined' ? !!littleEndian : true; return this; }; /** * Switches (to) big endian byte order. * @param {boolean=} bigEndian Defaults to `true`, otherwise uses little endian * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.BE = function(bigEndian) { this.littleEndian = typeof bigEndian !== 'undefined' ? !bigEndian : false; return this; }; /** * Prepends some data to this ByteBuffer. This will overwrite any contents before the specified offset up to the * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer * will be resized and its contents moved accordingly. * @param {!ByteBuffer|string||!Buffer} source Data to prepend. If `source` is a ByteBuffer, its offset will be modified * according to the performed read operation. * @param {(string|number)=} encoding Encoding if `data` is a string ("base64", "hex", "binary", defaults to "utf8") * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes * prepended if omitted. * @returns {!ByteBuffer} this * @expose * @example A relative `00<01 02 03>.prepend(<04 05>)` results in `<04 05 01 02 03>, 04 05|` * @example An absolute `00<01 02 03>.prepend(<04 05>, 2)` results in `04<05 02 03>, 04 05|` */ ByteBufferPrototype.prepend = function(source, encoding, offset) { if (typeof encoding === 'number' || typeof encoding !== 'string') { offset = encoding; encoding = undefined; } var relative = typeof offset === 'undefined'; if (relative) offset = this.offset; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: "+offset+" (not an integer)"); offset >>>= 0; if (offset < 0 || offset + 0 > this.buffer.length) throw RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.length); } if (!(source instanceof ByteBuffer)) source = ByteBuffer.wrap(source, encoding); var len = source.limit - source.offset; if (len <= 0) return this; // Nothing to prepend var diff = len - offset; if (diff > 0) { // Not enough space before offset, so resize + move var buffer = new Buffer(this.buffer.length + diff); this.buffer.copy(buffer, len, offset, this.buffer.length); this.buffer = buffer; this.offset += diff; if (this.markedOffset >= 0) this.markedOffset += diff; this.limit += diff; offset += diff; } source.buffer.copy(this.buffer, offset - len, source.offset, source.limit); source.offset = source.limit; if (relative) this.offset -= len; return this; }; /** * Prepends this ByteBuffer to another ByteBuffer. This will overwrite any contents before the specified offset up to the * prepended data's length. If there is not enough space available before the specified `offset`, the backing buffer * will be resized and its contents moved accordingly. * @param {!ByteBuffer} target Target ByteBuffer * @param {number=} offset Offset to prepend at. Will use and decrease {@link ByteBuffer#offset} by the number of bytes * prepended if omitted. * @returns {!ByteBuffer} this * @expose * @see ByteBuffer#prepend */ ByteBufferPrototype.prependTo = function(target, offset) { target.prepend(this, offset); return this; }; /** * Prints debug information about this ByteBuffer's contents. * @param {function(string)=} out Output function to call, defaults to console.log * @expose */ ByteBufferPrototype.printDebug = function(out) { if (typeof out !== 'function') out = console.log.bind(console); out( this.toString()+"\n"+ "-------------------------------------------------------------------\n"+ this.toDebug(/* columns */ true) ); }; /** * Gets the number of remaining readable bytes. Contents are the bytes between {@link ByteBuffer#offset} and * {@link ByteBuffer#limit}, so this returns `limit - offset`. * @returns {number} Remaining readable bytes. May be negative if `offset > limit`. * @expose */ ByteBufferPrototype.remaining = function() { return this.limit - this.offset; }; /** * Resets this ByteBuffer's {@link ByteBuffer#offset}. If an offset has been marked through {@link ByteBuffer#mark} * before, `offset` will be set to {@link ByteBuffer#markedOffset}, which will then be discarded. If no offset has been * marked, sets `offset = 0`. * @returns {!ByteBuffer} this * @see ByteBuffer#mark * @expose */ ByteBufferPrototype.reset = function() { if (this.markedOffset >= 0) { this.offset = this.markedOffset; this.markedOffset = -1; } else { this.offset = 0; } return this; }; /** * Resizes this ByteBuffer to be backed by a buffer of at least the given capacity. Will do nothing if already that * large or larger. * @param {number} capacity Capacity required * @returns {!ByteBuffer} this * @throws {TypeError} If `capacity` is not a number * @throws {RangeError} If `capacity < 0` * @expose */ ByteBufferPrototype.resize = function(capacity) { if (!this.noAssert) { if (typeof capacity !== 'number' || capacity % 1 !== 0) throw TypeError("Illegal capacity: "+capacity+" (not an integer)"); capacity |= 0; if (capacity < 0) throw RangeError("Illegal capacity: 0 <= "+capacity); } if (this.buffer.length < capacity) { var buffer = new Buffer(capacity); this.buffer.copy(buffer); this.buffer = buffer; } return this; }; /** * Reverses this ByteBuffer's contents. * @param {number=} begin Offset to start at, defaults to {@link ByteBuffer#offset} * @param {number=} end Offset to end at, defaults to {@link ByteBuffer#limit} * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.reverse = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } if (begin === end) return this; // Nothing to reverse Array.prototype.reverse.call(this.buffer.slice(begin, end)); return this; }; /** * Skips the next `length` bytes. This will just advance * @param {number} length Number of bytes to skip. May also be negative to move the offset back. * @returns {!ByteBuffer} this * @expose */ ByteBufferPrototype.skip = function(length) { if (!this.noAssert) { if (typeof length !== 'number' || length % 1 !== 0) throw TypeError("Illegal length: "+length+" (not an integer)"); length |= 0; } var offset = this.offset + length; if (!this.noAssert) { if (offset < 0 || offset > this.buffer.length) throw RangeError("Illegal length: 0 <= "+this.offset+" + "+length+" <= "+this.buffer.length); } this.offset = offset; return this; }; /** * Slices this ByteBuffer by creating a cloned instance with `offset = begin` and `limit = end`. * @param {number=} begin Begin offset, defaults to {@link ByteBuffer#offset}. * @param {number=} end End offset, defaults to {@link ByteBuffer#limit}. * @returns {!ByteBuffer} Clone of this ByteBuffer with slicing applied, backed by the same {@link ByteBuffer#buffer} * @expose */ ByteBufferPrototype.slice = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } var bb = this.clone(); bb.offset = begin; bb.limit = end; return bb; }; /** * Returns a copy of the backing buffer that contains this ByteBuffer's contents. Contents are the bytes between * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. * @param {boolean=} forceCopy If `true` returns a copy, otherwise returns a view referencing the same memory if * possible. Defaults to `false` * @returns {!Buffer} Contents as a Buffer * @expose */ ByteBufferPrototype.toBuffer = function(forceCopy) { var offset = this.offset, limit = this.limit; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: Not an integer"); offset >>>= 0; if (typeof limit !== 'number' || limit % 1 !== 0) throw TypeError("Illegal limit: Not an integer"); limit >>>= 0; if (offset < 0 || offset > limit || limit > this.buffer.length) throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.length); } if (forceCopy) { var buffer = new Buffer(limit - offset); this.buffer.copy(buffer, 0, offset, limit); return buffer; } else { if (offset === 0 && limit === this.buffer.length) return this.buffer; else return this.buffer.slice(offset, limit); } }; /** * Returns a copy of the backing buffer compacted to contain this ByteBuffer's contents. Contents are the bytes between * {@link ByteBuffer#offset} and {@link ByteBuffer#limit}. * @returns {!ArrayBuffer} Contents as an ArrayBuffer */ ByteBufferPrototype.toArrayBuffer = function() { var offset = this.offset, limit = this.limit; if (!this.noAssert) { if (typeof offset !== 'number' || offset % 1 !== 0) throw TypeError("Illegal offset: Not an integer"); offset >>>= 0; if (typeof limit !== 'number' || limit % 1 !== 0) throw TypeError("Illegal limit: Not an integer"); limit >>>= 0; if (offset < 0 || offset > limit || limit > this.buffer.length) throw RangeError("Illegal range: 0 <= "+offset+" <= "+limit+" <= "+this.buffer.length); } var ab = new ArrayBuffer(limit - offset); if (memcpy) { // Fast memcpy(ab, 0, this.buffer, offset, limit); } else { // Slow var dst = new Uint8Array(ab); for (var i=offset; i this.capacity || begin > end) throw RangeError("begin, end"); return this.buffer.toString("base64", begin, end); }; /** * Decodes a base64 encoded string to a ByteBuffer. * @param {string} str String to decode * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @returns {!ByteBuffer} ByteBuffer * @expose */ ByteBuffer.fromBase64 = function(str, littleEndian) { return ByteBuffer.wrap(new Buffer(str, "base64"), littleEndian); return bb; }; /** * Encodes a binary string to base64 like `window.btoa` does. * @param {string} str Binary string * @returns {string} Base64 encoded string * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.btoa * @expose */ ByteBuffer.btoa = function(str) { return ByteBuffer.fromBinary(str).toBase64(); }; /** * Decodes a base64 encoded string to binary like `window.atob` does. * @param {string} b64 Base64 encoded string * @returns {string} Binary string * @see https://developer.mozilla.org/en-US/docs/Web/API/Window.atob * @expose */ ByteBuffer.atob = function(b64) { return ByteBuffer.fromBase64(b64).toBinary(); }; // encodings/binary /** * Encodes this ByteBuffer to a binary encoded string, that is using only characters 0x00-0xFF as bytes. * @param {number=} begin Offset to begin at. Defaults to {@link ByteBuffer#offset}. * @param {number=} end Offset to end at. Defaults to {@link ByteBuffer#limit}. * @returns {string} Binary encoded string * @throws {RangeError} If `offset > limit` * @expose */ ByteBufferPrototype.toBinary = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; begin |= 0; end |= 0; if (begin < 0 || end > this.capacity() || begin > end) throw RangeError("begin, end"); return this.buffer.toString("binary", begin, end); }; /** * Decodes a binary encoded string, that is using only characters 0x00-0xFF as bytes, to a ByteBuffer. * @param {string} str String to decode * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @returns {!ByteBuffer} ByteBuffer * @expose */ ByteBuffer.fromBinary = function(str, littleEndian) { return ByteBuffer.wrap(new Buffer(str, "binary"), littleEndian); return bb; }; // encodings/debug /** * Encodes this ByteBuffer to a hex encoded string with marked offsets. Offset symbols are: * * `<` : offset, * * `'` : markedOffset, * * `>` : limit, * * `|` : offset and limit, * * `[` : offset and markedOffset, * * `]` : markedOffset and limit, * * `!` : offset, markedOffset and limit * @param {boolean=} columns If `true` returns two columns hex + ascii, defaults to `false` * @returns {string|!Array.} Debug string or array of lines if `asArray = true` * @expose * @example `>00'01 02<03` contains four bytes with `limit=0, markedOffset=1, offset=3` * @example `00[01 02 03>` contains four bytes with `offset=markedOffset=1, limit=4` * @example `00|01 02 03` contains four bytes with `offset=limit=1, markedOffset=-1` * @example `|` contains zero bytes with `offset=limit=0, markedOffset=-1` */ ByteBufferPrototype.toDebug = function(columns) { var i = -1, k = this.buffer.length, b, hex = "", asc = "", out = ""; while (i 32 && b < 127 ? String.fromCharCode(b) : '.'; } ++i; if (columns) { if (i > 0 && i % 16 === 0 && i !== k) { while (hex.length < 3*16+3) hex += " "; out += hex+asc+"\n"; hex = asc = ""; } } if (i === this.offset && i === this.limit) hex += i === this.markedOffset ? "!" : "|"; else if (i === this.offset) hex += i === this.markedOffset ? "[" : "<"; else if (i === this.limit) hex += i === this.markedOffset ? "]" : ">"; else hex += i === this.markedOffset ? "'" : (columns || (i !== 0 && i !== k) ? " " : ""); } if (columns && hex !== " ") { while (hex.length < 3*16+3) hex += " "; out += hex + asc + "\n"; } return columns ? out : hex; }; /** * Decodes a hex encoded string with marked offsets to a ByteBuffer. * @param {string} str Debug string to decode (not be generated with `columns = true`) * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} ByteBuffer * @expose * @see ByteBuffer#toDebug */ ByteBuffer.fromDebug = function(str, littleEndian, noAssert) { var k = str.length, bb = new ByteBuffer(((k+1)/3)|0, littleEndian, noAssert); var i = 0, j = 0, ch, b, rs = false, // Require symbol next ho = false, hm = false, hl = false, // Already has offset (ho), markedOffset (hm), limit (hl)? fail = false; while (i': if (!noAssert) { if (hl) { fail = true; break; } hl = true; } bb.limit = j; rs = false; break; case "'": if (!noAssert) { if (hm) { fail = true; break; } hm = true; } bb.markedOffset = j; rs = false; break; case ' ': rs = false; break; default: if (!noAssert) { if (rs) { fail = true; break; } } b = parseInt(ch+str.charAt(i++), 16); if (!noAssert) { if (isNaN(b) || b < 0 || b > 255) throw TypeError("Illegal str: Not a debug encoded string"); } bb.buffer[j++] = b; rs = true; } if (fail) throw TypeError("Illegal str: Invalid symbol at "+i); } if (!noAssert) { if (!ho || !hl) throw TypeError("Illegal str: Missing offset or limit"); if (j>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } return this.buffer.toString("hex", begin, end); }; /** * Decodes a hex encoded string to a ByteBuffer. * @param {string} str String to decode * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} ByteBuffer * @expose */ ByteBuffer.fromHex = function(str, littleEndian, noAssert) { if (!noAssert) { if (typeof str !== 'string') throw TypeError("Illegal str: Not a string"); if (str.length % 2 !== 0) throw TypeError("Illegal str: Length not a multiple of 2"); } var bb = new ByteBuffer(0, littleEndian, true); bb.buffer = new Buffer(str, "hex"); bb.limit = bb.buffer.length; return bb; }; // utfx-embeddable /** * utfx-embeddable (c) 2014 Daniel Wirtz * Released under the Apache License, Version 2.0 * see: https://github.com/dcodeIO/utfx for details */ var utfx = function() { "use strict"; /** * utfx namespace. * @inner * @type {!Object.} */ var utfx = {}; /** * Maximum valid code point. * @type {number} * @const */ utfx.MAX_CODEPOINT = 0x10FFFF; /** * Encodes UTF8 code points to UTF8 bytes. * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point * respectively `null` if there are no more code points left or a single numeric code point. * @param {!function(number)} dst Bytes destination as a function successively called with the next byte */ utfx.encodeUTF8 = function(src, dst) { var cp = null; if (typeof src === 'number') cp = src, src = function() { return null; }; while (cp !== null || (cp = src()) !== null) { if (cp < 0x80) dst(cp&0x7F); else if (cp < 0x800) dst(((cp>>6)&0x1F)|0xC0), dst((cp&0x3F)|0x80); else if (cp < 0x10000) dst(((cp>>12)&0x0F)|0xE0), dst(((cp>>6)&0x3F)|0x80), dst((cp&0x3F)|0x80); else dst(((cp>>18)&0x07)|0xF0), dst(((cp>>12)&0x3F)|0x80), dst(((cp>>6)&0x3F)|0x80), dst((cp&0x3F)|0x80); cp = null; } }; /** * Decodes UTF8 bytes to UTF8 code points. * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there * are no more bytes left. * @param {!function(number)} dst Code points destination as a function successively called with each decoded code point. * @throws {RangeError} If a starting byte is invalid in UTF8 * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the * remaining bytes. */ utfx.decodeUTF8 = function(src, dst) { var a, b, c, d, fail = function(b) { b = b.slice(0, b.indexOf(null)); var err = Error(b.toString()); err.name = "TruncatedError"; err['bytes'] = b; throw err; }; while ((a = src()) !== null) { if ((a&0x80) === 0) dst(a); else if ((a&0xE0) === 0xC0) ((b = src()) === null) && fail([a, b]), dst(((a&0x1F)<<6) | (b&0x3F)); else if ((a&0xF0) === 0xE0) ((b=src()) === null || (c=src()) === null) && fail([a, b, c]), dst(((a&0x0F)<<12) | ((b&0x3F)<<6) | (c&0x3F)); else if ((a&0xF8) === 0xF0) ((b=src()) === null || (c=src()) === null || (d=src()) === null) && fail([a, b, c ,d]), dst(((a&0x07)<<18) | ((b&0x3F)<<12) | ((c&0x3F)<<6) | (d&0x3F)); else throw RangeError("Illegal starting byte: "+a); } }; /** * Converts UTF16 characters to UTF8 code points. * @param {!function():number|null} src Characters source as a function returning the next char code respectively * `null` if there are no more characters left. * @param {!function(number)} dst Code points destination as a function successively called with each converted code * point. */ utfx.UTF16toUTF8 = function(src, dst) { var c1, c2 = null; while (true) { if ((c1 = c2 !== null ? c2 : src()) === null) break; if (c1 >= 0xD800 && c1 <= 0xDFFF) { if ((c2 = src()) !== null) { if (c2 >= 0xDC00 && c2 <= 0xDFFF) { dst((c1-0xD800)*0x400+c2-0xDC00+0x10000); c2 = null; continue; } } } dst(c1); } if (c2 !== null) dst(c2); }; /** * Converts UTF8 code points to UTF16 characters. * @param {(!function():number|null) | number} src Code points source, either as a function returning the next code point * respectively `null` if there are no more code points left or a single numeric code point. * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. * @throws {RangeError} If a code point is out of range */ utfx.UTF8toUTF16 = function(src, dst) { var cp = null; if (typeof src === 'number') cp = src, src = function() { return null; }; while (cp !== null || (cp = src()) !== null) { if (cp <= 0xFFFF) dst(cp); else cp -= 0x10000, dst((cp>>10)+0xD800), dst((cp%0x400)+0xDC00); cp = null; } }; /** * Converts and encodes UTF16 characters to UTF8 bytes. * @param {!function():number|null} src Characters source as a function returning the next char code respectively `null` * if there are no more characters left. * @param {!function(number)} dst Bytes destination as a function successively called with the next byte. */ utfx.encodeUTF16toUTF8 = function(src, dst) { utfx.UTF16toUTF8(src, function(cp) { utfx.encodeUTF8(cp, dst); }); }; /** * Decodes and converts UTF8 bytes to UTF16 characters. * @param {!function():number|null} src Bytes source as a function returning the next byte respectively `null` if there * are no more bytes left. * @param {!function(number)} dst Characters destination as a function successively called with each converted char code. * @throws {RangeError} If a starting byte is invalid in UTF8 * @throws {Error} If the last sequence is truncated. Has an array property `bytes` holding the remaining bytes. */ utfx.decodeUTF8toUTF16 = function(src, dst) { utfx.decodeUTF8(src, function(cp) { utfx.UTF8toUTF16(cp, dst); }); }; /** * Calculates the byte length of an UTF8 code point. * @param {number} cp UTF8 code point * @returns {number} Byte length */ utfx.calculateCodePoint = function(cp) { return (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; }; /** * Calculates the number of UTF8 bytes required to store UTF8 code points. * @param {(!function():number|null)} src Code points source as a function returning the next code point respectively * `null` if there are no more code points left. * @returns {number} The number of UTF8 bytes required */ utfx.calculateUTF8 = function(src) { var cp, l=0; while ((cp = src()) !== null) l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; return l; }; /** * Calculates the number of UTF8 code points respectively UTF8 bytes required to store UTF16 char codes. * @param {(!function():number|null)} src Characters source as a function returning the next char code respectively * `null` if there are no more characters left. * @returns {!Array.} The number of UTF8 code points at index 0 and the number of UTF8 bytes required at index 1. */ utfx.calculateUTF16asUTF8 = function(src) { var n=0, l=0; utfx.UTF16toUTF8(src, function(cp) { ++n; l += (cp < 0x80) ? 1 : (cp < 0x800) ? 2 : (cp < 0x10000) ? 3 : 4; }); return [n,l]; }; return utfx; }(); // encodings/utf8 /** * Encodes this ByteBuffer's contents between {@link ByteBuffer#offset} and {@link ByteBuffer#limit} to an UTF8 encoded * string. * @returns {string} Hex encoded string * @throws {RangeError} If `offset > limit` * @expose */ ByteBufferPrototype.toUTF8 = function(begin, end) { if (typeof begin === 'undefined') begin = this.offset; if (typeof end === 'undefined') end = this.limit; if (!this.noAssert) { if (typeof begin !== 'number' || begin % 1 !== 0) throw TypeError("Illegal begin: Not an integer"); begin >>>= 0; if (typeof end !== 'number' || end % 1 !== 0) throw TypeError("Illegal end: Not an integer"); end >>>= 0; if (begin < 0 || begin > end || end > this.buffer.length) throw RangeError("Illegal range: 0 <= "+begin+" <= "+end+" <= "+this.buffer.length); } return this.buffer.toString("utf8", begin, end); }; /** * Decodes an UTF8 encoded string to a ByteBuffer. * @param {string} str String to decode * @param {boolean=} littleEndian Whether to use little or big endian byte order. Defaults to * {@link ByteBuffer.DEFAULT_ENDIAN}. * @param {boolean=} noAssert Whether to skip assertions of offsets and values. Defaults to * {@link ByteBuffer.DEFAULT_NOASSERT}. * @returns {!ByteBuffer} ByteBuffer * @expose */ ByteBuffer.fromUTF8 = function(str, littleEndian, noAssert) { if (!noAssert) if (typeof str !== 'string') throw TypeError("Illegal str: Not a string"); var bb = new ByteBuffer(0, littleEndian, noAssert); bb.buffer = new Buffer(str, "utf8"); bb.limit = bb.buffer.length; return bb; }; /** * node-memcpy. This is an optional binding dependency and may not be present. * @function * @param {!(Buffer|ArrayBuffer|Uint8Array)} target Destination * @param {number|!(Buffer|ArrayBuffer)} targetStart Destination start, defaults to 0. * @param {(!(Buffer|ArrayBuffer|Uint8Array)|number)=} source Source * @param {number=} sourceStart Source start, defaults to 0. * @param {number=} sourceEnd Source end, defaults to capacity. * @returns {number} Number of bytes copied * @throws {Error} If any index is out of bounds * @expose */ ByteBuffer.memcpy = memcpy; return ByteBuffer; })();