Skip to content

Instantly share code, notes, and snippets.

@mika76
Created September 26, 2015 21:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mika76/20b86c76afb77c35e0b4 to your computer and use it in GitHub Desktop.
Save mika76/20b86c76afb77c35e0b4 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'));
@segg21
Copy link

segg21 commented Jan 12, 2022

@mika76 ah ok I thought you made this.. I'm not an expert when it comes to bitwise, but I've been developing a browser game for the past 2 years and I have an issue with some older mobile device browsers not supporting 64bit, and I've been trying for the life of me make the most precise polyfill for 64bit. Guess my game just won't support older devices.

Thanks anyhow.

@mika76
Copy link
Author

mika76 commented Jan 12, 2022

@LegitSoulja The original one was made by https://gist.github.com/benvie, you could always ask him I guess, although looking at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#see_also there's some polyfills there that could be useful?

@segg21
Copy link

segg21 commented Jan 26, 2022

@mika76 Thanks. I have tried these other poly fills, but didn’t work the way expected.

I don’t think what I want to do is possible, atleast not without changing how my game works server & client side entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment