bytebuffer
Version:
The swiss army knife for binary data in JavaScript.
1,100 lines (1,023 loc) • 150 kB
JavaScript
/*
Copyright 2013-2014 Daniel Wirtz <dcode@dcode.io>
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) 2013-2014 Daniel Wirtz <dcode@dcode.io>
* This version of ByteBuffer.js uses an ArrayBuffer (AB) as its backing buffer and is compatible with modern browsers.
* Released under the Apache License, Version 2.0
* see: https://github.com/dcodeIO/ByteBuffer.js for details
*/ //
(function(global) {
"use strict";
/**
* @param {function(new: Long, number, number, boolean=)=} Long
* @returns {function(new: ByteBuffer, number=, boolean=, boolean=)}}
* @inner
*/
function loadByteBuffer(Long) {
/**
* 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 new RangeError("Illegal capacity: 0 <= "+capacity);
if (typeof littleEndian !== 'boolean')
throw new TypeError("Illegal littleEndian: Not a boolean");
if (typeof noAssert !== 'boolean')
throw new TypeError("Illegal noAssert: Not a boolean");
}
/**
* Backing buffer.
* @type {!ArrayBuffer}
* @expose
*/
this.buffer = capacity === 0 ? EMPTY_BUFFER : new ArrayBuffer(capacity);
/**
* Data view to manipulate the backing buffer. Becomes `null` if the backing buffer has a capacity of `0`.
* @type {?DataView}
* @expose
*/
this.view = capacity === 0 ? null : new DataView(this.buffer);
/**
* 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 = typeof littleEndian !== 'undefined' ? !!littleEndian : false;
/**
* 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 = "3.2.3";
/**
* 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. May be `null` if Long.js has not been loaded
* and int64 support is not available.
* @type {?Long}
* @const
* @see https://github.com/dcodeIO/Long.js
* @expose
*/
ByteBuffer.Long = Long || null;
// helpers
/**
* @type {!ArrayBuffer}
* @inner
*/
var EMPTY_BUFFER = new ArrayBuffer(0);
/**
* 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.<!ByteBuffer|!ArrayBuffer|!Uint8Array|string>} 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<k; ++i) {
if (!ByteBuffer.isByteBuffer(buffers[i]))
buffers[i] = ByteBuffer.wrap(buffers[i], encoding);
length = buffers[i].limit - buffers[i].offset;
if (length > 0) capacity += length;
}
if (capacity === 0) return new ByteBuffer(0, littleEndian, noAssert);
var bb = new ByteBuffer(capacity, littleEndian, noAssert),
bi;
var view = new Uint8Array(bb.buffer);
i=0; while (i<k) {
bi = buffers[i++];
length = bi.limit - bi.offset;
if (length <= 0) continue;
view.set(new Uint8Array(bi.buffer).subarray(bi.offset, bi.limit), bb.offset);
bb.offset += length;
}
bb.limit = bb.offset;
bb.offset = 0;
return bb;
};
/**
* Tests if the specified type is a ByteBuffer.
* @param {*} bb ByteBuffer to test
* @returns {boolean} `true` if it is a ByteBuffer, otherwise `false`
* @expose
*/
ByteBuffer.isByteBuffer = function(bb) {
return bb && bb instanceof ByteBuffer;
};
/**
* Gets the backing buffer type.
* @returns {Function} `Buffer` for NB builds, `ArrayBuffer` for AB builds (classes)
* @expose
*/
ByteBuffer.type = function() {
return ArrayBuffer;
};
/**
* Wraps a buffer or a string. Sets the allocated ByteBuffer's {@link ByteBuffer#offset} to `0` and its
* {@link ByteBuffer#limit} to the length of the wrapped data.
* @param {!ByteBuffer|!ArrayBuffer|!Uint8Array|string|!Array.<number>} 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 new TypeError("Unsupported encoding: "+encoding);
}
}
if (buffer === null || typeof buffer !== 'object')
throw new TypeError("Illegal buffer: null or non-object");
var bb;
if (ByteBuffer.isByteBuffer(buffer)) {
bb = ByteBuffer.prototype.clone.call(buffer);
bb.markedOffset = -1;
return bb;
}
if (buffer instanceof Uint8Array) { // Extract ArrayBuffer from Uint8Array
bb = new ByteBuffer(0, littleEndian, noAssert);
if (buffer.length > 0) { // Avoid references to more than one EMPTY_BUFFER
bb.buffer = buffer.buffer;
bb.offset = buffer.byteOffset;
bb.limit = buffer.byteOffset + buffer.length;
bb.view = buffer.length > 0 ? new DataView(buffer.buffer) : null;
}
} else if (buffer instanceof ArrayBuffer) { // Reuse ArrayBuffer
bb = new ByteBuffer(0, littleEndian, noAssert);
if (buffer.byteLength > 0) {
bb.buffer = buffer;
bb.offset = 0;
bb.limit = buffer.byteLength;
bb.view = buffer.byteLength > 0 ? new DataView(buffer) : null;
}
} else if (Object.prototype.toString.call(buffer) === "[object Array]") { // Create from octets
bb = new ByteBuffer(buffer.length, littleEndian, noAssert);
bb.limit = buffer.length;
for (i=0; i<buffer.length; ++i)
bb.view.setUint8(i, buffer[i]);
} else
throw new TypeError("Illegal buffer"); // Otherwise fail
return bb;
};
// 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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value |= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 1;
var capacity0 = this.buffer.byteLength;
if (offset > capacity0)
this.resize((capacity0 *= 2) > offset ? capacity0 : offset);
offset -= 1;
this.view.setInt8(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
*/
ByteBuffer.prototype.writeByte = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readInt8 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 1 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
}
var value = this.view.getInt8(offset);
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
*/
ByteBuffer.prototype.readByte = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value >>>= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 1;
var capacity1 = this.buffer.byteLength;
if (offset > capacity1)
this.resize((capacity1 *= 2) > offset ? capacity1 : offset);
offset -= 1;
this.view.setUint8(offset, value);
if (relative) this.offset += 1;
return this;
};
/**
* 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
*/
ByteBuffer.prototype.readUint8 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 1 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+1+") <= "+this.buffer.byteLength);
}
var value = this.view.getUint8(offset);
if (relative) this.offset += 1;
return value;
};
// 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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value |= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 2;
var capacity2 = this.buffer.byteLength;
if (offset > capacity2)
this.resize((capacity2 *= 2) > offset ? capacity2 : offset);
offset -= 2;
this.view.setInt16(offset, value, this.littleEndian);
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
*/
ByteBuffer.prototype.writeShort = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readInt16 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 2 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
}
var value = this.view.getInt16(offset, this.littleEndian);
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
*/
ByteBuffer.prototype.readShort = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value >>>= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 2;
var capacity3 = this.buffer.byteLength;
if (offset > capacity3)
this.resize((capacity3 *= 2) > offset ? capacity3 : offset);
offset -= 2;
this.view.setUint16(offset, value, this.littleEndian);
if (relative) this.offset += 2;
return this;
};
/**
* 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
*/
ByteBuffer.prototype.readUint16 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 2 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+2+") <= "+this.buffer.byteLength);
}
var value = this.view.getUint16(offset, this.littleEndian);
if (relative) this.offset += 2;
return value;
};
// 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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value |= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 4;
var capacity4 = this.buffer.byteLength;
if (offset > capacity4)
this.resize((capacity4 *= 2) > offset ? capacity4 : offset);
offset -= 4;
this.view.setInt32(offset, value, this.littleEndian);
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
*/
ByteBuffer.prototype.writeInt = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readInt32 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 4 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
}
var value = this.view.getInt32(offset, this.littleEndian);
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
*/
ByteBuffer.prototype.readInt = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value >>>= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 4;
var capacity5 = this.buffer.byteLength;
if (offset > capacity5)
this.resize((capacity5 *= 2) > offset ? capacity5 : offset);
offset -= 4;
this.view.setUint32(offset, value, this.littleEndian);
if (relative) this.offset += 4;
return this;
};
/**
* 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
*/
ByteBuffer.prototype.readUint32 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 4 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
}
var value = this.view.getUint32(offset, this.littleEndian);
if (relative) this.offset += 4;
return value;
};
// 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
*/
ByteBuffer.prototype.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 (!(value && value instanceof Long))
throw new TypeError("Illegal value: "+value+" (not an integer or Long)");
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
if (typeof value === 'number')
value = Long.fromNumber(value);
offset += 8;
var capacity6 = this.buffer.byteLength;
if (offset > capacity6)
this.resize((capacity6 *= 2) > offset ? capacity6 : offset);
offset -= 8;
if (this.littleEndian) {
this.view.setInt32(offset , value.low , true);
this.view.setInt32(offset+4, value.high, true);
} else {
this.view.setInt32(offset , value.high, false);
this.view.setInt32(offset+4, value.low , false);
}
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
*/
ByteBuffer.prototype.writeLong = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readInt64 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 8 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
}
var value = this.littleEndian
? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), false)
: new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), 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
*/
ByteBuffer.prototype.readLong = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.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 (!(value && value instanceof Long))
throw new TypeError("Illegal value: "+value+" (not an integer or Long)");
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
if (typeof value === 'number')
value = Long.fromNumber(value);
offset += 8;
var capacity7 = this.buffer.byteLength;
if (offset > capacity7)
this.resize((capacity7 *= 2) > offset ? capacity7 : offset);
offset -= 8;
if (this.littleEndian) {
this.view.setInt32(offset , value.low , true);
this.view.setInt32(offset+4, value.high, true);
} else {
this.view.setInt32(offset , value.high, false);
this.view.setInt32(offset+4, value.low , false);
}
if (relative) this.offset += 8;
return this;
};
/**
* 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
*/
ByteBuffer.prototype.readUint64 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 8 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
}
var value = this.littleEndian
? new Long(this.view.getInt32(offset , true ), this.view.getInt32(offset+4, true ), true)
: new Long(this.view.getInt32(offset+4, false), this.view.getInt32(offset , false), true);
if (relative) this.offset += 8;
return value;
};
} // 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
*/
ByteBuffer.prototype.writeFloat32 = function(value, offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof value !== 'number')
throw new TypeError("Illegal value: "+value+" (not a number)");
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 4;
var capacity8 = this.buffer.byteLength;
if (offset > capacity8)
this.resize((capacity8 *= 2) > offset ? capacity8 : offset);
offset -= 4;
this.view.setFloat32(offset, value, this.littleEndian);
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
*/
ByteBuffer.prototype.writeFloat = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readFloat32 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 4 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+4+") <= "+this.buffer.byteLength);
}
var value = this.view.getFloat32(offset, this.littleEndian);
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
*/
ByteBuffer.prototype.readFloat = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.writeFloat64 = function(value, offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof value !== 'number')
throw new TypeError("Illegal value: "+value+" (not a number)");
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
offset += 8;
var capacity9 = this.buffer.byteLength;
if (offset > capacity9)
this.resize((capacity9 *= 2) > offset ? capacity9 : offset);
offset -= 8;
this.view.setFloat64(offset, value, this.littleEndian);
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
*/
ByteBuffer.prototype.writeDouble = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.readFloat64 = function(offset) {
var relative = typeof offset === 'undefined';
if (relative) offset = this.offset;
if (!this.noAssert) {
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 8 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+8+") <= "+this.buffer.byteLength);
}
var value = this.view.getFloat64(offset, this.littleEndian);
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
*/
ByteBuffer.prototype.readDouble = ByteBuffer.prototype.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
*/
ByteBuffer.prototype.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 new TypeError("Illegal value: "+value+" (not an integer)");
value |= 0;
if (typeof offset !== 'number' || offset % 1 !== 0)
throw new TypeError("Illegal offset: "+offset+" (not an integer)");
offset >>>= 0;
if (offset < 0 || offset + 0 > this.buffer.byteLength)
throw new RangeError("Illegal offset: 0 <= "+offset+" (+"+0+") <= "+this.buffer.byteLength);
}
var size = ByteBuffer.calculateVarint32(value),
b;
offset += size;
var capacity10 = this.buffer.byteLength;
if (offset > capacity10)
this.resize((capacity10 *= 2) > offset ? capacity10 : offset);
offset -= size;
// ref: http://code.google.com/searchframe#WTeibokF6gE/trunk/src/google/protobuf/io/coded_stream.cc
this.view.setUint8(offset, b = value | 0x80);
value >>>= 0;
if (value >= 1 << 7) {
b = (value >> 7) | 0x80;
this.view.setUint8(offset+1, b);
if (value >= 1 << 14) {
b = (value >> 14) | 0x80;
this.view.setUint8(offset+2, b);
if (value >= 1 << 21) {
b = (value >> 21) | 0x80;
this.view.setUint8(offset+3, b);
if (value >= 1 << 28) {
this.view.setUint8(offset+4, (value >> 28) & 0x0F);
size = 5;
} else {
this.view.setUint8(offset+3, b & 0x7F);
size = 4;
}
} else {
this.view.setUint8(offset+2, b & 0x7F);
size = 3;
}
} else {
this.view.setUint8(offset+1, b & 0x7F);
size = 2;
}
} else {
this.view.setUint8(offset, b & 0x7F);
size = 1;
}
if (relative) {
this.offset += size;
return this;
}
return size;
};
/**
* Writes a zig-zag encoded 32bit base 128 variable-length integer.