317 lines
7.5 KiB
JavaScript
317 lines
7.5 KiB
JavaScript
|
o1 = (function() {
|
||
|
|
||
|
// Float8
|
||
|
const exponentSize = 5;
|
||
|
const mantissaSize = 10;
|
||
|
const bias = Math.pow(2, exponentSize - 1) - 1;
|
||
|
|
||
|
// http://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript#8796597
|
||
|
function decodeFloat16 (binary) {
|
||
|
var exponent = (binary & 0x7C00) >> 10;
|
||
|
fraction = binary & 0x03FF;
|
||
|
return (binary >> 15 ? -1 : 1) * (
|
||
|
exponent ?
|
||
|
(
|
||
|
exponent === 0x1F ?
|
||
|
fraction ? NaN : Infinity :
|
||
|
Math.pow(2, exponent - 15) * (1 + fraction / 0x400)
|
||
|
) :
|
||
|
6.103515625e-5 * (fraction / 0x400)
|
||
|
);
|
||
|
};
|
||
|
|
||
|
function encodeFloat16(floating) {
|
||
|
let abs = Math.floor(Math.abs(floating));
|
||
|
let frac = Math.abs(floating) - abs;
|
||
|
let fraction = [];
|
||
|
|
||
|
let sign = Math.sign(floating) < 0;
|
||
|
|
||
|
// Compute the integral
|
||
|
let integral = new Number(abs).toString(2).split('').map((a) => a == 1);
|
||
|
|
||
|
if (integral.length === 1 && integral[0] === false)
|
||
|
integral = [];
|
||
|
|
||
|
// Compute the fraction
|
||
|
while (fraction.length < 16) {
|
||
|
frac *= 2;
|
||
|
fraction.push(frac >= 1);
|
||
|
if (frac >= 1) {
|
||
|
frac--;
|
||
|
}
|
||
|
if (frac == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Compute the exponent
|
||
|
let exponent = 0;
|
||
|
while (integral.length > 1) {
|
||
|
fraction = [integral.pop(), ...fraction];
|
||
|
exponent++;
|
||
|
}
|
||
|
while (integral.length < 1 || !integral[integral.length-1] && fraction.length > 0) {
|
||
|
integral = [...integral, fraction.shift()];
|
||
|
exponent--;
|
||
|
}
|
||
|
|
||
|
exponent += bias; // Math.pow(2, 5 - 1) - 1, the bias
|
||
|
|
||
|
// To array of bools
|
||
|
exponent = new Number(exponent).toString(2).split('').map((a) => a == 1);
|
||
|
|
||
|
// Pad the exponent with zeros
|
||
|
while (exponent.length < exponentSize) {
|
||
|
exponent = [false, ...exponent];
|
||
|
}
|
||
|
|
||
|
// Pad the fraction with zeros
|
||
|
while (fraction.length < mantissaSize) {
|
||
|
fraction.push(false);
|
||
|
}
|
||
|
fraction = fraction.slice(0, mantissaSize);
|
||
|
|
||
|
|
||
|
let result = [sign, ...exponent, ...fraction];
|
||
|
return parseInt(result.reduce((a,b) => a + (b ? '1' : '0'), ''), 2);
|
||
|
|
||
|
}
|
||
|
|
||
|
let o1 = {};
|
||
|
|
||
|
o1.encodeFloat16 = encodeFloat16;
|
||
|
o1.decodeFloat16 = decodeFloat16;
|
||
|
|
||
|
o1.IncompleteBufferError = class extends Error {};
|
||
|
|
||
|
function newCall(Cls) {
|
||
|
return new (Function.prototype.bind.apply(Cls, arguments));
|
||
|
}
|
||
|
|
||
|
o1.BinaryElement = class {
|
||
|
|
||
|
constructor(value) {
|
||
|
this.value = value;
|
||
|
}
|
||
|
|
||
|
loadFromBytes(buffers, offset) {
|
||
|
|
||
|
if (buffers instanceof ArrayBuffer) {
|
||
|
buffers = [buffers];
|
||
|
} else if (! buffers instanceof Array) {
|
||
|
throw new TypeError("Expecting buffer or array of buffers");
|
||
|
}
|
||
|
|
||
|
// Find correct buffer
|
||
|
let currentBufferIndex = 0;
|
||
|
|
||
|
while (offset >= buffers[currentBufferIndex].byteLength) {
|
||
|
offset -= buffers[currentBufferIndex].byteLength;
|
||
|
currentBufferIndex++;
|
||
|
|
||
|
if (buffers[currentBufferIndex] === undefined)
|
||
|
throw new o1.IncompleteBufferError();
|
||
|
}
|
||
|
|
||
|
let buffer = buffers[currentBufferIndex];
|
||
|
|
||
|
let view = new DataView(buffer, offset);
|
||
|
|
||
|
let bytes = new ArrayBuffer(this.size());
|
||
|
let bytesView = new DataView(bytes);
|
||
|
|
||
|
let offsetBetweenBuffers = 0;
|
||
|
|
||
|
for (let byteIndex = 0; byteIndex < this.size(); byteIndex++) {
|
||
|
|
||
|
if (byteIndex - offsetBetweenBuffers >= view.byteLength) {
|
||
|
|
||
|
// Change view
|
||
|
currentBufferIndex++;
|
||
|
let newBuffer = buffers[currentBufferIndex];
|
||
|
if (newBuffer === undefined) {
|
||
|
throw new o1.IncompleteBufferError();
|
||
|
}
|
||
|
|
||
|
view = new DataView(newBuffer);
|
||
|
offsetBetweenBuffers = byteIndex;
|
||
|
|
||
|
}
|
||
|
|
||
|
bytesView.setUint8(byteIndex, view.getUint8(byteIndex - offsetBetweenBuffers));
|
||
|
|
||
|
}
|
||
|
|
||
|
this.readFromView(bytesView);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.Uint8 = class extends o1.BinaryElement {
|
||
|
|
||
|
constructor(value) {
|
||
|
super(value);
|
||
|
}
|
||
|
|
||
|
size() {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
writeToView(view, offset = 0) {
|
||
|
view.setUint8(offset, this.value);
|
||
|
}
|
||
|
|
||
|
readFromView(view, offset) {
|
||
|
return this.value = view.getUint8(offset);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.Uint32 = class extends o1.BinaryElement {
|
||
|
|
||
|
constructor(value) {
|
||
|
super(value);
|
||
|
}
|
||
|
|
||
|
size() {
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
writeToView(view, offset = 0) {
|
||
|
view.setUint32(offset, this.value);
|
||
|
}
|
||
|
|
||
|
readFromView(view, offset) {
|
||
|
return this.value = view.getUint32(offset);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
o1.Float16 = class extends o1.BinaryElement {
|
||
|
|
||
|
constructor(value) {
|
||
|
super(value);
|
||
|
}
|
||
|
|
||
|
size() {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
writeToView(view, offset = 0) {
|
||
|
view.setUint16(offset, encodeFloat16(this.value));
|
||
|
}
|
||
|
|
||
|
readFromView(view, offset) {
|
||
|
let uint16 = view.getUint16(offset);
|
||
|
this.value = decodeFloat16(uint16);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.Float32 = class extends o1.BinaryElement {
|
||
|
|
||
|
constructor(value) {
|
||
|
super(value);
|
||
|
}
|
||
|
|
||
|
size() {
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
writeToView(view, offset = 0) {
|
||
|
view.setFloat32(offset, this.value);
|
||
|
}
|
||
|
|
||
|
readFromView(view, offset) {
|
||
|
return this.value = view.getFloat32(offset);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.BufferWriter = class {
|
||
|
|
||
|
constructor(size) {
|
||
|
this.buffer = new ArrayBuffer(size);
|
||
|
this.offset = 0;
|
||
|
}
|
||
|
|
||
|
write(element) {
|
||
|
|
||
|
element.writeToView(new DataView(this.buffer, this.offset));
|
||
|
this.offset += element.size();
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.BufferReader = class {
|
||
|
|
||
|
constructor(buffers) {
|
||
|
if (buffers instanceof ArrayBuffer) {
|
||
|
this.buffers = [buffers];
|
||
|
} else if (buffers instanceof Array) {
|
||
|
this.buffers = buffers;
|
||
|
} else {
|
||
|
throw new TypeError("Expexting buffer or array of buffers");
|
||
|
}
|
||
|
this.offset = 0;
|
||
|
this.array = [];
|
||
|
}
|
||
|
|
||
|
decodeElement(type) {
|
||
|
let elt = newCall(type);
|
||
|
elt.loadFromBytes(this.buffers, this.offset);
|
||
|
this.offset += elt.size();
|
||
|
this.array.push(elt);
|
||
|
return elt;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.decodeElement = function(buffer, offset, type) {
|
||
|
|
||
|
let elt = newCall(type);
|
||
|
elt.loadFromBytes(buffer, offset);
|
||
|
return elt;
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.encodeArray = function(array) {
|
||
|
|
||
|
let size = array.map((a) => a.size()).reduce((a,b) => a+b, 0);
|
||
|
|
||
|
let writer = new o1.BufferWriter(size);
|
||
|
|
||
|
for (let elt of array) {
|
||
|
writer.write(elt);
|
||
|
}
|
||
|
|
||
|
return writer.buffer;
|
||
|
|
||
|
}
|
||
|
|
||
|
o1.decodeArray = function(buffer, types) {
|
||
|
|
||
|
let reader = new o1.BufferReader(buffer);
|
||
|
|
||
|
for (let i = 0; i < types.length; i++) {
|
||
|
|
||
|
reader.decodeElement(types[i]);
|
||
|
|
||
|
}
|
||
|
|
||
|
return reader.array;
|
||
|
|
||
|
}
|
||
|
|
||
|
return o1;
|
||
|
|
||
|
})();
|
||
|
|
||
|
if (typeof module !== 'undefined' && module.exports) {
|
||
|
module.exports = o1;
|
||
|
}
|
||
|
|