Skip to content

Commit 189444a

Browse files
author
Ruben Bridgewater
committed
Improve number parsing further
1 parent ad578bf commit 189444a

File tree

3 files changed

+75
-24
lines changed

3 files changed

+75
-24
lines changed

benchmark/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var startBuffer = new Buffer('$100\r\nabcdefghij')
3434
var chunkBuffer = new Buffer('abcdefghijabcdefghijabcdefghij')
3535
var stringBuffer = new Buffer('+testing a simple string\r\n')
3636
var integerBuffer = new Buffer(':1237884\r\n')
37+
var bigIntegerBuffer = new Buffer(':18446744073709551617\r\n') // 2^64 + 1
3738
var errorBuffer = new Buffer('-Error ohnoesitbroke\r\n')
3839
var arrayBuffer = new Buffer('*1\r\n*1\r\n$1\r\na\r\n')
3940
var endBuffer = new Buffer('\r\n')
@@ -158,6 +159,20 @@ suite.add('NEW CODE: + integer', function () {
158159
parser.execute(integerBuffer)
159160
})
160161

162+
// BIG INTEGER
163+
164+
suite.add('\nOLD CODE: + big integer', function () {
165+
parserOld.execute(bigIntegerBuffer)
166+
})
167+
168+
suite.add('HIREDIS: + big integer', function () {
169+
parserHiRedis.execute(bigIntegerBuffer)
170+
})
171+
172+
suite.add('NEW CODE: + big integer', function () {
173+
parser.execute(bigIntegerBuffer)
174+
})
175+
161176
// ARRAYS
162177

163178
suite.add('\nOLD CODE: * array', function () {

lib/parser.js

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,52 @@ var ReplyError = require('./replyError')
44

55
/**
66
* Used for lengths and numbers only, faster perf on arrays / bulks
7-
* Don't use this for strings, as fromCharCode returns utf-16 and breaks utf-8 compatibility
87
* @param parser
98
* @returns {*}
109
*/
1110
function parseSimpleNumbers (parser) {
1211
var offset = parser.offset
1312
var length = parser.buffer.length
14-
var string = ''
15-
var c1 = parser.buffer[offset++]
13+
var number = 0
14+
var sign = false
1615

17-
if (c1 === 45) {
18-
string += '-'
19-
} else {
20-
string += c1 - 48
16+
if (parser.buffer[offset] === 45) {
17+
sign = true
18+
offset++
19+
}
20+
21+
while (offset < length) {
22+
var c1 = parser.buffer[offset++]
23+
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
24+
parser.offset = offset + 1
25+
return sign ? -number : number
26+
}
27+
number = (number * 10) + (c1 - 48)
28+
}
29+
}
30+
31+
/**
32+
* Used for integer numbers in case of the returnNumbers option
33+
* @param parser
34+
* @returns {*}
35+
*/
36+
function parseStringNumbers (parser) {
37+
var offset = parser.offset
38+
var length = parser.buffer.length
39+
var number = ''
40+
41+
if (parser.buffer[offset] === 45) {
42+
number += '-'
43+
offset++
2144
}
2245

2346
while (offset < length) {
24-
c1 = parser.buffer[offset++]
47+
var c1 = parser.buffer[offset++]
2548
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
2649
parser.offset = offset + 1
27-
return string
50+
return number
2851
}
29-
string += c1 - 48
52+
number += c1 - 48
3053
}
3154
}
3255

@@ -85,16 +108,13 @@ function parseLength (parser) {
85108
* @returns {*}
86109
*/
87110
function parseInteger (parser) {
88-
var string = parseSimpleNumbers(parser)
89-
if (string !== undefined) {
90-
// If stringNumbers is activated the parser always returns numbers as string
91-
// This is important for big numbers (number > Math.pow(2, 53)) as js numbers
92-
// are 64bit floating point numbers with reduced precision
93-
if (parser.optionStringNumbers === false) {
94-
return +string
95-
}
96-
return string
111+
// If stringNumbers is activated the parser always returns numbers as string
112+
// This is important for big numbers (number > Math.pow(2, 53)) as js numbers
113+
// are 64bit floating point numbers with reduced precision
114+
if (parser.optionStringNumbers) {
115+
return parseStringNumbers(parser)
97116
}
117+
return parseSimpleNumbers(parser)
98118
}
99119

100120
/**

test/parsers.spec.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
/* eslint-env mocha */
44

5-
var intercept = require('intercept-stdout')
65
var assert = require('assert')
76
var JavascriptParser = require('../')
87
var HiredisParser = require('./hiredis')
@@ -420,19 +419,36 @@ describe('parsers', function () {
420419
assert.strictEqual(reply, entries[replyCount])
421420
replyCount++
422421
}
423-
var unhookIntercept = intercept(function () {
424-
return ''
425-
})
426422
var parser = new Parser({
427423
returnReply: checkReply,
428424
returnError: returnError,
429425
returnFatalError: returnFatalError,
430426
stringNumbers: true
431427
})
432-
unhookIntercept()
433428
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n'))
434429
assert.strictEqual(replyCount, 3)
435430
})
431+
432+
it('handle big numbers', function () {
433+
if (Parser.name === 'HiredisReplyParser') {
434+
return this.skip()
435+
}
436+
var replyCount = 0
437+
var number = 9007199254740991 // Number.MAX_SAFE_INTEGER
438+
function checkReply (reply) {
439+
assert.strictEqual(reply, number++)
440+
replyCount++
441+
}
442+
var parser = new Parser({
443+
returnReply: checkReply,
444+
returnError: returnError,
445+
returnFatalError: returnFatalError
446+
})
447+
parser.execute(new Buffer(':' + number + '\r\n'))
448+
assert.strictEqual(replyCount, 1)
449+
parser.execute(new Buffer(':' + number + '\r\n'))
450+
assert.strictEqual(replyCount, 2)
451+
})
436452
})
437453
})
438454
})

0 commit comments

Comments
 (0)