From 1e752328a76b5907ea3a41f97b46180f6e78b229 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 9 Jul 2023 14:29:29 +0800 Subject: [PATCH] Fix parsing of large integer doubles (#504) Fixes https://github.com/com-lihaoyi/upickle/issues/240 The basic problem appears to be that we are using a `parseLong` fast-path inside `ujson.Value.visitFloat64StringParts`. This is only valid for numbers of a certain digit-length. We should be checking for numbers larger than that length, and falling back to the `toDouble` slow path for those --- ujson/src/ujson/Value.scala | 4 ++- ujson/test/src/ujson/SmallJsonTests.scala | 44 +++++++++++++++++++++++ upickle/core/templates/ElemUtils.scala | 5 ++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/ujson/src/ujson/Value.scala b/ujson/src/ujson/Value.scala index bf7507687..15dbcb22f 100644 --- a/ujson/src/ujson/Value.scala +++ b/ujson/src/ujson/Value.scala @@ -180,7 +180,9 @@ object Value extends AstTransformer[Value]{ override def visitFloat64StringParts(s: CharSequence, decIndex: Int, expIndex: Int, index: Int) = { ujson.Num( - if (decIndex != -1 || expIndex != -1) s.toString.toDouble + if (decIndex != -1 || expIndex != -1 || s.length() >= 19 /*digit count of largest positive long*/) { + s.toString.toDouble + } else ParseUtils.parseIntegralNum(s, decIndex, expIndex, index).toDouble ) } diff --git a/ujson/test/src/ujson/SmallJsonTests.scala b/ujson/test/src/ujson/SmallJsonTests.scala index 11fd25eaa..f06ac1fa9 100644 --- a/ujson/test/src/ujson/SmallJsonTests.scala +++ b/ujson/test/src/ujson/SmallJsonTests.scala @@ -23,6 +23,50 @@ object SmallJsonTests extends TestSuite { } } + test("largeDouble"){ + test("longMaxValue") { + assert( + ujson.read("9223372036854775807").num == + "9223372036854775807".toDouble + ) + } + test("longMinValue") { + assert( + ujson.read("-9223372036854775808").num == + "-9223372036854775808".toDouble + ) + } + test("longMaxValuePlusOne") { + assert( + ujson.read("9223372036854775808").num == + "9223372036854775808".toDouble + ) + } + test("longMinValueMinusOne") { + assert( + ujson.read("-9223372036854775809").num == + "-9223372036854775809".toDouble + ) + } + test("issue240"){ + assert( + ujson.read("28291041994989161181883157038606177750").num == + "28291041994989161181883157038606177750".toDouble + ) + } + test("largeDecimalPoint"){ + assert( + ujson.read("28291041994989161181883157038606177750.28291041994989161181883157038606177750").num == + "28291041994989161181883157038606177750.28291041994989161181883157038606177750".toDouble + ) + } + test("largeDecimalPointExponent"){ + assert( + ujson.read("28291041994989161181883157038606177750.28291041994989161181883157038606177750E123456789").num == + "28291041994989161181883157038606177750.28291041994989161181883157038606177750E123456789".toDouble + ) + } + } test("inputsEu"){ TestUtil.checkParse( """{ diff --git a/upickle/core/templates/ElemUtils.scala b/upickle/core/templates/ElemUtils.scala index 59647b5c6..cb10de952 100644 --- a/upickle/core/templates/ElemUtils.scala +++ b/upickle/core/templates/ElemUtils.scala @@ -174,7 +174,10 @@ object ElemUtils{ } val size = end - i - if (size <= 0 || size > 19) throw new NumberFormatException(new String(cs)) + + if (size <= 0 || size > 19 /*digit count of largest positive long*/) { + throw new NumberFormatException(new String(cs)) + } while (i < end) { val digit = cs(i).toInt - 48