Skip to content

Commit ca57a68

Browse files
authored
Merge pull request #20 from NodeRedis/improve-string-numbers
Improve string numbers
2 parents f14a73b + 583046b commit ca57a68

File tree

5 files changed

+49
-27
lines changed

5 files changed

+49
-27
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var myParser = new Parser(options);
2929
* `returnError`: *function*; mandatory
3030
* `returnFatalError`: *function*; optional, defaults to the returnError function
3131
* `returnBuffers`: *boolean*; optional, defaults to false
32+
* `stringNumbers`: *boolean*; optional, defaults to false
3233

3334
### Example
3435

@@ -66,7 +67,7 @@ You do not have to use the returnFatalError function. Fatal errors will be retur
6667

6768
And if you want to return buffers instead of strings, you can do this by adding the `returnBuffers` option.
6869

69-
Big numbers that are too large for JS are automatically stringified.
70+
If you handle with big numbers that are to large for JS (Number.MAX_SAFE_INTEGER === 2^53 - 16) please use the `stringNumbers` option. That way all numbers are going to be returned as String and you can handle them safely.
7071

7172
```js
7273
// Same functions as in the first example
@@ -78,7 +79,8 @@ var parser = new Parser({
7879
returnError: function(err) {
7980
lib.returnError(err);
8081
},
81-
returnBuffers: true // All strings are returned as buffer e.g. <Buffer 48 65 6c 6c 6f>
82+
returnBuffers: true, // All strings are returned as Buffer e.g. <Buffer 48 65 6c 6c 6f>
83+
stringNumbers: true // All numbers are returned as String
8284
});
8385

8486
// The streamHandler as above

benchmark/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +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
37+
var bigIntegerBuffer = new Buffer(':184467440737095516171234567890\r\n') // 2^64 + 1
3838
var errorBuffer = new Buffer('-Error ohnoesitbroke\r\n')
3939
var arrayBuffer = new Buffer('*1\r\n*1\r\n$1\r\na\r\n')
4040
var endBuffer = new Buffer('\r\n')

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v.2.2.0 - 31 Oct, 2016
2+
3+
Features
4+
5+
- Improve `stringNumbers` parsing performance by up to 100%
6+
17
## v.2.1.1 - 31 Oct, 2016
28

39
Bugfixes

lib/parser.js

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,47 +16,61 @@ var notDecreased = 0
1616
*/
1717
function parseSimpleNumbers (parser) {
1818
var offset = parser.offset
19-
var length = parser.buffer.length
19+
var length = parser.buffer.length - 1
2020
var number = 0
21-
var sign = false
21+
var sign = 1
2222

2323
if (parser.buffer[offset] === 45) {
24-
sign = true
24+
sign = -1
2525
offset++
2626
}
2727

2828
while (offset < length) {
2929
var c1 = parser.buffer[offset++]
30-
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
30+
if (c1 === 13) { // \r\n
3131
parser.offset = offset + 1
32-
return sign ? -number : number
32+
return sign * number
3333
}
3434
number = (number * 10) + (c1 - 48)
3535
}
3636
}
3737

3838
/**
3939
* Used for integer numbers in case of the returnNumbers option
40+
*
41+
* The maximimum possible integer to use is: Math.floor(Number.MAX_SAFE_INTEGER / 10)
42+
* Staying in a SMI Math.floor((Math.pow(2, 32) / 10) - 1) is even more efficient though
43+
*
4044
* @param parser
4145
* @returns {*}
4246
*/
4347
function parseStringNumbers (parser) {
4448
var offset = parser.offset
45-
var length = parser.buffer.length
46-
var number = ''
49+
var length = parser.buffer.length - 1
50+
var number = 0
51+
var res = ''
4752

4853
if (parser.buffer[offset] === 45) {
49-
number += '-'
54+
res += '-'
5055
offset++
5156
}
5257

5358
while (offset < length) {
5459
var c1 = parser.buffer[offset++]
55-
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
60+
if (c1 === 13) { // \r\n
5661
parser.offset = offset + 1
57-
return number
62+
if (number !== 0) {
63+
res += number
64+
}
65+
return res
66+
} else if (number > 429496728) {
67+
res += (number * 10) + (c1 - 48)
68+
number = 0
69+
} else if (c1 === 48 && number === 0) {
70+
res += 0
71+
} else {
72+
number = (number * 10) + (c1 - 48)
5873
}
59-
number += c1 - 48
6074
}
6175
}
6276

@@ -88,13 +102,13 @@ function convertBufferRange (parser, start, end) {
88102
*/
89103
function parseSimpleStringViaOffset (parser) {
90104
var start = parser.offset
91-
var offset = parser.offset
92-
var length = parser.buffer.length
105+
var offset = start
93106
var buffer = parser.buffer
107+
var length = buffer.length - 1
94108

95109
while (offset < length) {
96-
if (buffer[offset++] === 10) { // \r\n
97-
return convertBufferRange(parser, start, offset - 2)
110+
if (buffer[offset++] === 13) { // \r\n
111+
return convertBufferRange(parser, start, offset - 1)
98112
}
99113
}
100114
}
@@ -107,7 +121,7 @@ function parseSimpleStringViaOffset (parser) {
107121
function parseLength (parser) {
108122
var string = parseSimpleNumbers(parser)
109123
if (string !== undefined) {
110-
return +string
124+
return string
111125
}
112126
}
113127

@@ -193,7 +207,7 @@ function parseArray (parser) {
193207
}
194208

195209
var responses = new Array(length)
196-
var bufferLength = parser.buffer.length
210+
var bufferLength = parser.buffer.length - 3
197211
for (var i = 0; i < length; i++) {
198212
if (parser.offset >= bufferLength) {
199213
return
@@ -305,14 +319,14 @@ function concatBulkString (parser) {
305319
if (chunks === 2) {
306320
return list[0].toString('utf8', parser.bigOffset, list[0].length - 1)
307321
}
308-
} else {
309-
chunks++
322+
chunks--
323+
offset = list[list.length - 2].length + 1
310324
}
311325
var res = decoder.write(list[0].slice(parser.bigOffset))
312-
for (var i = 1; i < chunks - 2; i++) {
326+
for (var i = 1; i < chunks - 1; i++) {
313327
res += decoder.write(list[i])
314328
}
315-
res += decoder.end(list[i].slice(0, offset === 1 ? list[i].length - 1 : offset - 2))
329+
res += decoder.end(list[i].slice(0, offset - 2))
316330
return res
317331
}
318332

test/parsers.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ describe('parsers', function () {
541541
return this.skip()
542542
}
543543
var replyCount = 0
544-
var entries = ['123', '590295810358705700002', '-99999999999999999']
544+
var entries = ['123', '590295810358705700002', '-99999999999999999', '4294967290', '90071992547409920', '10000040000000000000000000000000000000020']
545545
function checkReply (reply) {
546546
assert.strictEqual(typeof reply, 'string')
547547
assert.strictEqual(reply, entries[replyCount])
@@ -551,8 +551,8 @@ describe('parsers', function () {
551551
returnReply: checkReply,
552552
stringNumbers: true
553553
})
554-
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n'))
555-
assert.strictEqual(replyCount, 3)
554+
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n:4294967290\r\n:90071992547409920\r\n:10000040000000000000000000000000000000020\r\n'))
555+
assert.strictEqual(replyCount, 6)
556556
})
557557

558558
it('handle big numbers', function () {

0 commit comments

Comments
 (0)