packet.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. //var BigNumber = require("bignumber.js");
  2. var ErrorCodeToName = require('../constants/errors.js').codeToName;
  3. var bn = require('bn.js');
  4. function Packet(id, buffer, start, end)
  5. {
  6. this.sequenceId = id;
  7. this.buffer = buffer;
  8. this.start = start;
  9. this.offset = start;
  10. this.end = end;
  11. }
  12. // ==============================
  13. // readers
  14. // ==============================
  15. Packet.prototype.reset = function() {
  16. this.offset = this.start;
  17. };
  18. Packet.prototype.length = function() {
  19. return this.end - this.start;
  20. };
  21. Packet.prototype.slice = function() {
  22. return this.buffer.slice(this.start, this.end);
  23. };
  24. Packet.prototype.dump = function() {
  25. console.log([this.buffer.asciiSlice(this.start, this.end)], this.buffer.slice(this.start, this.end), this.length(), this.sequenceId);
  26. };
  27. Packet.prototype.haveMoreData = function() {
  28. return this.end > this.offset;
  29. };
  30. Packet.prototype.skip = function(num) {
  31. this.offset += num;
  32. };
  33. Packet.prototype.readInt8 = function()
  34. {
  35. return this.buffer[this.offset++];
  36. };
  37. Packet.prototype.readInt16 = function()
  38. {
  39. this.offset += 2;
  40. return this.buffer.readUInt16LE(this.offset - 2, true);
  41. };
  42. Packet.prototype.readInt24 = function() {
  43. return this.readInt16() + (this.readInt8() << 16);
  44. };
  45. Packet.prototype.readInt32 = function()
  46. {
  47. this.offset += 4;
  48. return this.buffer.readUInt32LE(this.offset - 4, true);
  49. };
  50. Packet.prototype.readSInt8 = function()
  51. {
  52. return this.buffer.readInt8(this.offset++, true);
  53. };
  54. Packet.prototype.readSInt16 = function()
  55. {
  56. this.offset += 2;
  57. return this.buffer.readInt16LE(this.offset - 2, true);
  58. };
  59. Packet.prototype.readSInt32 = function()
  60. {
  61. this.offset += 4;
  62. return this.buffer.readInt32LE(this.offset - 4, true);
  63. };
  64. Packet.prototype.readInt64 = function() {
  65. return this.readInt32() + 0x100000000*this.readInt32();
  66. };
  67. Packet.prototype.readSInt64 = function() {
  68. var word0 = this.readInt32();
  69. var word1 = this.readInt32();
  70. if (!(word1 & 0x80000000))
  71. return word0 + 0x100000000*word1;
  72. return -((((~word1)>>>0) * 0x100000000) + ((~word0)>>>0) + 1);
  73. };
  74. Packet.prototype.isEOF = function() {
  75. return this.buffer[this.offset] == 0xfe && this.length() < 9;
  76. };
  77. Packet.prototype.eofStatusFlags = function() {
  78. return this.buffer.readInt16LE(this.offset + 3);
  79. };
  80. Packet.prototype.eofWarningCount = function() {
  81. return this.buffer.readInt16LE(this.offset + 1);
  82. };
  83. Packet.prototype.readLengthCodedNumber = function(bigNumberStrings) {
  84. var byte1 = this.buffer[this.offset++];
  85. if (byte1 < 251)
  86. return byte1;
  87. return this.readLengthCodedNumberExt(byte1, bigNumberStrings);
  88. }
  89. Packet.prototype.readLengthCodedNumberExt = function(tag, bigNumberStrings) {
  90. var word0, word1;
  91. var res;
  92. if (tag == 0xfb)
  93. return null;
  94. if (tag == 0xfc) {
  95. return this.readInt8() + (this.readInt8() << 8);
  96. }
  97. if (tag == 0xfd) {
  98. return this.readInt8() + (this.readInt8() << 8) + (this.readInt8() << 16);
  99. }
  100. if (tag == 0xfe) {
  101. // TODO: check version
  102. // Up to MySQL 3.22, 0xfe was followed by a 4-byte integer.
  103. word0 = this.readInt32();
  104. word1 = this.readInt32();
  105. if (word1 === 0)
  106. return word0; // don't convert to float if possible
  107. if (word1 < 2097152) // max exact float point int, 2^52 / 2^32
  108. return word1*0x100000000 + word0;
  109. res = (new bn(word1)).ishln(32).iaddn(word0);
  110. return bigNumberStrings ? res.toString() : res;
  111. }
  112. console.trace();
  113. throw "Should not reach here: " + byte1;
  114. };
  115. Packet.prototype.readFloat = function() {
  116. var res = this.buffer.readFloatLE(this.offset);
  117. this.offset += 4;
  118. return res;
  119. };
  120. Packet.prototype.readDouble = function() {
  121. var res = this.buffer.readDoubleLE(this.offset);
  122. this.offset += 8;
  123. return res;
  124. };
  125. Packet.prototype.readBuffer = function(len) {
  126. if (typeof len == 'undefined')
  127. len = this.end - this.offset;
  128. this.offset += len;
  129. return this.buffer.slice(this.offset - len, this.offset);
  130. };
  131. var INVALID_DATE = new Date(NaN);
  132. // DATE, DATETIME and TIMESTAMP
  133. Packet.prototype.readDateTime = function() {
  134. var length = this.readInt8();
  135. var y = 0;
  136. var m = 0;
  137. var d = 0;
  138. var H = 0;
  139. var M = 0;
  140. var S = 0;
  141. var ms = 0;
  142. if (length > 3) {
  143. y = this.readInt16();
  144. m = this.readInt8();
  145. d = this.readInt8();
  146. }
  147. if (length > 6) {
  148. H = this.readInt8();
  149. M = this.readInt8();
  150. S = this.readInt8();
  151. }
  152. if (length > 11)
  153. ms = this.readInt32();
  154. if ((y + m + d + H + M + S + ms) === 0) {
  155. return INVALID_DATE;
  156. }
  157. return new Date(y, m-1, d, H, M, S, ms);
  158. };
  159. // this is nearly duplicate of previous function so generated code is not slower
  160. // due to "if (dateStrings)" branching
  161. var pad = "000000000000";
  162. function leftPad(num, value) {
  163. var s = value.toString();
  164. // if we don't need to pad
  165. if (s.length >= num)
  166. return s;
  167. return (pad + s).slice(-num);
  168. }
  169. Packet.prototype.readDateTimeString = function() {
  170. var length = this.readInt8();
  171. var y = 0;
  172. var m = 0;
  173. var d = 0;
  174. var H = 0;
  175. var M = 0;
  176. var S = 0;
  177. var ms = 0;
  178. var str;
  179. if (length > 3) {
  180. y = this.readInt16();
  181. m = this.readInt8();
  182. d = this.readInt8();
  183. str = [leftPad(4, y), leftPad(2, m), leftPad(2, d)].join('-');
  184. }
  185. if (length > 6) {
  186. H = this.readInt8();
  187. M = this.readInt8();
  188. S = this.readInt8();
  189. str += ' ' + [leftPad(2, H), leftPad(2, M), leftPad(2, S)].join(':');
  190. }
  191. /* in text protocol you don't see microseconds as DATETIME/TIMESTAMP result.
  192. instead you need to use MICROSECOND() function
  193. if (length > 11) {
  194. ms = this.readInt32();
  195. }
  196. */
  197. return str;
  198. };
  199. // TIME - value as a string, Can be negative
  200. Packet.prototype.readTimeString = function(convertTtoMs) {
  201. var length = this.readInt8();
  202. if (length === 0)
  203. return 0;
  204. var result = 0;
  205. var sign = this.readInt8() ? -1 : 1; // 'isNegative' flag byte
  206. var d = 0;
  207. var H = 0;
  208. var M = 0;
  209. var S = 0;
  210. var ms = 0;
  211. if (length > 7) {
  212. d = this.readInt32();
  213. H = this.readInt8();
  214. M = this.readInt8();
  215. S = this.readInt8();
  216. }
  217. if (length > 11)
  218. ms = this.readInt32();
  219. if (convertTtoMs) {
  220. H += d * 24;
  221. M += H * 60;
  222. S += M * 60;
  223. ms += S * 1000;
  224. ms *= sign;
  225. return ms;
  226. }
  227. return ( sign === -1 ? '-' : '' ) + [( d ? (d*24) + H : H ), leftPad(2, M), leftPad(2, S)].join(':') + ( ms ? '.'+ ms : '' );
  228. };
  229. Packet.prototype.readLengthCodedString = function() {
  230. var len = this.readLengthCodedNumber();
  231. // TODO: check manually first byte here to avoid polymorphic return type?
  232. if (len === null)
  233. return null;
  234. this.offset += len;
  235. return this.buffer.utf8Slice(this.offset - len, this.offset);
  236. };
  237. Packet.prototype.readLengthCodedBuffer = function() {
  238. var len = this.readLengthCodedNumber();
  239. return this.readBuffer(len);
  240. };
  241. Packet.prototype.readNullTerminatedString = function() {
  242. var start = this.offset;
  243. var end = this.offset;
  244. while (this.buffer[end])
  245. end = end + 1; // TODO: handle OOB check
  246. this.offset = end + 1;
  247. return this.buffer.utf8Slice(start, end);
  248. };
  249. // TODO reuse?
  250. Packet.prototype.readString = function(len) {
  251. if (typeof len == 'undefined')
  252. len = this.end - this.offset;
  253. this.offset += len;
  254. return this.buffer.utf8Slice(this.offset - len, this.offset);
  255. };
  256. var minus = '-'.charCodeAt(0);
  257. var plus = '+'.charCodeAt(0);
  258. // TODO: faster versions of parseInt for smaller types?
  259. // discard overflow checks if we know from type definition it's safe so
  260. Packet.prototype.parseInt = function(len) {
  261. if (len === null)
  262. return null;
  263. var result = 0;
  264. var end = this.offset + len;
  265. var sign = 1;
  266. if (len === 0)
  267. return 0; // TODO: assert? exception?
  268. if (this.buffer[this.offset] == minus) {
  269. this.offset++;
  270. sign = -1;
  271. }
  272. // max precise int is 9007199254740992
  273. // note that we are here after handling optional +/-
  274. // treat everything above 9000000000000000 as potential overflow
  275. var str;
  276. var numDigits = end - this.offset;
  277. if (numDigits == 16 && (this.buffer[this.offset] - 48) > 8) {
  278. str = this.readString(end - this.offset);
  279. result = parseInt(str, 10);
  280. if (result.toString() == str)
  281. return sign*result;
  282. else
  283. return sign == -1 ? "-" + str : str;
  284. } else if (numDigits > 16) {
  285. str = this.readString(end - this.offset);
  286. return sign == -1 ? "-" + str : str;
  287. }
  288. if (this.buffer[this.offset] == plus) {
  289. this.offset++; // just ignore
  290. }
  291. while(this.offset < end) {
  292. result *= 10;
  293. result += this.buffer[this.offset] - 48;
  294. this.offset++;
  295. }
  296. return result*sign;
  297. };
  298. // copy-paste from https://github.com/felixge/node-mysql/blob/master/lib/protocol/Parser.js
  299. Packet.prototype.parseGeometryValue = function() {
  300. var buffer = this.readLengthCodedBuffer();
  301. var offset = 4;
  302. if (buffer === null || !buffer.length) {
  303. return null;
  304. }
  305. function parseGeometry() {
  306. var x, y, i, j, numPoints, line;
  307. var result = null;
  308. var byteOrder = buffer.readUInt8(offset); offset += 1;
  309. var wkbType = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  310. switch(wkbType) {
  311. case 1: // WKBPoint
  312. x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  313. y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  314. result = {x: x, y: y};
  315. break;
  316. case 2: // WKBLineString
  317. numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  318. result = [];
  319. for(i=numPoints;i>0;i--) {
  320. x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  321. y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  322. result.push({x: x, y: y});
  323. }
  324. break;
  325. case 3: // WKBPolygon
  326. var numRings = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  327. result = [];
  328. for(i=numRings;i>0;i--) {
  329. numPoints = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  330. line = [];
  331. for(j=numPoints;j>0;j--) {
  332. x = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  333. y = byteOrder? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;
  334. line.push({x: x, y: y});
  335. }
  336. result.push(line);
  337. }
  338. break;
  339. case 4: // WKBMultiPoint
  340. case 5: // WKBMultiLineString
  341. case 6: // WKBMultiPolygon
  342. case 7: // WKBGeometryCollection
  343. var num = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
  344. result = [];
  345. for(i=num;i>0;i--) {
  346. result.push(parseGeometry());
  347. }
  348. break;
  349. }
  350. return result;
  351. }
  352. return parseGeometry();
  353. };
  354. Packet.prototype.parseDate = function() {
  355. var strLen = this.readLengthCodedNumber();
  356. if (strLen === null)
  357. return null;
  358. if (strLen != 10) {
  359. // we expect only YYYY-MM-DD here.
  360. // if for some reason it's not the case return invalid date
  361. return new Date(NaN);
  362. }
  363. var y = this.parseInt(4);
  364. this.offset++; // -
  365. var m = this.parseInt(2);
  366. this.offset++; // -
  367. var d = this.parseInt(2);
  368. return new Date(y, m-1, d);
  369. };
  370. Packet.prototype.parseDateTime = function() {
  371. var str = this.readLengthCodedString();
  372. if (str === null)
  373. return null;
  374. return new Date(str);
  375. };
  376. // TODO: handle E notation
  377. var dot = '.'.charCodeAt(0);
  378. var exponent = 'e'.charCodeAt(0);
  379. var exponentCapital = 'E'.charCodeAt(0);
  380. Packet.prototype.parseFloat = function(len) {
  381. if (len === null)
  382. return null;
  383. var result = 0;
  384. var end = this.offset + len;
  385. var factor = 1;
  386. var pastDot = false;
  387. var charCode = 0;
  388. if (len === 0)
  389. return 0; // TODO: assert? exception?
  390. if (this.buffer[this.offset] == minus) {
  391. this.offset++;
  392. factor = -1;
  393. }
  394. if (this.buffer[this.offset] == plus) {
  395. this.offset++; // just ignore
  396. }
  397. while(this.offset < end) {
  398. charCode = this.buffer[this.offset];
  399. if (charCode == dot)
  400. {
  401. pastDot = true;
  402. this.offset++;
  403. } else if (charCode == exponent || charCode == exponentCapital) {
  404. this.offset++;
  405. var exponentValue = this.parseInt(end - this.offset);
  406. return (result/factor)*Math.pow(10, exponentValue);
  407. } else {
  408. result *= 10;
  409. result += this.buffer[this.offset] - 48;
  410. this.offset++;
  411. if (pastDot)
  412. factor = factor*10;
  413. }
  414. }
  415. return result/factor;
  416. };
  417. Packet.prototype.parseLengthCodedInt = function() {
  418. return this.parseInt(this.readLengthCodedNumber());
  419. };
  420. Packet.prototype.parseLengthCodedIntString = function() {
  421. return this.readString(this.readLengthCodedNumber());
  422. };
  423. Packet.prototype.parseLengthCodedFloat = function() {
  424. return this.parseFloat(this.readLengthCodedNumber());
  425. };
  426. Packet.prototype.isError = function() {
  427. return this.buffer[this.offset] == 0xff;
  428. };
  429. Packet.prototype.asError = function() {
  430. this.reset();
  431. var fieldCount = this.readInt8();
  432. var errorCode = this.readInt16();
  433. var sqlState = '';
  434. if (this.buffer[this.offset] == 0x23)
  435. sqlState = this.readBuffer(6).toString();
  436. var message = this.readString();
  437. var err = new Error(message);
  438. err.code = ErrorCodeToName[errorCode];
  439. err.errno = errorCode;
  440. err.sqlState = sqlState;
  441. return err;
  442. };
  443. Packet.lengthCodedNumberLength = function(n) {
  444. if (n < 0xfb)
  445. return 1;
  446. if (n < 0xffff)
  447. return 3;
  448. if (n < 0xffffff)
  449. return 5;
  450. else
  451. return 9;
  452. };
  453. Packet.prototype.writeInt32 = function(n) {
  454. this.buffer.writeUInt32LE(n, this.offset);
  455. this.offset += 4;
  456. };
  457. Packet.prototype.writeInt24 = function(n) {
  458. this.writeInt8(n & 0xff);
  459. this.writeInt16(n >> 8);
  460. };
  461. Packet.prototype.writeInt16 = function(n) {
  462. this.buffer.writeUInt16LE(n, this.offset);
  463. this.offset += 2;
  464. };
  465. Packet.prototype.writeInt8 = function(n) {
  466. this.buffer.writeUInt8(n, this.offset);
  467. this.offset++;
  468. };
  469. Packet.prototype.writeBuffer = function(b) {
  470. b.copy(this.buffer, this.offset);
  471. this.offset += b.length;
  472. };
  473. Packet.prototype.writeNull = function() {
  474. this.buffer[this.offset] = 0xfb;
  475. this.offset++;
  476. };
  477. // TODO: refactor following three?
  478. Packet.prototype.writeNullTerminatedString = function(s) {
  479. this.buffer.write(s, this.offset);
  480. this.offset += s.length;
  481. this.writeInt8(0);
  482. };
  483. Packet.prototype.writeString = function(s) {
  484. var bytes = Buffer.byteLength(s, 'utf8');
  485. this.buffer.write(s, this.offset, bytes, 'utf8');
  486. this.offset += bytes;
  487. };
  488. Packet.prototype.writeLengthCodedString = function(s) {
  489. var bytes = Buffer.byteLength(s, 'utf8');
  490. this.writeLengthCodedNumber(bytes);
  491. this.buffer.write(s, this.offset, bytes, 'utf8');
  492. this.offset += bytes;
  493. };
  494. Packet.prototype.writeLengthCodedBuffer = function(b) {
  495. this.writeLengthCodedNumber(b.length);
  496. b.copy(this.buffer, this.offset);
  497. this.offset += b.length;
  498. };
  499. Packet.prototype.writeLengthCodedNumber = function(n) {
  500. // TODO: null - http://dev.mysql.com/doc/internals/en/overview.html#length-encoded-integer
  501. if (n < 0xfb)
  502. return this.writeInt8(n);
  503. if (n < 0xffff) {
  504. this.writeInt8(0xfc);
  505. return this.writeInt16(n);
  506. }
  507. if (n < 0xffffff) {
  508. this.writeInt8(0xfd);
  509. return this.writeInt24(n);
  510. }
  511. console.log(n);
  512. console.trace();
  513. throw "No bignumbers yet";
  514. };
  515. Packet.prototype.writeHeader = function(sequenceId)
  516. {
  517. var offset = this.offset;
  518. this.offset = 0;
  519. this.writeInt24(this.buffer.length - 4);
  520. this.writeInt8(sequenceId);
  521. this.offset = offset;
  522. };
  523. Packet.prototype.clone = function() {
  524. var buffer = this.buffer.slice(this.start, this.end);
  525. var other = new Packet(this.sequenceId, this.buffer, 0, this.buffer.length);
  526. return other;
  527. };
  528. Packet.prototype.type = function() {
  529. if (this.isEOF())
  530. return 'EOF';
  531. if (this.isError())
  532. return 'Error';
  533. if (this.buffer[this.offset] == 0)
  534. return 'maybeOK'; // could be other packet types as well
  535. return '';
  536. };
  537. module.exports = Packet;