848 lines
22 KiB
JavaScript
848 lines
22 KiB
JavaScript
// @ts-nocheck
|
|
//
|
|
// jDataView by Vjeux <vjeuxx@gmail.com> - Jan 2010
|
|
// Continued by RReverser <me@rreverser.com> - Feb 2013
|
|
//
|
|
// A unique way to work with a binary file in the browser
|
|
// http://github.com/jDataView/jDataView
|
|
// http://jDataView.github.io/
|
|
|
|
var compatibility = {
|
|
// NodeJS Buffer in v0.5.5 and newer
|
|
NodeBuffer: "Buffer" in globalThis && "readInt16LE" in Buffer.prototype,
|
|
DataView:
|
|
"DataView" in globalThis &&
|
|
("getFloat64" in DataView.prototype || // Chrome
|
|
"getFloat64" in new DataView(new ArrayBuffer(1))), // Node
|
|
ArrayBuffer: "ArrayBuffer" in globalThis,
|
|
PixelData:
|
|
"CanvasPixelArray" in globalThis &&
|
|
"ImageData" in globalThis &&
|
|
"document" in globalThis,
|
|
};
|
|
|
|
var createPixelData = function (byteLength, buffer) {
|
|
var data = createPixelData.context2d.createImageData(
|
|
(byteLength + 3) / 4,
|
|
1
|
|
).data;
|
|
data.byteLength = byteLength;
|
|
if (buffer !== undefined) {
|
|
for (var i = 0; i < byteLength; i++) {
|
|
data[i] = buffer[i];
|
|
}
|
|
}
|
|
return data;
|
|
};
|
|
createPixelData.context2d = document.createElement("canvas").getContext("2d");
|
|
|
|
var dataTypes = {
|
|
Int8: 1,
|
|
Int16: 2,
|
|
Int32: 4,
|
|
Uint8: 1,
|
|
Uint16: 2,
|
|
Uint32: 4,
|
|
Float32: 4,
|
|
Float64: 8,
|
|
};
|
|
|
|
var nodeNaming = {
|
|
Int8: "Int8",
|
|
Int16: "Int16",
|
|
Int32: "Int32",
|
|
Uint8: "UInt8",
|
|
Uint16: "UInt16",
|
|
Uint32: "UInt32",
|
|
Float32: "Float",
|
|
Float64: "Double",
|
|
};
|
|
|
|
function arrayFrom(arrayLike, forceCopy) {
|
|
return !forceCopy && arrayLike instanceof Array
|
|
? arrayLike
|
|
: Array.prototype.slice.call(arrayLike);
|
|
}
|
|
|
|
function defined(value, defaultValue) {
|
|
return value !== undefined ? value : defaultValue;
|
|
}
|
|
|
|
export function jDataView(buffer, byteOffset, byteLength, littleEndian) {
|
|
/* jshint validthis:true */
|
|
|
|
if (buffer instanceof jDataView) {
|
|
var result = buffer.slice(byteOffset, byteOffset + byteLength);
|
|
result._littleEndian = defined(littleEndian, result._littleEndian);
|
|
return result;
|
|
}
|
|
|
|
if (!(this instanceof jDataView)) {
|
|
return new jDataView(buffer, byteOffset, byteLength, littleEndian);
|
|
}
|
|
|
|
this.buffer = buffer = jDataView.wrapBuffer(buffer);
|
|
|
|
// Check parameters and existing functionnalities
|
|
this._isArrayBuffer =
|
|
compatibility.ArrayBuffer && buffer instanceof ArrayBuffer;
|
|
this._isPixelData =
|
|
compatibility.PixelData && buffer instanceof CanvasPixelArray;
|
|
this._isDataView = compatibility.DataView && this._isArrayBuffer;
|
|
this._isNodeBuffer = compatibility.NodeBuffer && buffer instanceof Buffer;
|
|
|
|
// Handle Type Errors
|
|
if (
|
|
!this._isNodeBuffer &&
|
|
!this._isArrayBuffer &&
|
|
!this._isPixelData &&
|
|
!(buffer instanceof Array)
|
|
) {
|
|
throw new TypeError("jDataView buffer has an incompatible type");
|
|
}
|
|
|
|
// Default Values
|
|
this._littleEndian = !!littleEndian;
|
|
|
|
var bufferLength = "byteLength" in buffer ? buffer.byteLength : buffer.length;
|
|
this.byteOffset = byteOffset = defined(byteOffset, 0);
|
|
this.byteLength = byteLength = defined(byteLength, bufferLength - byteOffset);
|
|
|
|
if (!this._isDataView) {
|
|
this._checkBounds(byteOffset, byteLength, bufferLength);
|
|
} else {
|
|
this._view = new DataView(buffer, byteOffset, byteLength);
|
|
}
|
|
|
|
// Create uniform methods (action wrappers) for the following data types
|
|
|
|
this._engineAction = this._isDataView
|
|
? this._dataViewAction
|
|
: this._isNodeBuffer
|
|
? this._nodeBufferAction
|
|
: this._isArrayBuffer
|
|
? this._arrayBufferAction
|
|
: this._arrayAction;
|
|
}
|
|
|
|
function getCharCodes(string) {
|
|
if (compatibility.NodeBuffer) {
|
|
return new Buffer(string, "binary");
|
|
}
|
|
|
|
var Type = compatibility.ArrayBuffer ? Uint8Array : Array,
|
|
codes = new Type(string.length);
|
|
|
|
for (var i = 0, length = string.length; i < length; i++) {
|
|
codes[i] = string.charCodeAt(i) & 0xff;
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
// mostly internal function for wrapping any supported input (String or Array-like) to best suitable buffer format
|
|
jDataView.wrapBuffer = function (buffer) {
|
|
switch (typeof buffer) {
|
|
case "number":
|
|
if (compatibility.NodeBuffer) {
|
|
buffer = new Buffer(buffer);
|
|
buffer.fill(0);
|
|
} else if (compatibility.ArrayBuffer) {
|
|
buffer = new Uint8Array(buffer).buffer;
|
|
} else if (compatibility.PixelData) {
|
|
buffer = createPixelData(buffer);
|
|
} else {
|
|
buffer = new Array(buffer);
|
|
for (var i = 0; i < buffer.length; i++) {
|
|
buffer[i] = 0;
|
|
}
|
|
}
|
|
return buffer;
|
|
|
|
case "string":
|
|
buffer = getCharCodes(buffer);
|
|
/* falls through */
|
|
default:
|
|
if (
|
|
"length" in buffer &&
|
|
!(
|
|
(compatibility.NodeBuffer && buffer instanceof Buffer) ||
|
|
(compatibility.ArrayBuffer && buffer instanceof ArrayBuffer) ||
|
|
(compatibility.PixelData && buffer instanceof CanvasPixelArray)
|
|
)
|
|
) {
|
|
if (compatibility.NodeBuffer) {
|
|
buffer = new Buffer(buffer);
|
|
} else if (compatibility.ArrayBuffer) {
|
|
if (!(buffer instanceof ArrayBuffer)) {
|
|
buffer = new Uint8Array(buffer).buffer;
|
|
// bug in Node.js <= 0.8:
|
|
if (!(buffer instanceof ArrayBuffer)) {
|
|
buffer = new Uint8Array(arrayFrom(buffer, true)).buffer;
|
|
}
|
|
}
|
|
} else if (compatibility.PixelData) {
|
|
buffer = createPixelData(buffer.length, buffer);
|
|
} else {
|
|
buffer = arrayFrom(buffer);
|
|
}
|
|
}
|
|
return buffer;
|
|
}
|
|
};
|
|
|
|
function pow2(n) {
|
|
return n >= 0 && n < 31 ? 1 << n : pow2[n] || (pow2[n] = Math.pow(2, n));
|
|
}
|
|
|
|
// left for backward compatibility
|
|
jDataView.createBuffer = function () {
|
|
return jDataView.wrapBuffer(arguments);
|
|
};
|
|
|
|
function Uint64(lo, hi) {
|
|
this.lo = lo;
|
|
this.hi = hi;
|
|
}
|
|
|
|
jDataView.Uint64 = Uint64;
|
|
|
|
Uint64.prototype = {
|
|
valueOf: function () {
|
|
return this.lo + pow2(32) * this.hi;
|
|
},
|
|
|
|
toString: function () {
|
|
return Number.prototype.toString.apply(this.valueOf(), arguments);
|
|
},
|
|
};
|
|
|
|
Uint64.fromNumber = function (number) {
|
|
var hi = Math.floor(number / pow2(32)),
|
|
lo = number - hi * pow2(32);
|
|
|
|
return new Uint64(lo, hi);
|
|
};
|
|
|
|
function Int64(lo, hi) {
|
|
Uint64.apply(this, arguments);
|
|
}
|
|
|
|
jDataView.Int64 = Int64;
|
|
|
|
Int64.prototype =
|
|
"create" in Object ? Object.create(Uint64.prototype) : new Uint64();
|
|
|
|
Int64.prototype.valueOf = function () {
|
|
if (this.hi < pow2(31)) {
|
|
return Uint64.prototype.valueOf.apply(this, arguments);
|
|
}
|
|
return -(pow2(32) - this.lo + pow2(32) * (pow2(32) - 1 - this.hi));
|
|
};
|
|
|
|
Int64.fromNumber = function (number) {
|
|
var lo, hi;
|
|
if (number >= 0) {
|
|
var unsigned = Uint64.fromNumber(number);
|
|
lo = unsigned.lo;
|
|
hi = unsigned.hi;
|
|
} else {
|
|
hi = Math.floor(number / pow2(32));
|
|
lo = number - hi * pow2(32);
|
|
hi += pow2(32);
|
|
}
|
|
return new Int64(lo, hi);
|
|
};
|
|
|
|
jDataView.prototype = {
|
|
_offset: 0,
|
|
_bitOffset: 0,
|
|
|
|
compatibility: compatibility,
|
|
|
|
_checkBounds: function (byteOffset, byteLength, maxLength) {
|
|
// Do additional checks to simulate DataView
|
|
if (typeof byteOffset !== "number") {
|
|
throw new TypeError("Offset is not a number.");
|
|
}
|
|
if (typeof byteLength !== "number") {
|
|
throw new TypeError("Size is not a number.");
|
|
}
|
|
if (byteLength < 0) {
|
|
throw new RangeError("Length is negative.");
|
|
}
|
|
if (
|
|
byteOffset < 0 ||
|
|
byteOffset + byteLength > defined(maxLength, this.byteLength)
|
|
) {
|
|
throw new RangeError("Offsets are out of bounds.");
|
|
}
|
|
},
|
|
|
|
_action: function (type, isReadAction, byteOffset, littleEndian, value) {
|
|
return this._engineAction(
|
|
type,
|
|
isReadAction,
|
|
defined(byteOffset, this._offset),
|
|
defined(littleEndian, this._littleEndian),
|
|
value
|
|
);
|
|
},
|
|
|
|
_dataViewAction: function (
|
|
type,
|
|
isReadAction,
|
|
byteOffset,
|
|
littleEndian,
|
|
value
|
|
) {
|
|
// Move the internal offset forward
|
|
this._offset = byteOffset + dataTypes[type];
|
|
return isReadAction
|
|
? this._view["get" + type](byteOffset, littleEndian)
|
|
: this._view["set" + type](byteOffset, value, littleEndian);
|
|
},
|
|
|
|
_nodeBufferAction: function (
|
|
type,
|
|
isReadAction,
|
|
byteOffset,
|
|
littleEndian,
|
|
value
|
|
) {
|
|
// Move the internal offset forward
|
|
this._offset = byteOffset + dataTypes[type];
|
|
var nodeName =
|
|
nodeNaming[type] +
|
|
(type === "Int8" || type === "Uint8" ? "" : littleEndian ? "LE" : "BE");
|
|
byteOffset += this.byteOffset;
|
|
return isReadAction
|
|
? this.buffer["read" + nodeName](byteOffset)
|
|
: this.buffer["write" + nodeName](value, byteOffset);
|
|
},
|
|
|
|
_arrayBufferAction: function (
|
|
type,
|
|
isReadAction,
|
|
byteOffset,
|
|
littleEndian,
|
|
value
|
|
) {
|
|
var size = dataTypes[type],
|
|
TypedArray = globalThis[type + "Array"],
|
|
typedArray;
|
|
|
|
littleEndian = defined(littleEndian, this._littleEndian);
|
|
|
|
// ArrayBuffer: we use a typed array of size 1 from original buffer if alignment is good and from slice when it's not
|
|
if (
|
|
size === 1 ||
|
|
((this.byteOffset + byteOffset) % size === 0 && littleEndian)
|
|
) {
|
|
typedArray = new TypedArray(this.buffer, this.byteOffset + byteOffset, 1);
|
|
this._offset = byteOffset + size;
|
|
return isReadAction ? typedArray[0] : (typedArray[0] = value);
|
|
} else {
|
|
var bytes = new Uint8Array(
|
|
isReadAction
|
|
? this.getBytes(size, byteOffset, littleEndian, true)
|
|
: size
|
|
);
|
|
typedArray = new TypedArray(bytes.buffer, 0, 1);
|
|
|
|
if (isReadAction) {
|
|
return typedArray[0];
|
|
} else {
|
|
typedArray[0] = value;
|
|
this._setBytes(byteOffset, bytes, littleEndian);
|
|
}
|
|
}
|
|
},
|
|
|
|
_arrayAction: function (type, isReadAction, byteOffset, littleEndian, value) {
|
|
return isReadAction
|
|
? this["_get" + type](byteOffset, littleEndian)
|
|
: this["_set" + type](byteOffset, value, littleEndian);
|
|
},
|
|
|
|
// Helpers
|
|
|
|
_getBytes: function (length, byteOffset, littleEndian) {
|
|
littleEndian = defined(littleEndian, this._littleEndian);
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
length = defined(length, this.byteLength - byteOffset);
|
|
|
|
this._checkBounds(byteOffset, length);
|
|
|
|
byteOffset += this.byteOffset;
|
|
|
|
this._offset = byteOffset - this.byteOffset + length;
|
|
|
|
var result = this._isArrayBuffer
|
|
? new Uint8Array(this.buffer, byteOffset, length)
|
|
: (this.buffer.slice || Array.prototype.slice).call(
|
|
this.buffer,
|
|
byteOffset,
|
|
byteOffset + length
|
|
);
|
|
|
|
return littleEndian || length <= 1 ? result : arrayFrom(result).reverse();
|
|
},
|
|
|
|
// wrapper for external calls (do not return inner buffer directly to prevent it's modifying)
|
|
getBytes: function (length, byteOffset, littleEndian, toArray) {
|
|
var result = this._getBytes(
|
|
length,
|
|
byteOffset,
|
|
defined(littleEndian, true)
|
|
);
|
|
return toArray ? arrayFrom(result) : result;
|
|
},
|
|
|
|
_setBytes: function (byteOffset, bytes, littleEndian) {
|
|
var length = bytes.length;
|
|
|
|
// needed for Opera
|
|
if (length === 0) {
|
|
return;
|
|
}
|
|
|
|
littleEndian = defined(littleEndian, this._littleEndian);
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
|
|
this._checkBounds(byteOffset, length);
|
|
|
|
if (!littleEndian && length > 1) {
|
|
bytes = arrayFrom(bytes, true).reverse();
|
|
}
|
|
|
|
byteOffset += this.byteOffset;
|
|
|
|
if (this._isArrayBuffer) {
|
|
new Uint8Array(this.buffer, byteOffset, length).set(bytes);
|
|
} else {
|
|
if (this._isNodeBuffer) {
|
|
new Buffer(bytes).copy(this.buffer, byteOffset);
|
|
} else {
|
|
for (var i = 0; i < length; i++) {
|
|
this.buffer[byteOffset + i] = bytes[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
this._offset = byteOffset - this.byteOffset + length;
|
|
},
|
|
|
|
setBytes: function (byteOffset, bytes, littleEndian) {
|
|
this._setBytes(byteOffset, bytes, defined(littleEndian, true));
|
|
},
|
|
|
|
getString: function (byteLength, byteOffset, encoding) {
|
|
if (this._isNodeBuffer) {
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
byteLength = defined(byteLength, this.byteLength - byteOffset);
|
|
|
|
this._checkBounds(byteOffset, byteLength);
|
|
|
|
this._offset = byteOffset + byteLength;
|
|
return this.buffer.toString(
|
|
encoding || "binary",
|
|
this.byteOffset + byteOffset,
|
|
this.byteOffset + this._offset
|
|
);
|
|
}
|
|
var bytes = this._getBytes(byteLength, byteOffset, true),
|
|
string = "";
|
|
byteLength = bytes.length;
|
|
for (var i = 0; i < byteLength; i++) {
|
|
string += String.fromCharCode(bytes[i]);
|
|
}
|
|
if (encoding === "utf8") {
|
|
string = decodeURIComponent(escape(string));
|
|
}
|
|
return string;
|
|
},
|
|
|
|
setString: function (byteOffset, subString, encoding) {
|
|
if (this._isNodeBuffer) {
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
this._checkBounds(byteOffset, subString.length);
|
|
this._offset =
|
|
byteOffset +
|
|
this.buffer.write(
|
|
subString,
|
|
this.byteOffset + byteOffset,
|
|
encoding || "binary"
|
|
);
|
|
return;
|
|
}
|
|
if (encoding === "utf8") {
|
|
subString = unescape(encodeURIComponent(subString));
|
|
}
|
|
this._setBytes(byteOffset, getCharCodes(subString), true);
|
|
},
|
|
|
|
getChar: function (byteOffset) {
|
|
return this.getString(1, byteOffset);
|
|
},
|
|
|
|
setChar: function (byteOffset, character) {
|
|
this.setString(byteOffset, character);
|
|
},
|
|
|
|
tell: function () {
|
|
return this._offset;
|
|
},
|
|
|
|
seek: function (byteOffset) {
|
|
this._checkBounds(byteOffset, 0);
|
|
/* jshint boss: true */
|
|
return (this._offset = byteOffset);
|
|
},
|
|
|
|
skip: function (byteLength) {
|
|
return this.seek(this._offset + byteLength);
|
|
},
|
|
|
|
slice: function (start, end, forceCopy) {
|
|
function normalizeOffset(offset, byteLength) {
|
|
return offset < 0 ? offset + byteLength : offset;
|
|
}
|
|
|
|
start = normalizeOffset(start, this.byteLength);
|
|
end = normalizeOffset(defined(end, this.byteLength), this.byteLength);
|
|
|
|
return forceCopy
|
|
? new jDataView(
|
|
this.getBytes(end - start, start, true, true),
|
|
undefined,
|
|
undefined,
|
|
this._littleEndian
|
|
)
|
|
: new jDataView(
|
|
this.buffer,
|
|
this.byteOffset + start,
|
|
end - start,
|
|
this._littleEndian
|
|
);
|
|
},
|
|
|
|
alignBy: function (byteCount) {
|
|
this._bitOffset = 0;
|
|
if (defined(byteCount, 1) !== 1) {
|
|
return this.skip(byteCount - (this._offset % byteCount || byteCount));
|
|
} else {
|
|
return this._offset;
|
|
}
|
|
},
|
|
|
|
// Compatibility functions
|
|
|
|
_getFloat64: function (byteOffset, littleEndian) {
|
|
var b = this._getBytes(8, byteOffset, littleEndian),
|
|
sign = 1 - 2 * (b[7] >> 7),
|
|
exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1),
|
|
// Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead
|
|
mantissa =
|
|
(b[6] & 0x0f) * pow2(48) +
|
|
b[5] * pow2(40) +
|
|
b[4] * pow2(32) +
|
|
b[3] * pow2(24) +
|
|
b[2] * pow2(16) +
|
|
b[1] * pow2(8) +
|
|
b[0];
|
|
|
|
if (exponent === 1024) {
|
|
if (mantissa !== 0) {
|
|
return NaN;
|
|
} else {
|
|
return sign * Infinity;
|
|
}
|
|
}
|
|
|
|
if (exponent === -1023) {
|
|
// Denormalized
|
|
return sign * mantissa * pow2(-1022 - 52);
|
|
}
|
|
|
|
return sign * (1 + mantissa * pow2(-52)) * pow2(exponent);
|
|
},
|
|
|
|
_getFloat32: function (byteOffset, littleEndian) {
|
|
var b = this._getBytes(4, byteOffset, littleEndian),
|
|
sign = 1 - 2 * (b[3] >> 7),
|
|
exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127,
|
|
mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0];
|
|
|
|
if (exponent === 128) {
|
|
if (mantissa !== 0) {
|
|
return NaN;
|
|
} else {
|
|
return sign * Infinity;
|
|
}
|
|
}
|
|
|
|
if (exponent === -127) {
|
|
// Denormalized
|
|
return sign * mantissa * pow2(-126 - 23);
|
|
}
|
|
|
|
return sign * (1 + mantissa * pow2(-23)) * pow2(exponent);
|
|
},
|
|
|
|
_get64: function (Type, byteOffset, littleEndian) {
|
|
littleEndian = defined(littleEndian, this._littleEndian);
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
|
|
var parts = littleEndian ? [0, 4] : [4, 0];
|
|
|
|
for (var i = 0; i < 2; i++) {
|
|
parts[i] = this.getUint32(byteOffset + parts[i], littleEndian);
|
|
}
|
|
|
|
this._offset = byteOffset + 8;
|
|
|
|
return new Type(parts[0], parts[1]);
|
|
},
|
|
|
|
getInt64: function (byteOffset, littleEndian) {
|
|
return this._get64(Int64, byteOffset, littleEndian);
|
|
},
|
|
|
|
getUint64: function (byteOffset, littleEndian) {
|
|
return this._get64(Uint64, byteOffset, littleEndian);
|
|
},
|
|
|
|
_getInt32: function (byteOffset, littleEndian) {
|
|
var b = this._getBytes(4, byteOffset, littleEndian);
|
|
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
|
|
},
|
|
|
|
_getUint32: function (byteOffset, littleEndian) {
|
|
return this._getInt32(byteOffset, littleEndian) >>> 0;
|
|
},
|
|
|
|
_getInt16: function (byteOffset, littleEndian) {
|
|
return (this._getUint16(byteOffset, littleEndian) << 16) >> 16;
|
|
},
|
|
|
|
_getUint16: function (byteOffset, littleEndian) {
|
|
var b = this._getBytes(2, byteOffset, littleEndian);
|
|
return (b[1] << 8) | b[0];
|
|
},
|
|
|
|
_getInt8: function (byteOffset) {
|
|
return (this._getUint8(byteOffset) << 24) >> 24;
|
|
},
|
|
|
|
_getUint8: function (byteOffset) {
|
|
return this._getBytes(1, byteOffset)[0];
|
|
},
|
|
|
|
_getBitRangeData: function (bitLength, byteOffset) {
|
|
var startBit = (defined(byteOffset, this._offset) << 3) + this._bitOffset,
|
|
endBit = startBit + bitLength,
|
|
start = startBit >>> 3,
|
|
end = (endBit + 7) >>> 3,
|
|
b = this._getBytes(end - start, start, true),
|
|
wideValue = 0;
|
|
|
|
/* jshint boss: true */
|
|
if ((this._bitOffset = endBit & 7)) {
|
|
this._bitOffset -= 8;
|
|
}
|
|
|
|
for (var i = 0, length = b.length; i < length; i++) {
|
|
wideValue = (wideValue << 8) | b[i];
|
|
}
|
|
|
|
return {
|
|
start: start,
|
|
bytes: b,
|
|
wideValue: wideValue,
|
|
};
|
|
},
|
|
|
|
getSigned: function (bitLength, byteOffset) {
|
|
var shift = 32 - bitLength;
|
|
return (this.getUnsigned(bitLength, byteOffset) << shift) >> shift;
|
|
},
|
|
|
|
getUnsigned: function (bitLength, byteOffset) {
|
|
var value =
|
|
this._getBitRangeData(bitLength, byteOffset).wideValue >>>
|
|
-this._bitOffset;
|
|
return bitLength < 32 ? value & ~(-1 << bitLength) : value;
|
|
},
|
|
|
|
_setBinaryFloat: function (
|
|
byteOffset,
|
|
value,
|
|
mantSize,
|
|
expSize,
|
|
littleEndian
|
|
) {
|
|
var signBit = value < 0 ? 1 : 0,
|
|
exponent,
|
|
mantissa,
|
|
eMax = ~(-1 << (expSize - 1)),
|
|
eMin = 1 - eMax;
|
|
|
|
if (value < 0) {
|
|
value = -value;
|
|
}
|
|
|
|
if (value === 0) {
|
|
exponent = 0;
|
|
mantissa = 0;
|
|
} else if (isNaN(value)) {
|
|
exponent = 2 * eMax + 1;
|
|
mantissa = 1;
|
|
} else if (value === Infinity) {
|
|
exponent = 2 * eMax + 1;
|
|
mantissa = 0;
|
|
} else {
|
|
exponent = Math.floor(Math.log(value) / Math.LN2);
|
|
if (exponent >= eMin && exponent <= eMax) {
|
|
mantissa = Math.floor((value * pow2(-exponent) - 1) * pow2(mantSize));
|
|
exponent += eMax;
|
|
} else {
|
|
mantissa = Math.floor(value / pow2(eMin - mantSize));
|
|
exponent = 0;
|
|
}
|
|
}
|
|
|
|
var b = [];
|
|
while (mantSize >= 8) {
|
|
b.push(mantissa % 256);
|
|
mantissa = Math.floor(mantissa / 256);
|
|
mantSize -= 8;
|
|
}
|
|
exponent = (exponent << mantSize) | mantissa;
|
|
expSize += mantSize;
|
|
while (expSize >= 8) {
|
|
b.push(exponent & 0xff);
|
|
exponent >>>= 8;
|
|
expSize -= 8;
|
|
}
|
|
b.push((signBit << expSize) | exponent);
|
|
|
|
this._setBytes(byteOffset, b, littleEndian);
|
|
},
|
|
|
|
_setFloat32: function (byteOffset, value, littleEndian) {
|
|
this._setBinaryFloat(byteOffset, value, 23, 8, littleEndian);
|
|
},
|
|
|
|
_setFloat64: function (byteOffset, value, littleEndian) {
|
|
this._setBinaryFloat(byteOffset, value, 52, 11, littleEndian);
|
|
},
|
|
|
|
_set64: function (Type, byteOffset, value, littleEndian) {
|
|
if (!(value instanceof Type)) {
|
|
value = Type.fromNumber(value);
|
|
}
|
|
|
|
littleEndian = defined(littleEndian, this._littleEndian);
|
|
byteOffset = defined(byteOffset, this._offset);
|
|
|
|
var parts = littleEndian ? { lo: 0, hi: 4 } : { lo: 4, hi: 0 };
|
|
|
|
for (var partName in parts) {
|
|
this.setUint32(
|
|
byteOffset + parts[partName],
|
|
value[partName],
|
|
littleEndian
|
|
);
|
|
}
|
|
|
|
this._offset = byteOffset + 8;
|
|
},
|
|
|
|
setInt64: function (byteOffset, value, littleEndian) {
|
|
this._set64(Int64, byteOffset, value, littleEndian);
|
|
},
|
|
|
|
setUint64: function (byteOffset, value, littleEndian) {
|
|
this._set64(Uint64, byteOffset, value, littleEndian);
|
|
},
|
|
|
|
_setUint32: function (byteOffset, value, littleEndian) {
|
|
this._setBytes(
|
|
byteOffset,
|
|
[value & 0xff, (value >>> 8) & 0xff, (value >>> 16) & 0xff, value >>> 24],
|
|
littleEndian
|
|
);
|
|
},
|
|
|
|
_setUint16: function (byteOffset, value, littleEndian) {
|
|
this._setBytes(
|
|
byteOffset,
|
|
[value & 0xff, (value >>> 8) & 0xff],
|
|
littleEndian
|
|
);
|
|
},
|
|
|
|
_setUint8: function (byteOffset, value) {
|
|
this._setBytes(byteOffset, [value & 0xff]);
|
|
},
|
|
|
|
setUnsigned: function (byteOffset, value, bitLength) {
|
|
var data = this._getBitRangeData(bitLength, byteOffset),
|
|
wideValue = data.wideValue,
|
|
b = data.bytes;
|
|
|
|
wideValue &= ~(~(-1 << bitLength) << -this._bitOffset); // clearing bit range before binary "or"
|
|
wideValue |=
|
|
(bitLength < 32 ? value & ~(-1 << bitLength) : value) << -this._bitOffset; // setting bits
|
|
|
|
for (var i = b.length - 1; i >= 0; i--) {
|
|
b[i] = wideValue & 0xff;
|
|
wideValue >>>= 8;
|
|
}
|
|
|
|
this._setBytes(data.start, b, true);
|
|
},
|
|
};
|
|
|
|
var proto = jDataView.prototype;
|
|
|
|
for (var type in dataTypes) {
|
|
(function (type) {
|
|
proto["get" + type] = function (byteOffset, littleEndian) {
|
|
return this._action(type, true, byteOffset, littleEndian);
|
|
};
|
|
proto["set" + type] = function (byteOffset, value, littleEndian) {
|
|
this._action(type, false, byteOffset, littleEndian, value);
|
|
};
|
|
})(type);
|
|
}
|
|
|
|
proto._setInt32 = proto._setUint32;
|
|
proto._setInt16 = proto._setUint16;
|
|
proto._setInt8 = proto._setUint8;
|
|
proto.setSigned = proto.setUnsigned;
|
|
|
|
for (var method in proto) {
|
|
if (method.slice(0, 3) === "set") {
|
|
(function (type) {
|
|
proto["write" + type] = function () {
|
|
Array.prototype.unshift.call(arguments, undefined);
|
|
this["set" + type].apply(this, arguments);
|
|
};
|
|
})(method.slice(3));
|
|
}
|
|
}
|
|
|
|
if (typeof module !== "undefined" && typeof module.exports === "object") {
|
|
module.exports = jDataView;
|
|
} else if (typeof define === "function" && define.amd) {
|
|
define([], function () {
|
|
return jDataView;
|
|
});
|
|
} else {
|
|
var oldGlobalThis = globalThis.jDataView;
|
|
(globalThis.jDataView = jDataView).noConflict = function () {
|
|
globalThis.jDataView = oldGlobalThis;
|
|
return this;
|
|
};
|
|
}
|