Skip to content

Commit

Permalink
Fix returning of wrong value or throwing of exceptions during decodin…
Browse files Browse the repository at this point in the history
…g of floating point numbers with extreme exponents (#1303)
  • Loading branch information
plokhotnyuk authored Feb 11, 2025
1 parent d214beb commit 4a298ec
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 67 deletions.
36 changes: 23 additions & 13 deletions zio-json/js/src/main/scala/zio/json/internal/UnsafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -213,15 +212,20 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
if (negate) loM10 = -loM10
return java.math.BigDecimal.valueOf(loM10, -e10)
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate)
}

Expand All @@ -235,12 +239,10 @@ object UnsafeNumbers {
): java.math.BigDecimal = {
var loM10 = lo
if (negate) loM10 = -loM10
val bd =
if (loDigits != 0) java.math.BigDecimal.valueOf(loM10, -e10)
else java.math.BigDecimal.ZERO
val bd = java.math.BigDecimal.valueOf(loM10, -e10)
if (hi eq null) return bd
var hiM10 = hi
val scale = loDigits + e10
var hiM10 = hi
if (scale != 0) hiM10 = hiM10.scaleByPowerOfTen(scale)
hiM10 = hiM10.add(bd)
if (hiM10.unscaledValue.bitLength >= max_bits) throw UnsafeNumber
Expand Down Expand Up @@ -300,7 +302,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -318,12 +319,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toFloat
else {
Expand All @@ -336,6 +340,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).floatValue
}

Expand Down Expand Up @@ -425,7 +431,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -443,12 +448,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toDouble
else {
Expand All @@ -465,6 +473,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).doubleValue
}

Expand Down
36 changes: 23 additions & 13 deletions zio-json/jvm/src/main/scala/zio/json/internal/UnsafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -213,15 +212,20 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
if (negate) loM10 = -loM10
return java.math.BigDecimal.valueOf(loM10, -e10)
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate)
}

Expand All @@ -235,12 +239,10 @@ object UnsafeNumbers {
): java.math.BigDecimal = {
var loM10 = lo
if (negate) loM10 = -loM10
val bd =
if (loDigits != 0) java.math.BigDecimal.valueOf(loM10, -e10)
else java.math.BigDecimal.ZERO
val bd = java.math.BigDecimal.valueOf(loM10, -e10)
if (hi eq null) return bd
var hiM10 = hi
val scale = loDigits + e10
var hiM10 = hi
if (scale != 0) hiM10 = hiM10.scaleByPowerOfTen(scale)
hiM10 = hiM10.add(bd)
if (hiM10.unscaledValue.bitLength >= max_bits) throw UnsafeNumber
Expand Down Expand Up @@ -300,7 +302,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -318,12 +319,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toFloat
else {
Expand All @@ -336,6 +340,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).floatValue
}

Expand Down Expand Up @@ -425,7 +431,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -443,12 +448,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toDouble
else {
Expand All @@ -465,6 +473,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).doubleValue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -213,15 +212,20 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
if (negate) loM10 = -loM10
return java.math.BigDecimal.valueOf(loM10, -e10)
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate)
}

Expand All @@ -235,12 +239,10 @@ object UnsafeNumbers {
): java.math.BigDecimal = {
var loM10 = lo
if (negate) loM10 = -loM10
val bd =
if (loDigits != 0) java.math.BigDecimal.valueOf(loM10, -e10)
else java.math.BigDecimal.ZERO
val bd = java.math.BigDecimal.valueOf(loM10, -e10)
if (hi eq null) return bd
var hiM10 = hi
val scale = loDigits + e10
var hiM10 = hi
if (scale != 0) hiM10 = hiM10.scaleByPowerOfTen(scale)
hiM10 = hiM10.add(bd)
if (hiM10.unscaledValue.bitLength >= max_bits) throw UnsafeNumber
Expand Down Expand Up @@ -300,7 +302,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -318,12 +319,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toFloat
else {
Expand All @@ -336,6 +340,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).floatValue
}

