binaryParsers.js
1 var parseInt64 = require('pg-int8'); 2 3 var parseBits = function(data, bits, offset, invert, callback) { 4 offset = offset || 0; 5 invert = invert || false; 6 callback = callback || function(lastValue, newValue, bits) { return (lastValue * Math.pow(2, bits)) + newValue; }; 7 var offsetBytes = offset >> 3; 8 9 var inv = function(value) { 10 if (invert) { 11 return ~value & 0xff; 12 } 13 14 return value; 15 }; 16 17 // read first (maybe partial) byte 18 var mask = 0xff; 19 var firstBits = 8 - (offset % 8); 20 if (bits < firstBits) { 21 mask = (0xff << (8 - bits)) & 0xff; 22 firstBits = bits; 23 } 24 25 if (offset) { 26 mask = mask >> (offset % 8); 27 } 28 29 var result = 0; 30 if ((offset % 8) + bits >= 8) { 31 result = callback(0, inv(data[offsetBytes]) & mask, firstBits); 32 } 33 34 // read bytes 35 var bytes = (bits + offset) >> 3; 36 for (var i = offsetBytes + 1; i < bytes; i++) { 37 result = callback(result, inv(data[i]), 8); 38 } 39 40 // bits to read, that are not a complete byte 41 var lastBits = (bits + offset) % 8; 42 if (lastBits > 0) { 43 result = callback(result, inv(data[bytes]) >> (8 - lastBits), lastBits); 44 } 45 46 return result; 47 }; 48 49 var parseFloatFromBits = function(data, precisionBits, exponentBits) { 50 var bias = Math.pow(2, exponentBits - 1) - 1; 51 var sign = parseBits(data, 1); 52 var exponent = parseBits(data, exponentBits, 1); 53 54 if (exponent === 0) { 55 return 0; 56 } 57 58 // parse mantissa 59 var precisionBitsCounter = 1; 60 var parsePrecisionBits = function(lastValue, newValue, bits) { 61 if (lastValue === 0) { 62 lastValue = 1; 63 } 64 65 for (var i = 1; i <= bits; i++) { 66 precisionBitsCounter /= 2; 67 if ((newValue & (0x1 << (bits - i))) > 0) { 68 lastValue += precisionBitsCounter; 69 } 70 } 71 72 return lastValue; 73 }; 74 75 var mantissa = parseBits(data, precisionBits, exponentBits + 1, false, parsePrecisionBits); 76 77 // special cases 78 if (exponent == (Math.pow(2, exponentBits + 1) - 1)) { 79 if (mantissa === 0) { 80 return (sign === 0) ? Infinity : -Infinity; 81 } 82 83 return NaN; 84 } 85 86 // normale number 87 return ((sign === 0) ? 1 : -1) * Math.pow(2, exponent - bias) * mantissa; 88 }; 89 90 var parseInt16 = function(value) { 91 if (parseBits(value, 1) == 1) { 92 return -1 * (parseBits(value, 15, 1, true) + 1); 93 } 94 95 return parseBits(value, 15, 1); 96 }; 97 98 var parseInt32 = function(value) { 99 if (parseBits(value, 1) == 1) { 100 return -1 * (parseBits(value, 31, 1, true) + 1); 101 } 102 103 return parseBits(value, 31, 1); 104 }; 105 106 var parseFloat32 = function(value) { 107 return parseFloatFromBits(value, 23, 8); 108 }; 109 110 var parseFloat64 = function(value) { 111 return parseFloatFromBits(value, 52, 11); 112 }; 113 114 var parseNumeric = function(value) { 115 var sign = parseBits(value, 16, 32); 116 if (sign == 0xc000) { 117 return NaN; 118 } 119 120 var weight = Math.pow(10000, parseBits(value, 16, 16)); 121 var result = 0; 122 123 var digits = []; 124 var ndigits = parseBits(value, 16); 125 for (var i = 0; i < ndigits; i++) { 126 result += parseBits(value, 16, 64 + (16 * i)) * weight; 127 weight /= 10000; 128 } 129 130 var scale = Math.pow(10, parseBits(value, 16, 48)); 131 return ((sign === 0) ? 1 : -1) * Math.round(result * scale) / scale; 132 }; 133 134 var parseDate = function(isUTC, value) { 135 var sign = parseBits(value, 1); 136 var rawValue = parseBits(value, 63, 1); 137 138 // discard usecs and shift from 2000 to 1970 139 var result = new Date((((sign === 0) ? 1 : -1) * rawValue / 1000) + 946684800000); 140 141 if (!isUTC) { 142 result.setTime(result.getTime() + result.getTimezoneOffset() * 60000); 143 } 144 145 // add microseconds to the date 146 result.usec = rawValue % 1000; 147 result.getMicroSeconds = function() { 148 return this.usec; 149 }; 150 result.setMicroSeconds = function(value) { 151 this.usec = value; 152 }; 153 result.getUTCMicroSeconds = function() { 154 return this.usec; 155 }; 156 157 return result; 158 }; 159 160 var parseArray = function(value) { 161 var dim = parseBits(value, 32); 162 163 var flags = parseBits(value, 32, 32); 164 var elementType = parseBits(value, 32, 64); 165 166 var offset = 96; 167 var dims = []; 168 for (var i = 0; i < dim; i++) { 169 // parse dimension 170 dims[i] = parseBits(value, 32, offset); 171 offset += 32; 172 173 // ignore lower bounds 174 offset += 32; 175 } 176 177 var parseElement = function(elementType) { 178 // parse content length 179 var length = parseBits(value, 32, offset); 180 offset += 32; 181 182 // parse null values 183 if (length == 0xffffffff) { 184 return null; 185 } 186 187 var result; 188 if ((elementType == 0x17) || (elementType == 0x14)) { 189 // int/bigint 190 result = parseBits(value, length * 8, offset); 191 offset += length * 8; 192 return result; 193 } 194 else if (elementType == 0x19) { 195 // string 196 result = value.toString(this.encoding, offset >> 3, (offset += (length << 3)) >> 3); 197 return result; 198 } 199 else { 200 console.log("ERROR: ElementType not implemented: " + elementType); 201 } 202 }; 203 204 var parse = function(dimension, elementType) { 205 var array = []; 206 var i; 207 208 if (dimension.length > 1) { 209 var count = dimension.shift(); 210 for (i = 0; i < count; i++) { 211 array[i] = parse(dimension, elementType); 212 } 213 dimension.unshift(count); 214 } 215 else { 216 for (i = 0; i < dimension[0]; i++) { 217 array[i] = parseElement(elementType); 218 } 219 } 220 221 return array; 222 }; 223 224 return parse(dims, elementType); 225 }; 226 227 var parseText = function(value) { 228 return value.toString('utf8'); 229 }; 230 231 var parseBool = function(value) { 232 if(value === null) return null; 233 return (parseBits(value, 8) > 0); 234 }; 235 236 var init = function(register) { 237 register(20, parseInt64); 238 register(21, parseInt16); 239 register(23, parseInt32); 240 register(26, parseInt32); 241 register(1700, parseNumeric); 242 register(700, parseFloat32); 243 register(701, parseFloat64); 244 register(16, parseBool); 245 register(1114, parseDate.bind(null, false)); 246 register(1184, parseDate.bind(null, true)); 247 register(1000, parseArray); 248 register(1007, parseArray); 249 register(1016, parseArray); 250 register(1008, parseArray); 251 register(1009, parseArray); 252 register(25, parseText); 253 }; 254 255 module.exports = { 256 init: init 257 };