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; } var result; if ((elementType == 0x17) || (elementType == 0x14)) { // int/bigint result = parseBits(value, length * 8, offset); offset += length * 8; return result; } else if (elementType == 0x19) { // string 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 = []; var i; if (dimension.length > 1) { var count = dimension.shift(); for (i = 0; i < count; i++) { array[i] = parse(dimension, elementType); } dimension.unshift(count); } else { for (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 };