239d8bd0c2
WARNING: bigint support is not correctly working for really big values. If the value of a integer gets big the number gets fuzzy in javascript. This is not a limitation of this library. If you want to handle bigint with the exact value, get it as string and do not calculate things with it!
259 lines
6.5 KiB
JavaScript
259 lines
6.5 KiB
JavaScript
var parseBits = function(data, bits, offset, invert, callback) {
|
|
offset = offset || 0;
|
|
invert = invert || false;
|
|
callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; };
|
|
var offsetBytes = offset >> 3;
|
|
|
|
var inv = function(value) {
|
|
if (invert) {
|
|
return ~value & 0xff;
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
// read first (maybe partial) byte
|
|
var mask = 0xff;
|
|
var firstBits = 8 - (offset % 8);
|
|
if (bits < firstBits) {
|
|
mask = (0xff << (8 - bits)) & 0xff;
|
|
firstBits = bits;
|
|
}
|
|
|
|
if (offset) {
|
|
mask = mask >> (offset % 8);
|
|
}
|
|
|
|
var result = 0;
|
|
if ((offset % 8) + bits >= 8) {
|
|
result = callback(0, inv(data[offsetBytes]) & mask, firstBits);
|
|
}
|
|
|
|
// read bytes
|
|
var bytes = (bits + offset) >> 3;
|
|
for (var i = offsetBytes + 1; i < bytes; i++) {
|
|
result = callback(result, inv(data[i]), 8);
|
|
}
|
|
|
|
// bits to read, that are not a complete byte
|
|
var lastBits = (bits + offset) % 8;
|
|
if (lastBits > 0) {
|
|
result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
var parseFloatFromBits = function(data, precisionBits, exponentBits) {
|
|
var bias = Math.pow(2, exponentBits - 1) - 1;
|
|
var sign = parseBits(data, 1);
|
|
var exponent = parseBits(data, exponentBits, 1);
|
|
|
|
if (exponent === 0)
|
|
return 0;
|
|
|
|
// parse mantissa
|
|
var precisionBitsCounter = 1;
|
|
var parsePrecisionBits = function(lastValue, newValue, bits) {
|
|
if (lastValue === 0) {
|
|
lastValue = 1;
|
|
}
|
|
|
|
for (var i = 1; i <= bits; i++) {
|
|
precisionBitsCounter /= 2;
|
|
if ((newValue & (0x1 << (bits - i))) > 0) {
|
|
lastValue += precisionBitsCounter;
|
|
}
|
|
}
|
|
|
|
return lastValue;
|
|
};
|
|
|
|
var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits);
|
|
|
|
// special cases
|
|
if (exponent == (Math.pow(2, exponentBits + 1) - 1)) {
|
|
if (mantissa === 0) {
|
|
return (sign === 0) ? Infinity : -Infinity;
|
|
}
|
|
|
|
return NaN;
|
|
}
|
|
|
|
// normale number
|
|
return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa;
|
|
};
|
|
|
|
var parseBool = function(value) {
|
|
return (parseBits(value, 8) == 1);
|
|
};
|
|
|
|
var parseInt16 = function(value) {
|
|
if (parseBits(value, 1) == 1) {
|
|
return -1 * (parseBits(value, 15, 1, true) + 1);
|
|
}
|
|
|
|
return parseBits(value, 15, 1);
|
|
};
|
|
|
|
var parseInt32 = function(value) {
|
|
if (parseBits(value, 1) == 1) {
|
|
return -1 * (parseBits(value, 31, 1, true) + 1);
|
|
}
|
|
|
|
return parseBits(value, 31, 1);
|
|
};
|
|
|
|
var parseInt64 = function(value) {
|
|
if (parseBits(value, 1) == 1) {
|
|
return -1 * (parseBits(value, 63, 1, true) + 1);
|
|
}
|
|
|
|
return parseBits(value, 63, 1);
|
|
};
|
|
|
|
var parseFloat32 = function(value) {
|
|
return parseFloatFromBits(value, 23, 8);
|
|
};
|
|
|
|
var parseFloat64 = function(value) {
|
|
return parseFloatFromBits(value, 52, 11);
|
|
};
|
|
|
|
var parseNumeric = function(value) {
|
|
var sign = parseBits(value, 16, 32);
|
|
if (sign == 0xc000) {
|
|
return NaN;
|
|
}
|
|
|
|
var weight = Math.pow(10000, parseBits(value, 16, 16));
|
|
var result = 0;
|
|
|
|
var digits = [];
|
|
var ndigits = parseBits(value, 16);
|
|
for (var i = 0; i < ndigits; i++) {
|
|
result += parseBits(value, 16, 64 + (16 * i)) * weight;
|
|
weight /= 10000;
|
|
}
|
|
|
|
var scale = Math.pow(10, parseBits(value, 16, 48));
|
|
return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale;
|
|
};
|
|
|
|
var parseDate = function(value) {
|
|
var sign = parseBits(value, 1);
|
|
var rawValue = parseBits(value, 63, 1);
|
|
|
|
// discard usecs and shift from 2000 to 1970
|
|
var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000);
|
|
|
|
// add microseconds to the date
|
|
result.usec = rawValue % 1000;
|
|
result.getMicroSeconds = function() {
|
|
return this.usec;
|
|
};
|
|
result.setMicroSeconds = function(value) {
|
|
this.usec = value;
|
|
};
|
|
result.getUTCMicroSeconds = function() {
|
|
return this.usec;
|
|
};
|
|
|
|
return result;
|
|
};
|
|
|
|
var parseArray = function(value) {
|
|
var dim = parseBits(value, 32);
|
|
|
|
var flags = parseBits(value, 32, 32);
|
|
var elementType = parseBits(value, 32, 64);
|
|
|
|
var offset = 96;
|
|
var dims = [];
|
|
for (var i = 0; i < dim; i++) {
|
|
// parse dimension
|
|
dims[i] = parseBits(value, 32, offset);
|
|
offset += 32;
|
|
|
|
// ignore lower bounds
|
|
offset += 32;
|
|
}
|
|
|
|
var parseElement = function(elementType) {
|
|
// parse content length
|
|
var length = parseBits(value, 32, offset);
|
|
offset += 32;
|
|
|
|
// parse null values
|
|
if (length == 0xffffffff) {
|
|
return null;
|
|
}
|
|
|
|
if ((elementType == 0x17) || (elementType == 0x14)) {
|
|
// int/bigint
|
|
var result = parseBits(value, length * 8, offset);
|
|
offset += length * 8;
|
|
return result;
|
|
}
|
|
else if (elementType == 0x19) {
|
|
// string
|
|
var result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3);
|
|
return result;
|
|
}
|
|
else {
|
|
console.log("ERROR: ElementType not implemented: " + elementType);
|
|
}
|
|
};
|
|
|
|
var parse = function(dimension, elementType) {
|
|
var array = [];
|
|
|
|
if (dimension.length > 1) {
|
|
var count = dimension.shift();
|
|
for (var i = 0; i < count; i++) {
|
|
array[i] = parse(dimension, elementType);
|
|
}
|
|
dimension.unshift(count);
|
|
}
|
|
else {
|
|
for (var i = 0; i < dimension[0]; i++) {
|
|
array[i] = parseElement(elementType);
|
|
}
|
|
}
|
|
|
|
return array;
|
|
};
|
|
|
|
return parse(dims, elementType);
|
|
};
|
|
|
|
var parseText = function(value) {
|
|
return value.toString('utf8');
|
|
};
|
|
|
|
var parseBool = function(value) {
|
|
return (parseBits(value, 8) > 0);
|
|
};
|
|
|
|
var init = function(register) {
|
|
register(20, parseInt64);
|
|
register(21, parseInt16);
|
|
register(23, parseInt32);
|
|
register(26, parseInt32);
|
|
register(1700, parseNumeric);
|
|
register(700, parseFloat32);
|
|
register(701, parseFloat64);
|
|
register(16, parseBool);
|
|
register(1114, parseDate);
|
|
register(1184, parseDate);
|
|
register(1007, parseArray);
|
|
register(1016, parseArray);
|
|
register(1008, parseArray);
|
|
register(1009, parseArray);
|
|
register(25, parseText);
|
|
};
|
|
|
|
module.exports = {
|
|
init: init
|
|
};
|