Skip to content

Commit

Permalink
Merge pull request #20 from NodeRedis/improve-string-numbers
Browse files Browse the repository at this point in the history
Improve string numbers
  • Loading branch information
BridgeAR authored Nov 18, 2016
2 parents f14a73b + 583046b commit ca57a68
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 27 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var myParser = new Parser(options);
* `returnError`: *function*; mandatory
* `returnFatalError`: *function*; optional, defaults to the returnError function
* `returnBuffers`: *boolean*; optional, defaults to false
* `stringNumbers`: *boolean*; optional, defaults to false

### Example

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

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

Big numbers that are too large for JS are automatically stringified.
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.

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

// The streamHandler as above
Expand Down
2 changes: 1 addition & 1 deletion benchmark/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var startBuffer = new Buffer('$100\r\nabcdefghij')
var chunkBuffer = new Buffer('abcdefghijabcdefghijabcdefghij')
var stringBuffer = new Buffer('+testing a simple string\r\n')
var integerBuffer = new Buffer(':1237884\r\n')
var bigIntegerBuffer = new Buffer(':18446744073709551617\r\n') // 2^64 + 1
var bigIntegerBuffer = new Buffer(':184467440737095516171234567890\r\n') // 2^64 + 1
var errorBuffer = new Buffer('-Error ohnoesitbroke\r\n')
var arrayBuffer = new Buffer('*1\r\n*1\r\n$1\r\na\r\n')
var endBuffer = new Buffer('\r\n')
Expand Down
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## v.2.2.0 - 31 Oct, 2016

Features

- Improve `stringNumbers` parsing performance by up to 100%

## v.2.1.1 - 31 Oct, 2016

Bugfixes
Expand Down
56 changes: 35 additions & 21 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,47 +16,61 @@ var notDecreased = 0
*/
function parseSimpleNumbers (parser) {
var offset = parser.offset
var length = parser.buffer.length
var length = parser.buffer.length - 1
var number = 0
var sign = false
var sign = 1

if (parser.buffer[offset] === 45) {
sign = true
sign = -1
offset++
}

while (offset < length) {
var c1 = parser.buffer[offset++]
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
if (c1 === 13) { // \r\n
parser.offset = offset + 1
return sign ? -number : number
return sign * number
}
number = (number * 10) + (c1 - 48)
}
}

/**
* Used for integer numbers in case of the returnNumbers option
*
* The maximimum possible integer to use is: Math.floor(Number.MAX_SAFE_INTEGER / 10)
* Staying in a SMI Math.floor((Math.pow(2, 32) / 10) - 1) is even more efficient though
*
* @param parser
* @returns {*}
*/
function parseStringNumbers (parser) {
var offset = parser.offset
var length = parser.buffer.length
var number = ''
var length = parser.buffer.length - 1
var number = 0
var res = ''

if (parser.buffer[offset] === 45) {
number += '-'
res += '-'
offset++
}

while (offset < length) {
var c1 = parser.buffer[offset++]
if (c1 === 13 && parser.buffer[offset] === 10) { // \r\n
if (c1 === 13) { // \r\n
parser.offset = offset + 1
return number
if (number !== 0) {
res += number
}
return res
} else if (number > 429496728) {
res += (number * 10) + (c1 - 48)
number = 0
} else if (c1 === 48 && number === 0) {
res += 0
} else {
number = (number * 10) + (c1 - 48)
}
number += c1 - 48
}
}

Expand Down Expand Up @@ -88,13 +102,13 @@ function convertBufferRange (parser, start, end) {
*/
function parseSimpleStringViaOffset (parser) {
var start = parser.offset
var offset = parser.offset
var length = parser.buffer.length
var offset = start
var buffer = parser.buffer
var length = buffer.length - 1

while (offset < length) {
if (buffer[offset++] === 10) { // \r\n
return convertBufferRange(parser, start, offset - 2)
if (buffer[offset++] === 13) { // \r\n
return convertBufferRange(parser, start, offset - 1)
}
}
}
Expand All @@ -107,7 +121,7 @@ function parseSimpleStringViaOffset (parser) {
function parseLength (parser) {
var string = parseSimpleNumbers(parser)
if (string !== undefined) {
return +string
return string
}
}

Expand Down Expand Up @@ -193,7 +207,7 @@ function parseArray (parser) {
}

var responses = new Array(length)
var bufferLength = parser.buffer.length
var bufferLength = parser.buffer.length - 3
for (var i = 0; i < length; i++) {
if (parser.offset >= bufferLength) {
return
Expand Down Expand Up @@ -305,14 +319,14 @@ function concatBulkString (parser) {
if (chunks === 2) {
return list[0].toString('utf8', parser.bigOffset, list[0].length - 1)
}
} else {
chunks++
chunks--
offset = list[list.length - 2].length + 1
}
var res = decoder.write(list[0].slice(parser.bigOffset))
for (var i = 1; i < chunks - 2; i++) {
for (var i = 1; i < chunks - 1; i++) {
res += decoder.write(list[i])
}
res += decoder.end(list[i].slice(0, offset === 1 ? list[i].length - 1 : offset - 2))
res += decoder.end(list[i].slice(0, offset - 2))
return res
}

Expand Down
6 changes: 3 additions & 3 deletions test/parsers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ describe('parsers', function () {
return this.skip()
}
var replyCount = 0
var entries = ['123', '590295810358705700002', '-99999999999999999']
var entries = ['123', '590295810358705700002', '-99999999999999999', '4294967290', '90071992547409920', '10000040000000000000000000000000000000020']
function checkReply (reply) {
assert.strictEqual(typeof reply, 'string')
assert.strictEqual(reply, entries[replyCount])
Expand All @@ -551,8 +551,8 @@ describe('parsers', function () {
returnReply: checkReply,
stringNumbers: true
})
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n'))
assert.strictEqual(replyCount, 3)
parser.execute(new Buffer(':123\r\n:590295810358705700002\r\n:-99999999999999999\r\n:4294967290\r\n:90071992547409920\r\n:10000040000000000000000000000000000000020\r\n'))
assert.strictEqual(replyCount, 6)
})

it('handle big numbers', function () {
Expand Down

0 comments on commit ca57a68

Please sign in to comment.