Initial commit
This commit is contained in:
commit
ed16e84800
|
@ -0,0 +1,29 @@
|
|||
const o1 = require('./o1.js');
|
||||
|
||||
let value = Math.PI;
|
||||
let value2 = Math.exp(1);
|
||||
|
||||
let buffer = o1.encodeArray([
|
||||
new o1.Float32(value),
|
||||
new o1.Float16(value2)
|
||||
]);
|
||||
|
||||
let buffView = new DataView(buffer);
|
||||
|
||||
let buff1 = new ArrayBuffer(3);
|
||||
let view1 = new DataView(buff1);
|
||||
view1.setUint8(0, buffView.getUint8(0));
|
||||
view1.setUint8(1, buffView.getUint8(1));
|
||||
view1.setUint8(2, buffView.getUint8(2));
|
||||
|
||||
let buff2 = new ArrayBuffer(1);
|
||||
let view2 = new DataView(buff2);
|
||||
view2.setUint8(0, buffView.getUint8(3));
|
||||
|
||||
let buff3 = new ArrayBuffer(2);
|
||||
let view3 = new DataView(buff3);
|
||||
view3.setUint8(0, buffView.getUint8(4));
|
||||
view3.setUint8(1, buffView.getUint8(5));
|
||||
|
||||
let elt = o1.decodeArray([buff1, buff2, buff3], [o1.Float32, o1.Float16]);
|
||||
console.log(elt[0], elt[1]);
|
|
@ -0,0 +1,316 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "node-o1",
|
||||
"version": "1.0.0",
|
||||
"description": "A small library for writing binary files",
|
||||
"main": "o1.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gogs.tforgione.fr/tforgione-phd/node-o1"
|
||||
},
|
||||
"keywords": [
|
||||
"node",
|
||||
"binary"
|
||||
],
|
||||
"author": "Thomas Forgione",
|
||||
"license": "MIT"
|
||||
}
|
Loading…
Reference in New Issue