From 7ec1815188fafa9764e8b1494692f28ff3cfd549 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sat, 7 Jul 2012 02:44:51 -0700 Subject: [PATCH 1/6] generally faster javascript parser --- lib/parser/javascript.js | 474 ++++++++++++++++----------------------- 1 file changed, 193 insertions(+), 281 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index b8f5bc6..bf67573 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -1,317 +1,229 @@ -/*global Buffer require exports console setTimeout */ - -// TODO - incorporate these V8 pro tips: -// pre-allocate Arrays if length is known in advance -// do not use delete -// use numbers for parser state - var events = require("events"), - util = require("../util"); + util = require('../util'); + +function Packet (type, size) { + this.type = type; + this.size = +size; +} +exports.name = 'faster'; exports.debug_mode = false; -exports.name = "javascript"; -function RedisReplyParser(options) { +function FasterReplyParser (options) { this.name = exports.name; - this.options = options || {}; - this.reset(); - events.EventEmitter.call(this); + this.options = options || { }; + + this._buffer = null; + this._offset = 0; + this._encoding = 'utf-8'; + this._debug_mode = options.debug_mode; + this._reply_type = null; } -util.inherits(RedisReplyParser, events.EventEmitter); +util.inherits(FasterReplyParser, events.EventEmitter); -exports.Parser = RedisReplyParser; +exports.Parser = FasterReplyParser; -// Buffer.toString() is quite slow for small strings -function small_toString(buf, len) { - var tmp = "", i; +FasterReplyParser.prototype._parseResult = function (type) { + var start, end, offset, packetHeader; + + if (type === 43 || type === 45) { // + + end = this._packetEndOffset() - 1; + start = this._offset; - for (i = 0; i < len; i += 1) { - tmp += String.fromCharCode(buf[i]); - } + this._offset = end + 2; - return tmp; -} + if (this.options.return_buffers) { + return this._buffer.slice(start, end); + } else { + return this._buffer.slice(start, end).toString(this._encoding); + } + } else if (type === 58) { // : + end = this._packetEndOffset() - 1; + start = this._offset; -// Reset parser to it's original state. -RedisReplyParser.prototype.reset = function () { - this.return_buffer = new Buffer(16384); // for holding replies, might grow - this.return_string = ""; - this.tmp_string = ""; // for holding size fields - - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; - this.multi_bulk_nested_length = 0; - this.multi_bulk_nested_replies = null; - - this.states = { - TYPE: 1, - SINGLE_LINE: 2, - MULTI_BULK_COUNT: 3, - INTEGER_LINE: 4, - BULK_LENGTH: 5, - ERROR_LINE: 6, - BULK_DATA: 7, - UNKNOWN_TYPE: 8, - FINAL_CR: 9, - FINAL_LF: 10, - MULTI_BULK_COUNT_LF: 11, - BULK_LF: 12 - }; - - this.state = this.states.TYPE; -}; + this._offset = end + 2; -RedisReplyParser.prototype.parser_error = function (message) { - this.emit("error", message); - this.reset(); + return +this._buffer.toString(this._encoding, start, end); + } else if (type === 36) { // $ + offset = this._offset - 1; + + packetHeader = new Packet(type, this.parseHeader()); + + if (packetHeader.size === null) { + this._offset++; + + return null; + } + + if (packetHeader.size === -1) { + return null; + } + + end = this._offset + packetHeader.size; + start = this._offset; + + this._offset = end + 2; + + if (end > this._buffer.length) { + this._offset = offset; + return null; + } + + if (this.options.return_buffers) { + return this._buffer.slice(start, end); + } else { + return this._buffer.slice(start, end).toString(this._encoding); + } + } else if (type === 42) { // * + offset = this._offset; + packetHeader = new Packet(type, this.parseHeader()); + + if (packetHeader.size > this._bytesRemaining()) { + this._offset = offset - 1; + return -1; + } + + if (packetHeader.size < 0) { + this._offset += 2; + return null; + } + + var reply = [ ]; + offset = this._offset - 1; + + for (var i = 0; i < packetHeader.size; i++) { + var ntype = this._buffer[this._offset++]; + + if (this._offset === this._buffer.length) { + throw new Error('too far'); + } + reply.push(this._parseResult(ntype)); + } + + return reply; + } }; -RedisReplyParser.prototype.execute = function (incoming_buf) { - var pos = 0, bd_tmp, bd_str, i, il, states = this.states; - //, state_times = {}, start_execute = new Date(), start_switch, end_switch, old_state; - //start_switch = new Date(); - - while (pos < incoming_buf.length) { - // old_state = this.state; - // console.log("execute: " + this.state + ", " + pos + "/" + incoming_buf.length + ", " + String.fromCharCode(incoming_buf[pos])); - - switch (this.state) { - case 1: // states.TYPE - this.type = incoming_buf[pos]; - pos += 1; - - switch (this.type) { - case 43: // + - this.state = states.SINGLE_LINE; - this.return_buffer.end = 0; - this.return_string = ""; - break; - case 42: // * - this.state = states.MULTI_BULK_COUNT; - this.tmp_string = ""; - break; - case 58: // : - this.state = states.INTEGER_LINE; - this.return_buffer.end = 0; - this.return_string = ""; - break; - case 36: // $ - this.state = states.BULK_LENGTH; - this.tmp_string = ""; - break; - case 45: // - - this.state = states.ERROR_LINE; - this.return_buffer.end = 0; - this.return_string = ""; +FasterReplyParser.prototype.execute = function (buffer) { + this.append(buffer); + + while (true) { + var offset = this._offset; + try { + var ret; + + // at least 4 bytes: *1\r\n + if (this._bytesRemaining() < 4) { break; - default: - this.state = states.UNKNOWN_TYPE; } - break; - case 4: // states.INTEGER_LINE - if (incoming_buf[pos] === 13) { - this.send_reply(+small_toString(this.return_buffer, this.return_buffer.end)); - this.state = states.FINAL_LF; - } else { - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - } - pos += 1; - break; - case 6: // states.ERROR_LINE - if (incoming_buf[pos] === 13) { - this.send_error(this.return_buffer.toString("ascii", 0, this.return_buffer.end)); - this.state = states.FINAL_LF; - } else { - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - } - pos += 1; - break; - case 2: // states.SINGLE_LINE - if (incoming_buf[pos] === 13) { - this.send_reply(this.return_string); - this.state = states.FINAL_LF; - } else { - this.return_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 3: // states.MULTI_BULK_COUNT - if (incoming_buf[pos] === 13) { // \r - this.state = states.MULTI_BULK_COUNT_LF; - } else { - this.tmp_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 11: // states.MULTI_BULK_COUNT_LF - if (incoming_buf[pos] === 10) { // \n - if (this.multi_bulk_length) { // nested multi-bulk - this.multi_bulk_nested_length = this.multi_bulk_length; - this.multi_bulk_nested_replies = this.multi_bulk_replies; - this.multi_bulk_nested_pos = this.multi_bulk_pos; - } - this.multi_bulk_length = +this.tmp_string; - this.multi_bulk_pos = 0; - this.state = states.TYPE; - if (this.multi_bulk_length < 0) { - this.send_reply(null); - this.multi_bulk_length = 0; - } else if (this.multi_bulk_length === 0) { - this.multi_bulk_pos = 0; - this.multi_bulk_replies = null; - this.send_reply([]); - } else { - this.multi_bulk_replies = new Array(this.multi_bulk_length); + + var type = this._buffer[this._offset++]; + + if (type === 43) { // + + ret = this._parseResult(type); + this.send_reply(ret); + } else if (type === 45) { + ret = this._parseResult(type); + this.send_error(ret); + } else if (type === 58) { // : + ret = this._parseResult(type); + this.send_reply(+ret); + } else if (type === 36) { // $ + ret = this._parseResult(type); + + if (ret === null) { + break; } - } else { - this.parser_error(new Error("didn't see LF after NL reading multi bulk count")); - return; - } - pos += 1; - break; - case 5: // states.BULK_LENGTH - if (incoming_buf[pos] === 13) { // \r - this.state = states.BULK_LF; - } else { - this.tmp_string += String.fromCharCode(incoming_buf[pos]); - } - pos += 1; - break; - case 12: // states.BULK_LF - if (incoming_buf[pos] === 10) { // \n - this.bulk_length = +this.tmp_string; - if (this.bulk_length === -1) { - this.send_reply(null); - this.state = states.TYPE; - } else if (this.bulk_length === 0) { - this.send_reply(new Buffer("")); - this.state = states.FINAL_CR; - } else { - this.state = states.BULK_DATA; - if (this.bulk_length > this.return_buffer.length) { - if (exports.debug_mode) { - console.log("Growing return_buffer from " + this.return_buffer.length + " to " + this.bulk_length); - } - this.return_buffer = new Buffer(this.bulk_length); - } - this.return_buffer.end = 0; + this.send_reply(ret); + } else if (type === 42) { // * + offset = this._offset - 1; + ret = this._parseResult(type); + if (ret === -1) { + this._offset = offset; + break; } - } else { - this.parser_error(new Error("didn't see LF after NL while reading bulk length")); - return; - } - pos += 1; - break; - case 7: // states.BULK_DATA - this.return_buffer[this.return_buffer.end] = incoming_buf[pos]; - this.return_buffer.end += 1; - pos += 1; - if (this.return_buffer.end === this.bulk_length) { - bd_tmp = new Buffer(this.bulk_length); - // When the response is small, Buffer.copy() is a lot slower. - if (this.bulk_length > 10) { - this.return_buffer.copy(bd_tmp, 0, 0, this.bulk_length); - } else { - for (i = 0, il = this.bulk_length; i < il; i += 1) { - bd_tmp[i] = this.return_buffer[i]; - } - } - this.send_reply(bd_tmp); - this.state = states.FINAL_CR; - } - break; - case 9: // states.FINAL_CR - if (incoming_buf[pos] === 13) { // \r - this.state = states.FINAL_LF; - pos += 1; - } else { - this.parser_error(new Error("saw " + incoming_buf[pos] + " when expecting final CR")); - return; - } - break; - case 10: // states.FINAL_LF - if (incoming_buf[pos] === 10) { // \n - this.state = states.TYPE; - pos += 1; - } else { - this.parser_error(new Error("saw " + incoming_buf[pos] + " when expecting final LF")); - return; + + this.send_reply(ret); } + } catch(err) { + this._offset = offset; break; - default: - this.parser_error(new Error("invalid state " + this.state)); } - // end_switch = new Date(); - // if (state_times[old_state] === undefined) { - // state_times[old_state] = 0; - // } - // state_times[old_state] += (end_switch - start_switch); - // start_switch = end_switch; } - // console.log("execute ran for " + (Date.now() - start_execute) + " ms, on " + incoming_buf.length + " Bytes. "); - // Object.keys(state_times).forEach(function (state) { - // console.log(" " + state + ": " + state_times[state]); - // }); }; -RedisReplyParser.prototype.send_error = function (reply) { - if (this.multi_bulk_length > 0 || this.multi_bulk_nested_length > 0) { - // TODO - can this happen? Seems like maybe not. - this.add_multi_bulk_reply(reply); - } else { - this.emit("reply error", reply); +FasterReplyParser.prototype.append = function(newBuffer) { + if (!newBuffer) { + return; } -}; -RedisReplyParser.prototype.send_reply = function (reply) { - if (this.multi_bulk_length > 0 || this.multi_bulk_nested_length > 0) { - if (!this.options.return_buffers && Buffer.isBuffer(reply)) { - this.add_multi_bulk_reply(reply.toString("utf8")); - } else { - this.add_multi_bulk_reply(reply); - } - } else { - if (!this.options.return_buffers && Buffer.isBuffer(reply)) { - this.emit("reply", reply.toString("utf8")); - } else { - this.emit("reply", reply); - } + var oldBuffer = this._buffer; + if (!oldBuffer) { + this._buffer = newBuffer; + + return; } -}; -RedisReplyParser.prototype.add_multi_bulk_reply = function (reply) { - if (this.multi_bulk_replies) { - this.multi_bulk_replies[this.multi_bulk_pos] = reply; - this.multi_bulk_pos += 1; - if (this.multi_bulk_pos < this.multi_bulk_length) { - return; - } - } else { - this.multi_bulk_replies = reply; + var bytesRemaining = this._bytesRemaining(); + + var newLength = bytesRemaining + newBuffer.length; + + if (bytesRemaining === 0) { + this._buffer = newBuffer; + this._offset = 0; + + return; } - if (this.multi_bulk_nested_length > 0) { - this.multi_bulk_nested_replies[this.multi_bulk_nested_pos] = this.multi_bulk_replies; - this.multi_bulk_nested_pos += 1; + this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + + this._offset = 0; +}; + +FasterReplyParser.prototype.parseHeader = function () { + var end = this._packetEndOffset(), + value = this._buffer.toString(this._encoding, this._offset, end - 1); + + this._offset = end + 1; + + return value; +}; + +FasterReplyParser.prototype.parseBuffer = function(length) { + var buffer = this._buffer.slice(this._offset, this._offset + length); - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; + this._offset += length; + return buffer; +}; + +FasterReplyParser.prototype._packetEndOffset = function () { + var offset = this._offset; - if (this.multi_bulk_nested_length === this.multi_bulk_nested_pos) { - this.emit("reply", this.multi_bulk_nested_replies); - this.multi_bulk_nested_length = 0; - this.multi_bulk_nested_pos = 0; - this.multi_bulk_nested_replies = null; + while (this._buffer[offset] !== 0x0d && this._buffer[offset + 1] !== 0x0a) { + offset++; + + if (offset >= this._buffer.length) { + throw new Error("didn't see LF after NL reading multi bulk count (" + offset + " => " + this._buffer.length + ", " + this._offset + ")"); } - } else { - this.emit("reply", this.multi_bulk_replies); - this.multi_bulk_length = 0; - this.multi_bulk_replies = null; - this.multi_bulk_pos = 0; } + + offset++; + return offset; }; + +FasterReplyParser.prototype._bytesRemaining = function() { + return (this._buffer.length - this._offset) < 0 ? 0 : (this._buffer.length - this._offset); +}; + +FasterReplyParser.prototype.parser_error = function (message) { + this.emit("error", message); +}; + +FasterReplyParser.prototype.send_error = function (reply) { + this.emit("reply error", reply); +}; + +FasterReplyParser.prototype.send_reply = function (reply) { + this.emit("reply", reply); +}; \ No newline at end of file From 4048349115ec94745d734f84eb79fb5dc3e5c53d Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 05:16:36 -0700 Subject: [PATCH 2/6] some cleanup and comments --- lib/parser/javascript.js | 53 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index bf67573..01c66fe 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -27,42 +27,46 @@ exports.Parser = FasterReplyParser; FasterReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; - if (type === 43 || type === 45) { // + + if (type === 43 || type === 45) { // + or - + // up to the delimiter end = this._packetEndOffset() - 1; start = this._offset; + // include the delimiter this._offset = end + 2; if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.slice(start, end).toString(this._encoding); + return this._buffer.toString(this._encoding, start, end); } } else if (type === 58) { // : + // up to the delimiter end = this._packetEndOffset() - 1; start = this._offset; + // include the delimiter this._offset = end + 2; + // return the coerced numeric value return +this._buffer.toString(this._encoding, start, end); } else if (type === 36) { // $ + // set a rewind point, as the packet could be larger than the + // buffer in memory offset = this._offset - 1; packetHeader = new Packet(type, this.parseHeader()); - if (packetHeader.size === null) { - this._offset++; - - return null; - } - + // packets with a size of -1 are considered null if (packetHeader.size === -1) { return null; } + end = this._offset + packetHeader.size; start = this._offset; + // set the offset to after the delimiter this._offset = end + 2; if (end > this._buffer.length) { @@ -73,7 +77,7 @@ FasterReplyParser.prototype._parseResult = function (type) { if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.slice(start, end).toString(this._encoding); + return this._buffer.toString(this._encoding, start, end); } } else if (type === 42) { // * offset = this._offset; @@ -113,7 +117,7 @@ FasterReplyParser.prototype.execute = function (buffer) { try { var ret; - // at least 4 bytes: *1\r\n + // at least 4 bytes: :1\r\n if (this._bytesRemaining() < 4) { break; } @@ -123,7 +127,7 @@ FasterReplyParser.prototype.execute = function (buffer) { if (type === 43) { // + ret = this._parseResult(type); this.send_reply(ret); - } else if (type === 45) { + } else if (type === 45) { // - ret = this._parseResult(type); this.send_error(ret); } else if (type === 58) { // : @@ -132,11 +136,10 @@ FasterReplyParser.prototype.execute = function (buffer) { } else if (type === 36) { // $ ret = this._parseResult(type); - if (ret === null) { - break; - } this.send_reply(ret); } else if (type === 42) { // * + // set a rewind point. if a failure occurs, + // wait for the next execute()/append() and try again offset = this._offset - 1; ret = this._parseResult(type); if (ret === -1) { @@ -147,6 +150,8 @@ FasterReplyParser.prototype.execute = function (buffer) { this.send_reply(ret); } } catch(err) { + // catch the error (not enough data), rewind, and wait + // for the next packet to appear this._offset = offset; break; } @@ -158,26 +163,23 @@ FasterReplyParser.prototype.append = function(newBuffer) { return; } - var oldBuffer = this._buffer; - if (!oldBuffer) { + // first run + if (this._buffer === null) { this._buffer = newBuffer; return; } - var bytesRemaining = this._bytesRemaining(); - - var newLength = bytesRemaining + newBuffer.length; - - if (bytesRemaining === 0) { + // out of data + if (this._offset >= this._buffer.length) { this._buffer = newBuffer; this._offset = 0; return; } + // very large packet this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); - this._offset = 0; }; @@ -190,13 +192,6 @@ FasterReplyParser.prototype.parseHeader = function () { return value; }; -FasterReplyParser.prototype.parseBuffer = function(length) { - var buffer = this._buffer.slice(this._offset, this._offset + length); - - this._offset += length; - return buffer; -}; - FasterReplyParser.prototype._packetEndOffset = function () { var offset = this._offset; From 8d0f2e72393d112093ec2284b20456485e2016b7 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 08:32:33 -0700 Subject: [PATCH 3/6] some microbenchmark updates, slight speed improvement --- lib/parser/javascript.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 01c66fe..2fe310b 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -24,6 +24,17 @@ util.inherits(FasterReplyParser, events.EventEmitter); exports.Parser = FasterReplyParser; +// Buffer.toString() is quite slow for small strings +function small_toString(buf, start, end) { + var tmp = "", i; + + for (i = start; i < end; i++) { + tmp += String.fromCharCode(buf[i]); + } + + return tmp; +} + FasterReplyParser.prototype._parseResult = function (type) { var start, end, offset, packetHeader; @@ -38,7 +49,11 @@ FasterReplyParser.prototype._parseResult = function (type) { if (this.options.return_buffers) { return this._buffer.slice(start, end); } else { - return this._buffer.toString(this._encoding, start, end); + if (end - start < 65536) { // completely arbitrary + return small_toString(this._buffer, start, end); + } else { + return this._buffer.toString(this._encoding, start, end); + } } } else if (type === 58) { // : // up to the delimiter @@ -49,7 +64,7 @@ FasterReplyParser.prototype._parseResult = function (type) { this._offset = end + 2; // return the coerced numeric value - return +this._buffer.toString(this._encoding, start, end); + return +small_toString(this._buffer, start, end); } else if (type === 36) { // $ // set a rewind point, as the packet could be larger than the // buffer in memory @@ -185,8 +200,8 @@ FasterReplyParser.prototype.append = function(newBuffer) { FasterReplyParser.prototype.parseHeader = function () { var end = this._packetEndOffset(), - value = this._buffer.toString(this._encoding, this._offset, end - 1); - + value = small_toString(this._buffer, this._offset, end - 1); + this._offset = end + 1; return value; From 34568a4bb39ec6d566f46796f1852d78987eb2a8 Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Sun, 8 Jul 2012 14:54:48 -0700 Subject: [PATCH 4/6] added fallback if missing Buffer.concat() --- lib/parser/javascript.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 2fe310b..1c1c410 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -194,7 +194,19 @@ FasterReplyParser.prototype.append = function(newBuffer) { } // very large packet - this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + // check for concat, if we have it, use it + if (Buffer.concat !== undefined) { + this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); + } else { + var remaining = this._bytesRemaining(); + var newLength = remaining + newBuffer.length; + var tmpBuffer = new Buffer(newLength); + + this._buffer.copy(tmpBuffer, 0, this._offset); + newBuffer.copy(tmpBuffer, remaining, 0); + + this._buffer = tmpBuffer; + } this._offset = 0; }; From 8be55fb3044b1160d0f7d854f127f5c3f0da285b Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Wed, 22 Aug 2012 21:53:18 -0700 Subject: [PATCH 5/6] fix all but lrange 20k --- lib/parser/javascript.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index 1c1c410..b35fd84 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -126,7 +126,7 @@ FasterReplyParser.prototype._parseResult = function (type) { FasterReplyParser.prototype.execute = function (buffer) { this.append(buffer); - + var type; while (true) { var offset = this._offset; try { @@ -137,20 +137,33 @@ FasterReplyParser.prototype.execute = function (buffer) { break; } - var type = this._buffer[this._offset++]; + type = this._buffer[this._offset++]; + if (type === 43) { // + ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(ret); } else if (type === 45) { // - ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_error(ret); } else if (type === 58) { // : ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(+ret); } else if (type === 36) { // $ ret = this._parseResult(type); + if (ret === null) { + break; + } this.send_reply(ret); } else if (type === 42) { // * // set a rewind point. if a failure occurs, @@ -167,8 +180,8 @@ FasterReplyParser.prototype.execute = function (buffer) { } catch(err) { // catch the error (not enough data), rewind, and wait // for the next packet to appear - this._offset = offset; - break; + this._offset = offset; + break; } } }; @@ -207,6 +220,7 @@ FasterReplyParser.prototype.append = function(newBuffer) { this._buffer = tmpBuffer; } + this._offset = 0; }; @@ -245,7 +259,7 @@ FasterReplyParser.prototype.parser_error = function (message) { FasterReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; - +var count = 0; FasterReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); }; \ No newline at end of file From 3d27cb23ee2e7c02abde4aff332074b2765f803b Mon Sep 17 00:00:00 2001 From: Jerry Sievert Date: Thu, 23 Aug 2012 13:09:05 -0700 Subject: [PATCH 6/6] update for style changes --- lib/parser/javascript.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/parser/javascript.js b/lib/parser/javascript.js index b35fd84..07a0962 100644 --- a/lib/parser/javascript.js +++ b/lib/parser/javascript.js @@ -109,10 +109,12 @@ FasterReplyParser.prototype._parseResult = function (type) { } var reply = [ ]; + var ntype, i; + offset = this._offset - 1; - for (var i = 0; i < packetHeader.size; i++) { - var ntype = this._buffer[this._offset++]; + for (i = 0; i < packetHeader.size; i++) { + ntype = this._buffer[this._offset++]; if (this._offset === this._buffer.length) { throw new Error('too far'); @@ -126,12 +128,12 @@ FasterReplyParser.prototype._parseResult = function (type) { FasterReplyParser.prototype.execute = function (buffer) { this.append(buffer); - var type; + + var type, ret, offset; + while (true) { - var offset = this._offset; + offset = this._offset; try { - var ret; - // at least 4 bytes: :1\r\n if (this._bytesRemaining() < 4) { break; @@ -142,21 +144,27 @@ FasterReplyParser.prototype.execute = function (buffer) { if (type === 43) { // + ret = this._parseResult(type); + if (ret === null) { break; } + this.send_reply(ret); } else if (type === 45) { // - ret = this._parseResult(type); + if (ret === null) { break; } + this.send_error(ret); } else if (type === 58) { // : ret = this._parseResult(type); + if (ret === null) { break; } + this.send_reply(+ret); } else if (type === 36) { // $ ret = this._parseResult(type); @@ -164,12 +172,15 @@ FasterReplyParser.prototype.execute = function (buffer) { if (ret === null) { break; } + this.send_reply(ret); } else if (type === 42) { // * // set a rewind point. if a failure occurs, // wait for the next execute()/append() and try again offset = this._offset - 1; + ret = this._parseResult(type); + if (ret === -1) { this._offset = offset; break; @@ -178,8 +189,8 @@ FasterReplyParser.prototype.execute = function (buffer) { this.send_reply(ret); } } catch(err) { - // catch the error (not enough data), rewind, and wait - // for the next packet to appear + // catch the error (not enough data), rewind, and wait + // for the next packet to appear this._offset = offset; break; } @@ -211,9 +222,9 @@ FasterReplyParser.prototype.append = function(newBuffer) { if (Buffer.concat !== undefined) { this._buffer = Buffer.concat([this._buffer.slice(this._offset), newBuffer]); } else { - var remaining = this._bytesRemaining(); - var newLength = remaining + newBuffer.length; - var tmpBuffer = new Buffer(newLength); + var remaining = this._bytesRemaining(), + newLength = remaining + newBuffer.length, + tmpBuffer = new Buffer(newLength); this._buffer.copy(tmpBuffer, 0, this._offset); newBuffer.copy(tmpBuffer, remaining, 0); @@ -259,7 +270,7 @@ FasterReplyParser.prototype.parser_error = function (message) { FasterReplyParser.prototype.send_error = function (reply) { this.emit("reply error", reply); }; -var count = 0; + FasterReplyParser.prototype.send_reply = function (reply) { this.emit("reply", reply); }; \ No newline at end of file