From 882b7dd065aafbb7173479006739909d04c45633 Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Wed, 7 Oct 2015 11:18:35 -0400 Subject: [PATCH 1/6] Refacrotored number parsing with parseFloat --- jsonparse.js | 166 ++++++++++++--------------------------------------- 1 file changed, 37 insertions(+), 129 deletions(-) diff --git a/jsonparse.js b/jsonparse.js index 11fa269..8e38f9d 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -108,15 +108,14 @@ proto.write = function (buffer) { }else if(n === 0x66){ this.tState = FALSE1; // f }else if(n === 0x6e){ this.tState = NULL1; // n }else if(n === 0x22){ this.string = ""; this.tState = STRING1; // " - }else if(n === 0x2d){ this.negative = true; this.tState = NUMBER1; // - - }else if(n === 0x30){ this.magnatude = 0; this.tState = NUMBER2; // 0 + }else if(n === 0x2d){ this.string = "-"; this.tState = NUMBER1; // - }else{ - if (n > 0x30 && n < 0x40) { // 1-9 - this.magnatude = n - 0x30; this.tState = NUMBER3; + if (n >= 0x30 && n < 0x40) { // 1-9 + this.string = String.fromCharCode(n); this.tState = NUMBER3; } else if (n === 0x20 || n === 0x09 || n === 0x0a || n === 0x0d) { // whitespace } else { - return this.charError(buffer, i); + return this.charError(buffer, i); } } }else if (this.tState === STRING1){ // After open quote @@ -180,131 +179,40 @@ proto.write = function (buffer) { } else { return this.charError(buffer, i); } - }else if (this.tState === NUMBER1){ // after minus - n = buffer[i]; - this.numberLength++; - if (n === 0x30) { this.magnatude = 0; this.tState = NUMBER2; } - else if (n > 0x30 && n < 0x40) { this.magnatude = n - 0x30; this.tState = NUMBER3; } - else { - return this.charError(buffer, i); - } - }else if (this.tState === NUMBER2){ // * After initial zero - n = buffer[i]; - this.numberLength++; - if(n === 0x2e){ // . - this.position = 0.1; this.tState = NUMBER4; - }else if(n === 0x65 || n === 0x45){ // e/E - this.exponent = 0; this.tState = NUMBER6; - }else{ - this.tState = START; - this.onToken(NUMBER, 0); - this.offset += this.numberLength - 1; - this.numberLength = 0; - this.magnatude = undefined; - this.negative = undefined; - i--; - } - }else if (this.tState === NUMBER3){ // * After digit (before period) - n = buffer[i]; - this.numberLength++; - if(n === 0x2e){ // . - this.position = 0.1; this.tState = NUMBER4; - }else if(n === 0x65 || n === 0x45){ // e/E - this.exponent = 0; this.tState = NUMBER6; - }else{ - if (n >= 0x30 && n < 0x40) { this.magnatude = this.magnatude * 10 + n - 0x30; } - else { - this.tState = START; - if (this.negative) { - this.magnatude = -this.magnatude; - this.negative = undefined; - } - this.onToken(NUMBER, this.magnatude); - this.offset += this.numberLength - 1; - this.numberLength = 0; - this.magnatude = undefined; - i--; - } - } - }else if (this.tState === NUMBER4){ // After period - n = buffer[i]; - this.numberLength++; - if (n >= 0x30 && n < 0x40) { // 0-9 - this.magnatude += this.position * (n - 0x30); - this.position /= 10; - this.tState = NUMBER5; - } else { - return this.charError(buffer, i); - } - }else if (this.tState === NUMBER5){ // * After digit (after period) - n = buffer[i]; - this.numberLength++; - if (n >= 0x30 && n < 0x40) { // 0-9 - this.magnatude += this.position * (n - 0x30); - this.position /= 10; - } - else if (n === 0x65 || n === 0x45) { this.exponent = 0; this.tState = NUMBER6; } // E/e - else { - this.tState = START; - if (this.negative) { - this.magnatude = -this.magnatude; - this.negative = undefined; - } - this.onToken(NUMBER, this.negative ? -this.magnatude : this.magnatude); - this.offset += this.numberLength - 1; - this.numberLength = 0; - this.magnatude = undefined; - this.position = undefined; - i--; - } - }else if (this.tState === NUMBER6){ // After E - n = buffer[i]; - this.numberLength++; - if (n === 0x2b || n === 0x2d) { // +/- - if (n === 0x2d) { this.negativeExponent = true; } - this.tState = NUMBER7; - } - else if (n >= 0x30 && n < 0x40) { - this.exponent = this.exponent * 10 + (n - 0x30); - this.tState = NUMBER8; - } - else { - return this.charError(buffer, i); - } - }else if (this.tState === NUMBER7){ // After +/- - n = buffer[i]; - this.numberLength++; - if (n >= 0x30 && n < 0x40) { // 0-9 - this.exponent = this.exponent * 10 + (n - 0x30); - this.tState = NUMBER8; - } - else { - return this.charError(buffer, i); - } - }else if (this.tState === NUMBER8){ // * After digit (after +/-) - n = buffer[i]; - this.numberLength++; - if (n >= 0x30 && n < 0x40) { // 0-9 - this.exponent = this.exponent * 10 + (n - 0x30); - } - else { - if (this.negativeExponent) { - this.exponent = -this.exponent; - this.negativeExponent = undefined; - } - this.magnatude *= Math.pow(10, this.exponent); - this.exponent = undefined; - if (this.negative) { - this.magnatude = -this.magnatude; - this.negative = undefined; + } else if (this.tState === NUMBER1 || this.tState === NUMBER3) { + n = buffer[i]; + + switch (n) { + case 0x30: // 0 + case 0x31: // 1 + case 0x32: // 2 + case 0x33: // 3 + case 0x34: // 4 + case 0x35: // 5 + case 0x36: // 6 + case 0x37: // 7 + case 0x38: // 8 + case 0x39: // 9 + case 0x2e: // . + case 0x65: // e + case 0x45: // E + case 0x2b: // + + case 0x2d: // - + this.string += String.fromCharCode(n); + this.tState = NUMBER3; + break; + default: + this.tState = START; + var result = parseFloat(this.string); + if (isNaN(result)) { + this.charError(buffer, i); + } else { + this.onToken(NUMBER, result); + this.string = undefined; + i--; + } + break; } - this.tState = START; - this.onToken(NUMBER, this.magnatude); - this.offset += this.numberLength - 1; - this.numberLength = 0; - this.magnatude = undefined; - i--; - } }else if (this.tState === TRUE1){ // r if (buffer[i] === 0x72) { this.tState = TRUE2; } else { return this.charError(buffer, i); } From 5c181b7ef21a9effa75a336daf21d0ae26224658 Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Wed, 7 Oct 2015 13:29:58 -0400 Subject: [PATCH 2/6] Unit tests are passing after refactoring --- jsonparse.js | 17 ++--------------- test/primitives.js | 4 ++-- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/jsonparse.js b/jsonparse.js index 8e38f9d..b1f9374 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -27,13 +27,7 @@ var NULL1 = C.NULL1 = 0x41; var NULL2 = C.NULL2 = 0x42; var NULL3 = C.NULL3 = 0x43; var NUMBER1 = C.NUMBER1 = 0x51; -var NUMBER2 = C.NUMBER2 = 0x52; var NUMBER3 = C.NUMBER3 = 0x53; -var NUMBER4 = C.NUMBER4 = 0x54; -var NUMBER5 = C.NUMBER5 = 0x55; -var NUMBER6 = C.NUMBER6 = 0x56; -var NUMBER7 = C.NUMBER7 = 0x57; -var NUMBER8 = C.NUMBER8 = 0x58; var STRING1 = C.STRING1 = 0x61; var STRING2 = C.STRING2 = 0x62; var STRING3 = C.STRING3 = 0x63; @@ -55,14 +49,6 @@ function Parser() { this.string = undefined; // string data this.unicode = undefined; // unicode escapes - // For number parsing - this.negative = undefined; - this.magnatude = undefined; - this.position = undefined; - this.exponent = undefined; - this.negativeExponent = undefined; - this.numberLength = 0; - this.key = undefined; this.mode = undefined; this.stack = []; @@ -83,7 +69,7 @@ Parser.toknam = function (code) { if (C[key] === code) { return key; } } return code && ("0x" + code.toString(16)); -} +}; var proto = Parser.prototype; proto.onError = function (err) { throw err; }; @@ -208,6 +194,7 @@ proto.write = function (buffer) { this.charError(buffer, i); } else { this.onToken(NUMBER, result); + this.offset += this.string.length - 1; this.string = undefined; i--; } diff --git a/test/primitives.js b/test/primitives.js index 3c5e789..8596e3f 100644 --- a/test/primitives.js +++ b/test/primitives.js @@ -23,8 +23,8 @@ var expected = [ [ [], [ -1 ] ], [ [ 0 ], -0.1 ], [ [], [ -0.1 ] ], - [ [ 0 ], 6.019999999999999e+23 ], - [ [], [ 6.019999999999999e+23 ] ] + [ [ 0 ], 6.02e+23 ], + [ [], [ 6.02e+23 ] ] ]; test('primitives', function (t) { From 9ad4f7e30390f72d778d0cdde8205eee57dcda9c Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Wed, 7 Oct 2015 13:42:09 -0400 Subject: [PATCH 3/6] Error handling just like the rest of the code --- jsonparse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonparse.js b/jsonparse.js index b1f9374..af14551 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -191,7 +191,7 @@ proto.write = function (buffer) { this.tState = START; var result = parseFloat(this.string); if (isNaN(result)) { - this.charError(buffer, i); + return this.charError(buffer, i); } else { this.onToken(NUMBER, result); this.offset += this.string.length - 1; From 0323bbc918d9c28f1f34f34bf2def1b5d4c052de Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Wed, 7 Oct 2015 16:07:40 -0400 Subject: [PATCH 4/6] Overflow check --- jsonparse.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jsonparse.js b/jsonparse.js index af14551..f122700 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -193,7 +193,15 @@ proto.write = function (buffer) { if (isNaN(result)) { return this.charError(buffer, i); } else { - this.onToken(NUMBER, result); + if (result.toString() == this.string) { + this.onToken(NUMBER, result); + } else if (result - parseFloat(result.toString()) <= 0) { + // If special handling of floating arithmetics is needed add it here. For now the comparison will be exact + this.onToken(NUMBER, result); + } else { + // overflow rounding error + this.onToken(NUMBER, this.string); + } this.offset += this.string.length - 1; this.string = undefined; i--; From 645897f349922c7bb11e4fee4f9f79ede9af84f7 Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Wed, 7 Oct 2015 16:09:14 -0400 Subject: [PATCH 5/6] Overflow check, passing a string --- jsonparse.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsonparse.js b/jsonparse.js index f122700..f91ea56 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -199,8 +199,8 @@ proto.write = function (buffer) { // If special handling of floating arithmetics is needed add it here. For now the comparison will be exact this.onToken(NUMBER, result); } else { - // overflow rounding error - this.onToken(NUMBER, this.string); + // overflow rounding error passing it along as a string + this.onToken(STRING, this.string); } this.offset += this.string.length - 1; this.string = undefined; From 5245aa69d69a6d1c5926f36154b413c64ea46373 Mon Sep 17 00:00:00 2001 From: Marina Novikova Date: Thu, 8 Oct 2015 14:20:40 -0400 Subject: [PATCH 6/6] Added few tests for the long numbers and improved the check --- jsonparse.js | 27 +++++++++++++-------------- test/primitives.js | 7 +++++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/jsonparse.js b/jsonparse.js index f91ea56..f6300a2 100644 --- a/jsonparse.js +++ b/jsonparse.js @@ -189,23 +189,22 @@ proto.write = function (buffer) { break; default: this.tState = START; - var result = parseFloat(this.string); - if (isNaN(result)) { + var result = Number(this.string); + + if (isNaN(result)){ return this.charError(buffer, i); + } + + if ((this.string.match(/[0-9]+/) == this.string) && (result.toString() != this.string)) { + // Long string of digits which is an ID string and not valid and/or safe JavaScript integer Number + this.onToken(STRING, this.string); } else { - if (result.toString() == this.string) { - this.onToken(NUMBER, result); - } else if (result - parseFloat(result.toString()) <= 0) { - // If special handling of floating arithmetics is needed add it here. For now the comparison will be exact - this.onToken(NUMBER, result); - } else { - // overflow rounding error passing it along as a string - this.onToken(STRING, this.string); - } - this.offset += this.string.length - 1; - this.string = undefined; - i--; + this.onToken(NUMBER, result); } + + this.offset += this.string.length - 1; + this.string = undefined; + i--; break; } }else if (this.tState === TRUE1){ // r diff --git a/test/primitives.js b/test/primitives.js index 8596e3f..33cae16 100644 --- a/test/primitives.js +++ b/test/primitives.js @@ -24,11 +24,13 @@ var expected = [ [ [ 0 ], -0.1 ], [ [], [ -0.1 ] ], [ [ 0 ], 6.02e+23 ], - [ [], [ 6.02e+23 ] ] + [ [], [ 6.02e+23 ] ], + [ [ 0 ], '7161093205057351174' ], + [ [], [ '7161093205057351174'] ] ]; test('primitives', function (t) { - t.plan(23); + t.plan(25); var p = new Parser(); p.onValue = function (value) { @@ -51,4 +53,5 @@ test('primitives', function (t) { p.write('[0,1,-1]'); p.write('[1.0,1.1,-1.1,-1.0][-1][-0.1]'); p.write('[6.02e23]'); + p.write('[7161093205057351174]'); });