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