Expand Down Expand Up @@ -425,7 +431,6 @@ object UnsafeNumbers {
}
}
}
if ((hiM10 eq null) && loDigits == 0) throw UnsafeNumber
if ((current | 0x20) == 'e') {
current = in.readChar().toInt
val negateExp = current == '-'
Expand All @@ -443,12 +448,15 @@ object UnsafeNumbers {
}
) throw UnsafeNumber
}
if (negateExp) e10 += exp
else if (exp != -2147483648) e10 -= exp
if (negateExp) {
e10 += exp
if (e10 > 0) throw UnsafeNumber
} else if (exp != -2147483648) e10 -= exp
else throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (hiM10 eq null) {
if (loDigits == 0) throw UnsafeNumber
var x =
if (e10 == 0) loM10.toDouble
else {
Expand All @@ -465,6 +473,8 @@ object UnsafeNumbers {
if (negate) x = -x
return x
}
val scale = loDigits + e10
if (((loDigits ^ scale) & (e10 ^ scale)) < 0) throw UnsafeNumber
toBigDecimal(hiM10, loM10, loDigits, e10, max_bits, negate).doubleValue
}

Expand Down
17 changes: 17 additions & 0 deletions zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ object DecoderSpec extends ZIOSpecDefault {
assert("-1.234567e9".fromJson[Float])(isRight(equalTo(-1.234567e9f))) &&
assert("1.234567e9".fromJson[Float])(isRight(equalTo(1.234567e9f))) &&
assert("\"-1.234567e9\"".fromJson[Float])(isRight(equalTo(-1.234567e9f))) &&
assert("-1.23456789012345678901e-2147483648".fromJson[Float])(isLeft(equalTo("(expected a Float)"))) &&
assert("123456789012345678901e+2147483647".fromJson[Float])(isLeft(equalTo("(expected a Float)"))) &&
assert("-123456789012345678901e+2147483647".fromJson[Float])(isLeft(equalTo("(expected a Float)"))) &&
assert("\"Infinity\"".fromJson[Float])(isRight(equalTo(Float.PositiveInfinity))) &&
assert("\"+Infinity\"".fromJson[Float])(isRight(equalTo(Float.PositiveInfinity))) &&
assert("\"-Infinity\"".fromJson[Float])(isRight(equalTo(Float.NegativeInfinity))) &&
Expand All @@ -92,6 +95,9 @@ object DecoderSpec extends ZIOSpecDefault {
test("double") {
assert("-1.23456789012345e9".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) &&
assert("\"-1.23456789012345e9\"".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) &&
assert("-1.23456789012345678901e-2147483648".fromJson[Double])(isLeft(equalTo("(expected a Double)"))) &&
assert("123456789012345678901e+2147483647".fromJson[Double])(isLeft(equalTo("(expected a Double)"))) &&
assert("-123456789012345678901e+2147483647".fromJson[Double])(isLeft(equalTo("(expected a Double)"))) &&
assert("\"Infinity\"".fromJson[Double])(isRight(equalTo(Double.PositiveInfinity))) &&
assert("\"+Infinity\"".fromJson[Double])(isRight(equalTo(Double.PositiveInfinity))) &&
assert("\"-Infinity\"".fromJson[Double])(isRight(equalTo(Double.NegativeInfinity))) &&
Expand Down Expand Up @@ -120,6 +126,17 @@ object DecoderSpec extends ZIOSpecDefault {
isLeft(equalTo("(expected a BigDecimal with 256-bit mantissa)"))
)
},
test("BigDecimal exponent too large") {
assert("1.23456789012345678901e-2147483648".fromJson[BigDecimal])(
isLeft(equalTo("(expected a BigDecimal with 256-bit mantissa)"))
) &&
assert("123456789012345678901e+2147483647".fromJson[BigDecimal])(
isLeft(equalTo("(expected a BigDecimal with 256-bit mantissa)"))
) &&
assert("-123456789012345678901e+2147483647".fromJson[BigDecimal])(
isLeft(equalTo("(expected a BigDecimal with 256-bit mantissa)"))
)
},
test("BigInteger") {
assert("170141183460469231731687303715884105728".fromJson[BigInteger])(
isRight(equalTo(new BigInteger("170141183460469231731687303715884105728")))
Expand Down
Loading

0 comments on commit 4a298ec

Please sign in to comment.