Skip to content

Commit 16d7e7a

Browse files
author
Ruben Bridgewater
committed
Fix bulk string concat
1 parent 86de5a0 commit 16d7e7a

File tree

2 files changed

+35
-27
lines changed

2 files changed

+35
-27
lines changed

lib/parser.js

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -281,27 +281,26 @@ function JavascriptRedisParser (options) {
281281
* @param buffer
282282
* @returns {String}
283283
*/
284-
function concatBigString (parser, buffer) {
284+
function concatBulkString (parser) {
285285
var list = parser.bufferCache
286-
var length = parser.bigStrSize
287-
var stringSize = length - parser.bigOffset - 2
288-
var res = list[0].toString('utf8', parser.bigOffset)
289-
for (var i = 1; i < list.length - 1; i++) {
290-
res += list[i].toString()
291-
}
292-
if (i > 1) {
293-
if (res.length + list[i].length <= stringSize) {
294-
res += list[i].toString()
295-
} else {
296-
res += list[i].toString('utf8', 0, list[i].length - 1)
286+
// The first chunk might contain the whole bulk string including the \r
287+
var end = parser.totalChunkSize - parser.bigStrSize === -1
288+
var chunks = list.length
289+
if (end) {
290+
parser.offset = 1
291+
if (chunks === 2) {
292+
return list[0].toString('utf8', parser.bigOffset, list[0].length - 1)
297293
}
294+
} else {
295+
parser.offset = parser.bigStrSize - parser.totalChunkSize
296+
chunks++
298297
}
299-
var offset = stringSize - res.length
300-
if (offset > 0) {
301-
res += buffer.toString('utf8', 0, offset)
298+
var res = list[0].toString('utf8', parser.bigOffset)
299+
for (var i = 1; i < chunks - 2; i++) {
300+
// We are only safe to fully add up elements that are neither the first nor any of the last two elements
301+
res += list[i].toString()
302302
}
303-
parser.offset = offset + 2
304-
parser.bigOffset = 0
303+
res += list[i].toString('utf8', 0, end ? list[i].length - 1 : parser.offset - 2)
305304
return res
306305
}
307306

@@ -313,7 +312,6 @@ function decreaseBufferPool () {
313312
if (bufferPool.length > 96 * 1024) {
314313
// Decrease the bufferPool by 16kb
315314
bufferPool = bufferPool.slice(0, bufferPool.length - 16 * 1024)
316-
// console.log(bufferPool.length)
317315
} else {
318316
clearInterval(interval)
319317
interval = null
@@ -326,7 +324,7 @@ function decreaseBufferPool () {
326324
* @param length
327325
* @returns {Buffer}
328326
*/
329-
function concat (parser, length) {
327+
function concatBuffer (parser, length) {
330328
var list = parser.bufferCache
331329
var pos = 0
332330
if (bufferPool.length < length) {
@@ -365,13 +363,14 @@ JavascriptRedisParser.prototype.execute = function (buffer) {
365363
this.buffer = newBuffer.slice(0, newLength)
366364
this.offset = 0
367365
} else if (this.totalChunkSize + buffer.length >= this.bigStrSize) {
366+
this.bufferCache.push(buffer)
368367
// The returned type might be Array * (42) and in that case we can't improve the parsing currently
369368
if (this.optionReturnBuffers === false && this.buffer[this.offset] === 36) {
370-
this.returnReply(concatBigString(this, buffer))
369+
this.returnReply(concatBulkString(this))
370+
this.bigOffset = 0
371371
this.buffer = buffer
372372
} else { // This applies for arrays too
373-
this.bufferCache.push(buffer)
374-
this.buffer = concat(this, this.totalChunkSize + buffer.length)
373+
this.buffer = concatBuffer(this, this.totalChunkSize + buffer.length)
375374
this.offset = 0
376375
}
377376
this.bigStrSize = 0

test/parsers.spec.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ describe('parsers', function () {
7070
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' +
7171
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ' +
7272
'ut aliquip ex ea commodo consequat. Duis aute irure dolor in' // 256 chars
73-
var bigString = (new Array(Math.pow(2, 17) / lorem.length).join(lorem)) // Math.pow(2, 17) chars long
73+
var bigString = (new Array(Math.pow(2, 17) / lorem.length + 1).join(lorem)) // Math.pow(2, 17) chars long
7474
var replyCount = 0
75+
var sizes = [4, Math.pow(2, 17)]
7576
function checkReply (reply) {
77+
assert.strictEqual(sizes[replyCount], reply.length)
7678
replyCount++
7779
}
7880
var parser = new Parser({
@@ -200,7 +202,7 @@ describe('parsers', function () {
200202
it('should handle \\r and \\n characters properly', function () {
201203
// If a string contains \r or \n characters it will always be send as a bulk string
202204
var replyCount = 0
203-
var entries = ['foo\r', 'foo\r\nbar', '\r\nfoo', 'foo\r\n', 'foo']
205+
var entries = ['foo\r', 'foo\r\nbar', '\r\nfoo', 'foo\r\n', 'foo', 'foobar', 'foo\r', 'äfooöü', 'abc']
204206
function checkReply (reply) {
205207
assert.strictEqual(reply, entries[replyCount])
206208
replyCount++
@@ -217,8 +219,16 @@ describe('parsers', function () {
217219
assert.strictEqual(replyCount, 4)
218220
parser.execute(new Buffer('+foo\r'))
219221
assert.strictEqual(replyCount, 4)
220-
parser.execute(new Buffer('\n'))
222+
parser.execute(new Buffer('\n$6\r\nfoobar\r'))
221223
assert.strictEqual(replyCount, 5)
224+
parser.execute(new Buffer('\n$4\r\nfoo\r\r\n'))
225+
assert.strictEqual(replyCount, 7)
226+
parser.execute(new Buffer('$9\r\näfo'))
227+
parser.execute(new Buffer('oö'))
228+
parser.execute(new Buffer('ü\r'))
229+
assert.strictEqual(replyCount, 7)
230+
parser.execute(new Buffer('\n+abc\r\n'))
231+
assert.strictEqual(replyCount, 9)
222232
})
223233

224234
it('line breaks in the beginning of the last chunk', function () {
@@ -271,10 +281,9 @@ describe('parsers', function () {
271281
parser.execute(new Buffer(
272282
'abcdefghij\r\n' +
273283
'$100\r\nabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij\r\n' +
274-
'$100\r\nabcdefghijabcdefghijabcdefghijabcdefghij'
284+
'$100\r\nabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij'
275285
))
276286
assert.strictEqual(replyCount, 3)
277-
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij'))
278287
parser.execute(new Buffer('abcdefghijabcdefghijabcdefghij\r'))
279288
assert.strictEqual(replyCount, 3)
280289
parser.execute(new Buffer('\n'))

0 commit comments

Comments
 (0)