2013-03-07 23:54:01 +08:00
|
|
|
var deprecate = require('deprecate');
|
|
|
|
|
2011-11-22 11:36:48 +08:00
|
|
|
var parseBits = function(data, bits, offset, invert, callback) {
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
2011-01-29 20:58:18 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
var parseFloatFromBits = function(data, precisionBits, exponentBits) {
|
2013-03-07 23:54:01 +08:00
|
|
|
deprecate('parsing and returning floats from PostgreSQL server is deprecated',
|
|
|
|
'JavaScript has a hard time with floats and there is precision loss which can cause',
|
|
|
|
'unexpected, hard to trace, potentially bad bugs in your program',
|
|
|
|
'for more information see the following:',
|
|
|
|
'https://github.com/brianc/node-postgres/pull/271',
|
|
|
|
'in node-postgres v1.0.0 all floats & decimals will be returned as strings');
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
2011-01-29 20:58:18 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
for (var i = 1; i <= bits; i++) {
|
|
|
|
precisionBitsCounter /= 2;
|
|
|
|
if ((newValue & (0x1 << (bits - i))) > 0) {
|
|
|
|
lastValue += precisionBitsCounter;
|
|
|
|
}
|
2011-01-29 20:58:18 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return lastValue;
|
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits);
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
// special cases
|
|
|
|
if (exponent == (Math.pow(2, exponentBits + 1) - 1)) {
|
|
|
|
if (mantissa === 0) {
|
|
|
|
return (sign === 0) ? Infinity : -Infinity;
|
2011-01-29 20:58:18 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return NaN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// normale number
|
|
|
|
return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa;
|
2011-01-29 20:58:18 +08:00
|
|
|
};
|
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseBool = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
return (parseBits(value, 8) == 1);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseInt16 = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
if (parseBits(value, 1) == 1) {
|
|
|
|
return -1 * (parseBits(value, 15, 1, true) + 1);
|
|
|
|
}
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return parseBits(value, 15, 1);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseInt32 = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
if (parseBits(value, 1) == 1) {
|
|
|
|
return -1 * (parseBits(value, 31, 1, true) + 1);
|
|
|
|
}
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return parseBits(value, 31, 1);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseInt64 = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
if (parseBits(value, 1) == 1) {
|
|
|
|
return -1 * (parseBits(value, 63, 1, true) + 1);
|
|
|
|
}
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return parseBits(value, 63, 1);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseFloat32 = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
return parseFloatFromBits(value, 23, 8);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseFloat64 = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
return parseFloatFromBits(value, 52, 11);
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseNumeric = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseDate = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
2011-11-21 18:40:17 +08:00
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseArray = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
|
|
|
}
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
var result;
|
|
|
|
if ((elementType == 0x17) || (elementType == 0x14)) {
|
|
|
|
// int/bigint
|
|
|
|
result = parseBits(value, length * 8, offset);
|
|
|
|
offset += length * 8;
|
|
|
|
return result;
|
2011-11-21 18:40:17 +08:00
|
|
|
}
|
2013-01-24 08:21:52 +08:00
|
|
|
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;
|
|
|
|
};
|
2011-01-29 20:58:18 +08:00
|
|
|
|
2013-01-24 08:21:52 +08:00
|
|
|
return parse(dims, elementType);
|
2011-01-29 20:58:18 +08:00
|
|
|
};
|
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseText = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
return value.toString('utf8');
|
2011-06-07 01:31:57 +08:00
|
|
|
};
|
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var parseBool = function(value) {
|
2013-01-24 08:21:52 +08:00
|
|
|
return (parseBits(value, 8) > 0);
|
2011-06-17 00:32:20 +08:00
|
|
|
};
|
|
|
|
|
2011-06-22 03:37:48 +08:00
|
|
|
var init = function(register) {
|
2013-01-24 08:21:52 +08:00
|
|
|
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);
|
2011-06-22 03:37:48 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
2013-01-24 08:21:52 +08:00
|
|
|
init: init
|
2011-06-22 03:37:48 +08:00
|
|
|
};
|