| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 | var PacketHeader = require('./PacketHeader');var BigNumber    = require('bignumber.js');var Buffer       = require('safe-buffer').Buffer;var BufferList   = require('./BufferList');var MAX_PACKET_LENGTH    = Math.pow(2, 24) - 1;var MUL_32BIT            = Math.pow(2, 32);var PACKET_HEADER_LENGTH = 4;module.exports = Parser;function Parser(options) {  options = options || {};  this._supportBigNumbers = options.config && options.config.supportBigNumbers;  this._buffer            = Buffer.alloc(0);  this._nextBuffers       = new BufferList();  this._longPacketBuffers = new BufferList();  this._offset            = 0;  this._packetEnd         = null;  this._packetHeader      = null;  this._packetOffset      = null;  this._onError           = options.onError || function(err) { throw err; };  this._onPacket          = options.onPacket || function() {};  this._nextPacketNumber  = 0;  this._encoding          = 'utf-8';  this._paused            = false;}Parser.prototype.write = function write(chunk) {  this._nextBuffers.push(chunk);  while (!this._paused) {    var packetHeader = this._tryReadPacketHeader();    if (!packetHeader) {      break;    }    if (!this._combineNextBuffers(packetHeader.length)) {      break;    }    this._parsePacket(packetHeader);  }};Parser.prototype.append = function append(chunk) {  if (!chunk || chunk.length === 0) {    return;  }  // Calculate slice ranges  var sliceEnd    = this._buffer.length;  var sliceStart  = this._packetOffset === null    ? this._offset    : this._packetOffset;  var sliceLength = sliceEnd - sliceStart;  // Get chunk data  var buffer = null;  var chunks = !(chunk instanceof Array || Array.isArray(chunk)) ? [chunk] : chunk;  var length = 0;  var offset = 0;  for (var i = 0; i < chunks.length; i++) {    length += chunks[i].length;  }  if (sliceLength !== 0) {    // Create a new Buffer    buffer = Buffer.allocUnsafe(sliceLength + length);    offset = 0;    // Copy data slice    offset += this._buffer.copy(buffer, 0, sliceStart, sliceEnd);    // Copy chunks    for (var i = 0; i < chunks.length; i++) {      offset += chunks[i].copy(buffer, offset);    }  } else if (chunks.length > 1) {    // Create a new Buffer    buffer = Buffer.allocUnsafe(length);    offset = 0;    // Copy chunks    for (var i = 0; i < chunks.length; i++) {      offset += chunks[i].copy(buffer, offset);    }  } else {    // Buffer is the only chunk    buffer = chunks[0];  }  // Adjust data-tracking pointers  this._buffer       = buffer;  this._offset       = this._offset - sliceStart;  this._packetEnd    = this._packetEnd !== null    ? this._packetEnd - sliceStart    : null;  this._packetOffset = this._packetOffset !== null    ? this._packetOffset - sliceStart    : null;};Parser.prototype.pause = function() {  this._paused = true;};Parser.prototype.resume = function() {  this._paused = false;  // nextTick() to avoid entering write() multiple times within the same stack  // which would cause problems as write manipulates the state of the object.  process.nextTick(this.write.bind(this));};Parser.prototype.peak = function peak(offset) {  return this._buffer[this._offset + (offset >>> 0)];};Parser.prototype.parseUnsignedNumber = function parseUnsignedNumber(bytes) {  if (bytes === 1) {    return this._buffer[this._offset++];  }  var buffer = this._buffer;  var offset = this._offset + bytes - 1;  var value  = 0;  if (bytes > 4) {    var err    = new Error('parseUnsignedNumber: Supports only up to 4 bytes');    err.offset = (this._offset - this._packetOffset - 1);    err.code   = 'PARSER_UNSIGNED_TOO_LONG';    throw err;  }  while (offset >= this._offset) {    value = ((value << 8) | buffer[offset]) >>> 0;    offset--;  }  this._offset += bytes;  return value;};Parser.prototype.parseLengthCodedString = function() {  var length = this.parseLengthCodedNumber();  if (length === null) {    return null;  }  return this.parseString(length);};Parser.prototype.parseLengthCodedBuffer = function() {  var length = this.parseLengthCodedNumber();  if (length === null) {    return null;  }  return this.parseBuffer(length);};Parser.prototype.parseLengthCodedNumber = function parseLengthCodedNumber() {  if (this._offset >= this._buffer.length) {    var err    = new Error('Parser: read past end');    err.offset = (this._offset - this._packetOffset);    err.code   = 'PARSER_READ_PAST_END';    throw err;  }  var bits = this._buffer[this._offset++];  if (bits <= 250) {    return bits;  }  switch (bits) {    case 251:      return null;    case 252:      return this.parseUnsignedNumber(2);    case 253:      return this.parseUnsignedNumber(3);    case 254:      break;    default:      var err    = new Error('Unexpected first byte' + (bits ? ': 0x' + bits.toString(16) : ''));      err.offset = (this._offset - this._packetOffset - 1);      err.code   = 'PARSER_BAD_LENGTH_BYTE';      throw err;  }  var low = this.parseUnsignedNumber(4);  var high = this.parseUnsignedNumber(4);  var value;  if (high >>> 21) {    value = BigNumber(MUL_32BIT).times(high).plus(low).toString();    if (this._supportBigNumbers) {      return value;    }    var err    = new Error(      'parseLengthCodedNumber: JS precision range exceeded, ' +      'number is >= 53 bit: "' + value + '"'    );    err.offset = (this._offset - this._packetOffset - 8);    err.code   = 'PARSER_JS_PRECISION_RANGE_EXCEEDED';    throw err;  }  value = low + (MUL_32BIT * high);  return value;};Parser.prototype.parseFiller = function(length) {  return this.parseBuffer(length);};Parser.prototype.parseNullTerminatedBuffer = function() {  var end      = this._nullByteOffset();  var value    = this._buffer.slice(this._offset, end);  this._offset = end + 1;  return value;};Parser.prototype.parseNullTerminatedString = function() {  var end      = this._nullByteOffset();  var value    = this._buffer.toString(this._encoding, this._offset, end);  this._offset = end + 1;  return value;};Parser.prototype._nullByteOffset = function() {  var offset = this._offset;  while (this._buffer[offset] !== 0x00) {    offset++;    if (offset >= this._buffer.length) {      var err    = new Error('Offset of null terminated string not found.');      err.offset = (this._offset - this._packetOffset);      err.code   = 'PARSER_MISSING_NULL_BYTE';      throw err;    }  }  return offset;};Parser.prototype.parsePacketTerminatedBuffer = function parsePacketTerminatedBuffer() {  var length = this._packetEnd - this._offset;  return this.parseBuffer(length);};Parser.prototype.parsePacketTerminatedString = function() {  var length = this._packetEnd - this._offset;  return this.parseString(length);};Parser.prototype.parseBuffer = function(length) {  var response = Buffer.alloc(length);  this._buffer.copy(response, 0, this._offset, this._offset + length);  this._offset += length;  return response;};Parser.prototype.parseString = function(length) {  var offset = this._offset;  var end = offset + length;  var value = this._buffer.toString(this._encoding, offset, end);  this._offset = end;  return value;};Parser.prototype.parseGeometryValue = function() {  var buffer = this.parseLengthCodedBuffer();  var offset = 4;  if (buffer === null || !buffer.length) {    return null;  }  function parseGeometry() {    var result = null;    var byteOrder = buffer.readUInt8(offset); offset += 1;    var wkbType = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;    switch (wkbType) {      case 1: // WKBPoint        var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;        var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;        result = {x: x, y: y};        break;      case 2: // WKBLineString        var numPoints = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;        result = [];        for (var i = numPoints; i > 0; i--) {          var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;          var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;          result.push({x: x, y: y});        }        break;      case 3: // WKBPolygon        var numRings = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;        result = [];        for (var i = numRings; i > 0; i--) {          var numPoints = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;          var line = [];          for (var j = numPoints; j > 0; j--) {            var x = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;            var y = byteOrder ? buffer.readDoubleLE(offset) : buffer.readDoubleBE(offset); offset += 8;            line.push({x: x, y: y});          }          result.push(line);        }        break;      case 4: // WKBMultiPoint      case 5: // WKBMultiLineString      case 6: // WKBMultiPolygon      case 7: // WKBGeometryCollection        var num = byteOrder ? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;        var result = [];        for (var i = num; i > 0; i--) {          result.push(parseGeometry());        }        break;    }    return result;  }  return parseGeometry();};Parser.prototype.reachedPacketEnd = function() {  return this._offset === this._packetEnd;};Parser.prototype.incrementPacketNumber = function() {  var currentPacketNumber = this._nextPacketNumber;  this._nextPacketNumber = (this._nextPacketNumber + 1) % 256;  return currentPacketNumber;};Parser.prototype.resetPacketNumber = function() {  this._nextPacketNumber = 0;};Parser.prototype.packetLength = function packetLength() {  if (!this._packetHeader) {    return null;  }  return this._packetHeader.length + this._longPacketBuffers.size;};Parser.prototype._combineNextBuffers = function _combineNextBuffers(bytes) {  var length = this._buffer.length - this._offset;  if (length >= bytes) {    return true;  }  if ((length + this._nextBuffers.size) < bytes) {    return false;  }  var buffers     = [];  var bytesNeeded = bytes - length;  while (bytesNeeded > 0) {    var buffer = this._nextBuffers.shift();    buffers.push(buffer);    bytesNeeded -= buffer.length;  }  this.append(buffers);  return true;};Parser.prototype._combineLongPacketBuffers = function _combineLongPacketBuffers() {  if (!this._longPacketBuffers.size) {    return;  }  // Calculate bytes  var remainingBytes      = this._buffer.length - this._offset;  var trailingPacketBytes = this._buffer.length - this._packetEnd;  // Create buffer  var buf    = null;  var buffer = Buffer.allocUnsafe(remainingBytes + this._longPacketBuffers.size);  var offset = 0;  // Copy long buffers  while ((buf = this._longPacketBuffers.shift())) {    offset += buf.copy(buffer, offset);  }  // Copy remaining bytes  this._buffer.copy(buffer, offset, this._offset);  this._buffer       = buffer;  this._offset       = 0;  this._packetEnd    = this._buffer.length - trailingPacketBytes;  this._packetOffset = 0;};Parser.prototype._parsePacket = function _parsePacket(packetHeader) {  this._packetEnd    = this._offset + packetHeader.length;  this._packetOffset = this._offset;  if (packetHeader.length === MAX_PACKET_LENGTH) {    this._longPacketBuffers.push(this._buffer.slice(this._packetOffset, this._packetEnd));    this._advanceToNextPacket();    return;  }  this._combineLongPacketBuffers();  var hadException = true;  try {    this._onPacket(packetHeader);    hadException = false;  } catch (err) {    if (!err || typeof err.code !== 'string' || err.code.substr(0, 7) !== 'PARSER_') {      throw err; // Rethrow non-MySQL errors    }    // Pass down parser errors    this._onError(err);    hadException = false;  } finally {    this._advanceToNextPacket();    // If there was an exception, the parser while loop will be broken out    // of after the finally block. So schedule a blank write to re-enter it    // to continue parsing any bytes that may already have been received.    if (hadException) {      process.nextTick(this.write.bind(this));    }  }};Parser.prototype._tryReadPacketHeader = function _tryReadPacketHeader() {  if (this._packetHeader) {    return this._packetHeader;  }  if (!this._combineNextBuffers(PACKET_HEADER_LENGTH)) {    return null;  }  this._packetHeader = new PacketHeader(    this.parseUnsignedNumber(3),    this.parseUnsignedNumber(1)  );  if (this._packetHeader.number !== this._nextPacketNumber) {    var err = new Error(      'Packets out of order. Got: ' + this._packetHeader.number + ' ' +      'Expected: ' + this._nextPacketNumber    );    err.code  = 'PROTOCOL_PACKETS_OUT_OF_ORDER';    err.fatal = true;    this._onError(err);  }  this.incrementPacketNumber();  return this._packetHeader;};Parser.prototype._advanceToNextPacket = function() {  this._offset       = this._packetEnd;  this._packetHeader = null;  this._packetEnd    = null;  this._packetOffset = null;};
 |