Skip to content

Instantly share code, notes, and snippets.

@ikorchynskyi
Created October 1, 2015 09:44
Show Gist options
  • Save ikorchynskyi/d260a9271b4a24d169a6 to your computer and use it in GitHub Desktop.
Save ikorchynskyi/d260a9271b4a24d169a6 to your computer and use it in GitHub Desktop.
DataView (and ArrayBuffer) polyfill that works in any engine (including old IE).
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