Created
October 1, 2015 09:44
-
-
Save ikorchynskyi/d260a9271b4a24d169a6 to your computer and use it in GitHub Desktop.
DataView (and ArrayBuffer) polyfill that works in any engine (including old IE).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void function(global){ | |
if ('DataView' in global && 'ArrayBuffer' in global) { | |
return; | |
} | |
var hide = (function(){ | |
// check if we're in ES5 | |
if (typeof Object.getOwnPropertyNames === 'function' && !('prototype' in Object.getOwnPropertyNames)) { | |
var hidden = { enumerable: false }; | |
return function(object, key){ | |
Object.defineProperty(object, key, hidden); | |
}; | |
} | |
// noop for ES3 | |
return function(){}; | |
})(); | |
function define(object, props){ | |
for (var key in props) { | |
object[key] = props[key]; | |
} | |
} | |
var ArrayBuffer = global.ArrayBuffer = (function(){ | |
var min = Math.min, | |
max = Math.max, | |
char = String.fromCharCode; | |
var chars = {}, | |
indices = []; | |
// create cached mapping of characters to char codes and back | |
void function(){ | |
for (var i = 0; i < 0x100; ++i) { | |
chars[indices[i] = char(i)] = i; | |
if (i >= 0x80) { | |
chars[char(0xf700 + i)] = i; | |
} | |
} | |
}(); | |
// read a string into an array of bytes | |
function readString(string){ | |
var array = [], | |
cycles = string.length % 8, | |
index = 0; | |
while (cycles--) { | |
array[index] = chars[string[index++]]; | |
} | |
cycles = string.length >> 3; | |
while (cycles--) { | |
array.push( | |
chars[string[index]], | |
chars[string[index+1]], | |
chars[string[index+2]], | |
chars[string[index+3]], | |
chars[string[index+4]], | |
chars[string[index+5]], | |
chars[string[index+6]], | |
chars[string[index+7]] | |
); | |
index += 8; | |
} | |
return array; | |
} | |
// write an array of bytes to a string | |
function writeString(array){ | |
try { return char.apply(null, array) } catch (e) {} | |
var string = '', | |
cycles = array.length % 8, | |
index = 0; | |
while (cycles--) { | |
string += indices[array[index++]]; | |
} | |
cycles = array.length >> 3; | |
while (cycles--) { | |
string += | |
indices[array[index]] + | |
indices[array[index+1]] + | |
indices[array[index+2]] + | |
indices[array[index+3]] + | |
indices[array[index+4]] + | |
indices[array[index+5]] + | |
indices[array[index+6]] + | |
indices[array[index+7]]; | |
index += 8; | |
} | |
return string; | |
} | |
// create a new array of given size where each element is 0 | |
function zerodArray(size){ | |
var data = new Array(size); | |
for (var i=0; i < size; i++) { | |
data[i] = 0; | |
} | |
return data; | |
} | |
// ################### | |
// ### ArrayBuffer ### | |
// ################### | |
function ArrayBuffer(length){ | |
if (length instanceof ArrayBuffer) { | |
this._data = length._data.slice(); | |
} else if (typeof length === 'string') { | |
this._data = readString(length); | |
} else { | |
if ((length >>= 0) < 0) { | |
throw new RangeError('ArrayBuffer length must be non-negative'); | |
} | |
this._data = zerodArray(length); | |
} | |
this.byteLength = this._data.length; | |
hide(this, '_data'); | |
} | |
define(ArrayBuffer, { | |
toByteString: function toByteString(arraybuffer){ | |
if (!(arraybuffer instanceof ArrayBuffer)) { | |
throw new TypeError('ArrayBuffer.toByteString requires an ArrayBuffer'); | |
} | |
return writeString(arraybuffer._data); | |
} | |
}); | |
define(ArrayBuffer.prototype, { | |
slice: function slice(begin, end){ | |
var arraybuffer = new ArrayBuffer(0); | |
arraybuffer._data = this._data.slice(begin, end); | |
arraybuffer.byteLength = arraybuffer._data.length; | |
return arraybuffer; | |
} | |
}); | |
return ArrayBuffer; | |
})(); | |
global.DataView = (function(){ | |
var log = Math.log, | |
pow = Math.pow, | |
LN2 = Math.LN2; | |
// Joyent copyright applies to readFloat and writeFloat | |
// Copyright Joyent, Inc. and other Node contributors. | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a | |
// copy of this software and associated documentation files (the | |
// "Software"), to deal in the Software without restriction, including | |
// without limitation the rights to use, copy, modify, merge, publish, | |
// distribute, sublicense, and/or sell copies of the Software, and to permit | |
// persons to whom the Software is furnished to do so, subject to the | |
// following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included | |
// in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN | |
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | |
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | |
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
// USE OR OTHER DEALINGS IN THE SOFTWARE. | |
function readFloat(dataview, offset, littleEndian, mLen, bytes){ | |
var buffer = dataview.buffer._data, | |
offset = dataview.byteOffset + offset, | |
e, m, | |
eLen = bytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
nBits = -7, | |
i = littleEndian ? bytes - 1 : 0 , | |
d = littleEndian ? -1 : 1, | |
s = buffer[offset + i]; | |
i += d; | |
e = s & ((1 << (-nBits)) - 1); | |
s >>= (-nBits); | |
nBits += eLen; | |
for (; nBits > 0; e = e * 0x100 + buffer[offset + i], i += d, nBits -= 8); | |
m = e & ((1 << (-nBits)) - 1); | |
e >>= (-nBits); | |
nBits += mLen; | |
for (; nBits > 0; m = m * 0x100 + buffer[offset + i], i += d, nBits -= 8); | |
if (e === 0) { | |
e = 1 - eBias; | |
} else if (e === eMax) { | |
return m ? NaN : s ? -Infinity : Infinity; | |
} else { | |
m = m + pow(2, mLen); | |
e = e - eBias; | |
} | |
return (s ? -1 : 1) * m * pow(2, e - mLen); | |
} | |
function writeFloat(dataview, offset, value, littleEndian, mLen, bytes){ | |
var buffer = dataview.buffer._data, | |
offset = dataview.byteOffset + offset, | |
e, m, c, | |
eLen = bytes * 8 - mLen - 1, | |
eMax = (1 << eLen) - 1, | |
eBias = eMax >> 1, | |
rt = (mLen === 23 ? pow(2, -24) - pow(2, -77) : 0), | |
i = littleEndian ? 0 : bytes - 1, | |
d = littleEndian ? 1 : -1, | |
s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; | |
value < 0 && (value = -value); | |
if (value !== value || value === Infinity) { | |
m = value !== value ? 1 : 0; | |
e = eMax; | |
} else { | |
e = (log(value) / LN2) | 0; | |
if (value * (c = pow(2, -e)) < 1) { | |
e--; | |
c *= 2; | |
} | |
if (e + eBias >= 1) { | |
value += rt / c; | |
} else { | |
value += rt * pow(2, 1 - eBias); | |
} | |
if (value * c >= 2) { | |
e++; | |
c /= 2; | |
} | |
if (e + eBias >= eMax) { | |
m = 0; | |
e = eMax; | |
} else if (e + eBias >= 1) { | |
m = (value * c - 1) * pow(2, mLen); | |
e = e + eBias; | |
} else { | |
m = value * pow(2, eBias - 1) * pow(2, mLen); | |
e = 0; | |
} | |
} | |
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 0x100, mLen -= 8); | |
e = (e << mLen) | m; | |
eLen += mLen; | |
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 0x100, eLen -= 8); | |
buffer[offset + i - d] |= s * 0x80; | |
} | |
var le2 = [1, 0], | |
le4 = [3, 2, 1, 0], | |
be2 = [0, 1], | |
be4 = [0, 1, 2, 3]; | |
function readUint8(dataview, byteOffset){ | |
var buffer = dataview.buffer._data, | |
offset = byteOffset + dataview.byteOffset; | |
return buffer[offset]; | |
} | |
function readUint16(dataview, byteOffset, littleEndian){ | |
var buffer = dataview.buffer._data, | |
offset = byteOffset + dataview.byteOffset, | |
order = littleEndian ? le2 : be2; | |
var b0 = buffer[offset + order[0]], | |
b1 = buffer[offset + order[1]] << 8; | |
return b0 | b1; | |
} | |
function readUint32(dataview, byteOffset, littleEndian){ | |
var buffer = dataview.buffer._data, | |
offset = byteOffset + dataview.byteOffset, | |
order = littleEndian ? le4 : be4; | |
var b0 = buffer[offset + order[0]], | |
b1 = buffer[offset + order[1]] << 8, | |
b2 = buffer[offset + order[2]] << 16, | |
b3 = buffer[offset + order[3]] << 24; | |
return b0 | b1 | b2 | b3; | |
} | |
function boundsCheck(offset, size, max){ | |
if (offset < 0) { | |
throw new RangeError('Tried to write to a negative index'); | |
} else if (offset + size > max) { | |
throw new RangeError('Tried to write '+size+' bytes past the end of a buffer at index '+offset+' of '+max); | |
} | |
} | |
function writeUint8(dataview, byteOffset, value){ | |
var buffer = dataview.buffer._data, | |
offset = byteOffset + dataview.byteOffset; | |
boundsCheck(offset, 1, buffer.length); | |
buffer[offset] = value & 0xff; | |
} | |
function writeUint16(dataview, byteOffset, value, littleEndian){ | |
var buffer = dataview.buffer._data, | |
order = littleEndian ? le2 : be2, | |
offset = byteOffset + dataview.byteOffset; | |
boundsCheck(offset, 2, buffer.length); | |
buffer[offset + order[0]] = value & 0xff; | |
buffer[offset + order[1]] = value >>> 8 & 0xff; | |
} | |
function writeUint32(dataview, byteOffset, value, littleEndian){ | |
var buffer = dataview.buffer._data, | |
order = littleEndian ? le4 : be4, | |
offset = byteOffset + dataview.byteOffset; | |
boundsCheck(offset, 4, buffer.length); | |
buffer[offset + order[0]] = value & 0xff; | |
buffer[offset + order[1]] = value >>> 8 & 0xff; | |
buffer[offset + order[2]] = value >>> 16 & 0xff; | |
buffer[offset + order[3]] = value >>> 24 & 0xff; | |
} | |
// ################ | |
// ### DataView ### | |
// ################ | |
function DataView(buffer, byteOffset, byteLength){ | |
if (!(buffer instanceof ArrayBuffer)) { | |
throw new TypeError('DataView must be initialized with an ArrayBuffer'); | |
} | |
if (byteOffset === undefined) { | |
this.byteOffset = buffer.byteOffset >> 0; | |
} else { | |
this.byteOffset = byteOffset >> 0; | |
} | |
if (this.byteOffset < 0) { | |
throw new RangeError('DataView byteOffset must be non-negative'); | |
} | |
if (byteLength === undefined) { | |
this.byteLength = (buffer.byteLength - this.byteOffset) >> 0; | |
} else { | |
this.byteLength = byteLength >> 0; | |
} | |
if (this.byteLength < 0) { | |
throw new RangeError('DataView byteLength must be non-negative'); | |
} | |
if (this.byteOffset + this.byteLength > buffer.byteLength) { | |
throw new RangeError('DataView byteOffset and byteLength greater than ArrayBuffer byteLength'); | |
} | |
this.buffer = buffer; | |
} | |
define(DataView.prototype, { | |
getFloat32: function getFloat32(byteOffset, littleEndian){ | |
return readFloat(this, byteOffset, littleEndian, 23, 4); | |
}, | |
getFloat64: function getFloat64(byteOffset, littleEndian){ | |
return readFloat(this, byteOffset, littleEndian, 52, 8); | |
}, | |
getInt8: function getInt8(byteOffset){ | |
var n = readUint8(this, byteOffset); | |
return n & 0x80 ? n ^ -0x100 : n; | |
}, | |
getInt16: function getInt16(byteOffset, littleEndian){ | |
var n = readUint16(this, byteOffset, littleEndian); | |
return n & 0x8000 ? n ^ -0x10000 : n; | |
}, | |
getInt32: function getInt32(byteOffset, littleEndian){ | |
var n = readUint32(this, byteOffset, littleEndian); | |
return n & 0x80000000 ? n ^ -0x100000000 : n; | |
}, | |
getUint8: function getUint8(byteOffset){ | |
return readUint8(this, byteOffset); | |
}, | |
getUint16: function getUint16(byteOffset, littleEndian){ | |
return readUint16(this, byteOffset, littleEndian); | |
}, | |
getUint32: function getUint32(byteOffset, littleEndian){ | |
return readUint32(this, byteOffset, littleEndian); | |
}, | |
setFloat32: function setFloat32(byteOffset, value, littleEndian){ | |
writeFloat(this, byteOffset, value, littleEndian, 23, 4); | |
}, | |
setFloat64: function setFloat64(byteOffset, value, littleEndian){ | |
writeFloat(this, byteOffset, value, littleEndian, 52, 8); | |
}, | |
setInt8: function setInt8(byteOffset, value){ | |
writeUint8(this, byteOffset, value < 0 ? value | 0x100 : value); | |
}, | |
setInt16: function setInt16(byteOffset, value, littleEndian){ | |
writeUint16(this, byteOffset, value < 0 ? value | 0x10000 : value, littleEndian); | |
}, | |
setInt32: function setInt32(byteOffset, value, littleEndian){ | |
writeUint32(this, byteOffset, value < 0 ? value | 0x100000000 : value, littleEndian); | |
}, | |
setUint8: function setUint8(byteOffset, value){ | |
writeUint8(this, byteOffset, value); | |
}, | |
setUint16: function setUint16(byteOffset, value, littleEndian){ | |
writeUint16(this, byteOffset, value, littleEndian); | |
}, | |
setUint32: function setUint32(byteOffset, value, littleEndian){ | |
writeUint32(this, byteOffset, value, littleEndian); | |
} | |
}); | |
return DataView; | |
})(); | |
}((0,eval)('this')); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment