Skip to content

Commit 8b77169

Browse files
committed
Merge pull request #5 from NodeRedis/interfering-parsers
v.2.0.1
2 parents c7d974f + facb55b commit 8b77169

File tree

5 files changed

+97
-21
lines changed

5 files changed

+97
-21
lines changed

.codeclimate.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
languages:
2+
JavaScript: true
3+
exclude_paths:
4+
- "benchmark/index.js"
5+
- "benchmark/old/javascript.js"

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v.2.0.1 - 0x Jun, 2016
2+
3+
Bugfixes
4+
5+
- Fixed multiple parsers working concurrently resulting in faulty data in some cases
6+
17
## v.2.0.0 - 29 May, 2016
28

39
The javascript parser got completly rewritten by [Michael Diarmid](https://github.com/Salakar) and [Ruben Bridgewater](https://github.com/BridgeAR) and is now a lot faster than the hiredis parser.

lib/parser.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,6 @@ function parseBulkString (parser) {
135135
var offsetEnd = parser.offset + length
136136
if (offsetEnd + 2 > parser.buffer.length) {
137137
parser.bigStrSize = offsetEnd + 2
138-
parser.bigOffset = parser.offset
139-
parser.totalChunkSize = parser.buffer.length
140-
parser.bufferCache.push(parser.buffer)
141138
return
142139
}
143140

@@ -334,7 +331,7 @@ function concatBuffer (parser, length) {
334331
list[i].copy(bufferPool, pos)
335332
pos += list[i].length
336333
}
337-
return bufferPool.slice(parser.offset, length)
334+
return bufferPool.slice(0, length)
338335
}
339336

340337
/**
@@ -348,25 +345,22 @@ JavascriptRedisParser.prototype.execute = function (buffer) {
348345
this.offset = 0
349346
} else if (this.bigStrSize === 0) {
350347
var oldLength = this.buffer.length
351-
var remainingLength = oldLength - this.offset
352-
var newLength = remainingLength + buffer.length
348+
var newLength = oldLength + buffer.length
353349
// ~ 5% speed increase over using new Buffer(length) all the time
354350
if (bufferPool.length < newLength) { // We can't rely on the chunk size
355351
bufferPool = new Buffer(newLength)
356352
}
357-
this.buffer.copy(bufferPool, 0, this.offset, oldLength)
358-
buffer.copy(bufferPool, remainingLength, 0, buffer.length)
353+
this.buffer.copy(bufferPool, 0, 0, oldLength)
354+
buffer.copy(bufferPool, oldLength, 0, buffer.length)
359355
this.buffer = bufferPool.slice(0, newLength)
360-
this.offset = 0
361356
} else if (this.totalChunkSize + buffer.length >= this.bigStrSize) {
362357
this.bufferCache.push(buffer)
363358
// The returned type might be Array * (42) and in that case we can't improve the parsing currently
364-
if (this.optionReturnBuffers === false && this.buffer[this.offset] === 36) {
359+
if (this.optionReturnBuffers === false && this.buffer[0] === 36) {
365360
this.returnReply(concatBulkString(this))
366361
this.buffer = buffer
367362
} else { // This applies for arrays too
368363
this.buffer = concatBuffer(this, this.totalChunkSize + buffer.length)
369-
this.offset = 0
370364
}
371365
this.bigStrSize = 0
372366
this.totalChunkSize = 0
@@ -382,7 +376,19 @@ JavascriptRedisParser.prototype.execute = function (buffer) {
382376
var type = this.buffer[this.offset++]
383377
var response = parseType(this, type)
384378
if (response === undefined) {
385-
this.offset = offset
379+
if (this.buffer === null) {
380+
return
381+
}
382+
var tempBuffer = new Buffer(this.buffer.length - offset)
383+
this.buffer.copy(tempBuffer, 0, offset, this.buffer.length)
384+
this.buffer = tempBuffer
385+
if (this.bigStrSize !== 0) {
386+
this.bigStrSize -= offset
387+
this.bigOffset = this.offset - offset
388+
this.totalChunkSize = this.buffer.length
389+
this.bufferCache.push(this.buffer)
390+
}
391+
this.offset = 0
386392
return
387393
}
388394

lib/replyError.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,17 @@ function ReplyError (message) {
77
Error.stackTraceLimit = 2
88
Error.captureStackTrace(this, this.constructor)
99
Error.stackTraceLimit = limit
10-
Object.defineProperty(this, 'name', {
11-
value: 'ReplyError',
12-
configurable: false,
13-
enumerable: false,
14-
writable: true
15-
})
1610
Object.defineProperty(this, 'message', {
1711
value: message || '',
18-
configurable: false,
19-
enumerable: false,
2012
writable: true
2113
})
2214
}
2315

2416
util.inherits(ReplyError, Error)
2517

18+
Object.defineProperty(ReplyError.prototype, 'name', {
19+
value: 'ReplyError',
20+
writable: true
21+
})
22+
2623
module.exports = ReplyError

test/parsers.spec.js

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var parsers = [JavascriptParser, HiredisParser]
1111
// Mock the not needed return functions
1212
function returnReply () { throw new Error('failed') }
1313
function returnError () { throw new Error('failed') }
14-
function returnFatalError () { throw new Error('failed') }
14+
function returnFatalError (err) { throw err }
1515

1616
describe('parsers', function () {
1717
describe('general parser functionality', function () {
@@ -64,6 +64,68 @@ describe('parsers', function () {
6464

6565
parsers.forEach(function (Parser) {
6666
describe(Parser.name, function () {
67+
it('multiple parsers do not interfere', function () {
68+
var replyCount = 0
69+
var results = [1234567890, 'foo bar baz', 'hello world']
70+
function checkReply (reply) {
71+
assert.strictEqual(results[replyCount], reply)
72+
replyCount++
73+
}
74+
var parserOne = new Parser({
75+
returnReply: checkReply,
76+
returnError: returnError,
77+
returnFatalError: returnFatalError
78+
})
79+
var parserTwo = new Parser({
80+
returnReply: checkReply,
81+
returnError: returnError,
82+
returnFatalError: returnFatalError
83+
})
84+
parserOne.execute(new Buffer('+foo '))
85+
parserOne.execute(new Buffer('bar '))
86+
assert.strictEqual(replyCount, 0)
87+
parserTwo.execute(new Buffer(':1234567890\r\n+hello '))
88+
assert.strictEqual(replyCount, 1)
89+
parserTwo.execute(new Buffer('wor'))
90+
parserOne.execute(new Buffer('baz\r\n'))
91+
assert.strictEqual(replyCount, 2)
92+
parserTwo.execute(new Buffer('ld\r\n'))
93+
assert.strictEqual(replyCount, 3)
94+
})
95+
96+
it('multiple parsers do not interfere with bulk strings in arrays', function () {
97+
var replyCount = 0
98+
var results = [['foo', 'foo bar baz'], [1234567890, 'hello world', 'the end'], 'ttttttttttttttttttttttttttttttttttttttttttttttt']
99+
function checkReply (reply) {
100+
console.log(reply)
101+
assert.deepEqual(results[replyCount], reply)
102+
replyCount++
103+
}
104+
var parserOne = new Parser({
105+
returnReply: checkReply,
106+
returnError: returnError,
107+
returnFatalError: returnFatalError
108+
})
109+
var parserTwo = new Parser({
110+
returnReply: checkReply,
111+
returnError: returnError,
112+
returnFatalError: returnFatalError
113+
})
114+
parserOne.execute(new Buffer('*2\r\n+foo\r\n$11\r\nfoo '))
115+
parserOne.execute(new Buffer('bar '))
116+
assert.strictEqual(replyCount, 0)
117+
parserTwo.execute(new Buffer('*3\r\n:1234567890\r\n$11\r\nhello '))
118+
assert.strictEqual(replyCount, 0)
119+
parserOne.execute(new Buffer('baz\r\n+ttttttttttttttttttttttttt'))
120+
assert.strictEqual(replyCount, 1)
121+
parserTwo.execute(new Buffer('wor'))
122+
parserTwo.execute(new Buffer('ld\r\n'))
123+
assert.strictEqual(replyCount, 1)
124+
parserTwo.execute(new Buffer('+the end\r\n'))
125+
assert.strictEqual(replyCount, 2)
126+
parserOne.execute(new Buffer('tttttttttttttttttttttt\r\n'))
127+
})
128+
67129
it('chunks getting to big for the bufferPool', function () {
68130
// This is a edge case. Chunks should not exceed Math.pow(2, 16) bytes
69131
var lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' +

0 commit comments

Comments
 (0)