Initial commit

This commit is contained in:
Thomas FORGIONE 2017-04-04 09:50:06 +02:00
commit ed16e84800
No known key found for this signature in database
GPG Key ID: 2A210FFC062E00C3
3 changed files with 361 additions and 0 deletions

29
example.js Normal file
View File

@ -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]);

316
o1.js Normal file
View File

@ -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;
}

16
package.json Normal file
View File

@ -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"
}