Skip to content

Commit

Permalink
Fix to throw an error when parsing numeric values that starts from '+' (
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk authored Jan 27, 2025
1 parent 2867a74 commit 23db2c1
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 23 deletions.
67 changes: 47 additions & 20 deletions zio-json/shared/src/main/scala/zio/json/internal/numbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -691,15 +691,15 @@ object UnsafeNumbers {
): java.math.BigInteger = {
var current: Int = in.read()
val negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()
if (current == -1) throw UnsafeNumber
bigDecimal__(in, consume, negative, current, true, max_bits).unscaledValue
}

def int__(in: Reader, lower: Int, upper: Int, consume: Boolean): Int = {
var current = in.read()
val negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()
if (current < '0' || current > '9') throw UnsafeNumber
var accum = '0' - current
while ({
Expand All @@ -726,7 +726,7 @@ object UnsafeNumbers {
def long__(in: Reader, lower: Long, upper: Long, consume: Boolean): Long = {
var current = in.read()
val negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()
if (current < '0' || current > '9') throw UnsafeNumber
var accum = ('0' - current).toLong
while ({
Expand Down Expand Up @@ -763,9 +763,13 @@ object UnsafeNumbers {
}

negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()

if (current == 'I') {
if (current == 'I' || current == '+') {
if (current == '+') {
current = in.read()
if (current != 'I') throw UnsafeNumber
}
readAll(in, "nfinity", consume)
if (negative) return Float.NegativeInfinity
else return Float.PositiveInfinity
Expand All @@ -792,9 +796,13 @@ object UnsafeNumbers {
}

negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()

if (current == 'I') {
if (current == 'I' || current == '+') {
if (current == '+') {
current = in.read()
if (current != 'I') throw UnsafeNumber
}
readAll(in, "nfinity", consume)
if (negative) return Double.NegativeInfinity
else return Double.PositiveInfinity
Expand Down Expand Up @@ -839,7 +847,7 @@ object UnsafeNumbers {
): java.math.BigDecimal = {
var current: Int = in.read()
val negative = current == '-'
if (negative || current == '+') current = in.read()
if (negative) current = in.read()
if (current == -1) throw UnsafeNumber
bigDecimal__(in, consume, negative, current, false, max_bits)
}
Expand All @@ -859,15 +867,11 @@ object UnsafeNumbers {
var dot: Int = 0 // counts from the right
var exp: Int = 0 // implied

def advance(): Boolean = {
current = in.read()
current != -1
}

// skip trailing zero on the left
while (current == '0') {
sig = 0
if (!advance())
current = in.read()
if (current == -1)
return java.math.BigDecimal.ZERO
}

Expand Down Expand Up @@ -903,7 +907,8 @@ object UnsafeNumbers {

while ('0' <= current && current <= '9') {
push_sig()
if (!advance())
current = in.read()
if (current == -1)
return significand()
}

Expand All @@ -915,23 +920,45 @@ object UnsafeNumbers {

if (current == '.') {
if (sig < 0) sig = 0 // e.g. ".1" is shorthand for "0.1"
if (!advance())
current = in.read()
if (current == -1)
return significand()
while ('0' <= current && current <= '9') {
dot += 1
if (sig > 0 || current != '0')
push_sig()
// overflowed...
if (dot < 0) throw UnsafeNumber
advance()
current = in.read()
}
}

if (sig < 0) throw UnsafeNumber // no significand

if (current == 'E' || current == 'e')
exp = int_(in, consume)
else if (consume && current != -1)
if (current == 'E' || current == 'e') {
current = in.read()
val negative = current == '-'
if (negative || current == '+') current = in.read()
if (current < '0' || current > '9') throw UnsafeNumber
var accum = '0' - current
while ({
current = in.read()
'0' <= current && current <= '9'
}) {
if (
accum < -214748364 || {
accum = accum * 10 + ('0' - current)
accum > 0
}
) throw UnsafeNumber
}
if (consume && current != -1) throw UnsafeNumber
if (negative) {
exp = accum
} else if (accum != -2147483648) {
exp = -accum
} else throw UnsafeNumber
} else if (consume && current != -1)
throw UnsafeNumber

val scale = if (dot < 1) exp else exp - dot
Expand Down
31 changes: 29 additions & 2 deletions zio-json/shared/src/test/scala/zio/json/DecoderSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,84 @@ object DecoderSpec extends ZIOSpecDefault {
suite("fromJson")(
test("byte") {
assert("-123".fromJson[Byte])(isRight(equalTo(-123: Byte))) &&
assert("123".fromJson[Byte])(isRight(equalTo(123: Byte))) &&
assert("\"-123\"".fromJson[Byte])(isRight(equalTo(-123: Byte))) &&
assert("\"123\"".fromJson[Byte])(isRight(equalTo(123: Byte))) &&
assertTrue("+123".fromJson[Byte].isLeft) &&
assertTrue("\"Infinity\"".fromJson[Byte].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[Byte].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[Byte].isLeft) &&
assertTrue("\"NaN\"".fromJson[Byte].isLeft)
},
test("short") {
assert("-12345".fromJson[Short])(isRight(equalTo(-12345: Short))) &&
assert("12345".fromJson[Short])(isRight(equalTo(12345: Short))) &&
assert("\"-12345\"".fromJson[Short])(isRight(equalTo(-12345: Short))) &&
assert("\"12345\"".fromJson[Short])(isRight(equalTo(12345: Short))) &&
assertTrue("+12345".fromJson[Short].isLeft) &&
assertTrue("\"Infinity\"".fromJson[Short].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[Short].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[Short].isLeft) &&
assertTrue("\"NaN\"".fromJson[Short].isLeft)
},
test("int") {
assert("-1234567890".fromJson[Int])(isRight(equalTo(-1234567890))) &&
assert("1234567890".fromJson[Int])(isRight(equalTo(1234567890))) &&
assert("\"-1234567890\"".fromJson[Int])(isRight(equalTo(-1234567890))) &&
assert("\"1234567890\"".fromJson[Int])(isRight(equalTo(1234567890))) &&
assertTrue("+1234567890".fromJson[Int].isLeft) &&
assertTrue("\"Infinity\"".fromJson[Int].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[Int].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[Int].isLeft) &&
assertTrue("\"NaN\"".fromJson[Int].isLeft)
},
test("long") {
assert("-123456789012345678".fromJson[Long])(isRight(equalTo(-123456789012345678L))) &&
assert("123456789012345678".fromJson[Long])(isRight(equalTo(123456789012345678L))) &&
assert("\"-123456789012345678\"".fromJson[Long])(isRight(equalTo(-123456789012345678L))) &&
assert("\"123456789012345678\"".fromJson[Long])(isRight(equalTo(123456789012345678L))) &&
assertTrue("+123456789012345678".fromJson[Long].isLeft) &&
assertTrue("\"Infinity\"".fromJson[Long].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[Long].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[Long].isLeft) &&
assertTrue("\"NaN\"".fromJson[Long].isLeft)
},
test("float") {
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("\"Infinity\"".fromJson[Float])(isRight(equalTo(Float.PositiveInfinity))) &&
assert("\"+Infinity\"".fromJson[Float])(isRight(equalTo(Float.PositiveInfinity))) &&
assert("\"-Infinity\"".fromJson[Float])(isRight(equalTo(Float.NegativeInfinity))) &&
assertTrue("\"NaN\"".fromJson[Float].isRight)
assertTrue("\"NaN\"".fromJson[Float].isRight) &&
assertTrue("+1.234567e9".fromJson[Float].isLeft)
},
test("double") {
assert("-1.23456789012345e9".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) &&
assert("\"-1.23456789012345e9\"".fromJson[Double])(isRight(equalTo(-1.23456789012345e9))) &&
assert("\"Infinity\"".fromJson[Double])(isRight(equalTo(Double.PositiveInfinity))) &&
assert("\"+Infinity\"".fromJson[Double])(isRight(equalTo(Double.PositiveInfinity))) &&
assert("\"-Infinity\"".fromJson[Double])(isRight(equalTo(Double.NegativeInfinity))) &&
assertTrue("\"NaN\"".fromJson[Double].isRight)
assertTrue("\"NaN\"".fromJson[Double].isRight) &&
assertTrue("+1.23456789012345e9".fromJson[Double].isLeft)
},
test("BigDecimal") {
assert("-123.0e123".fromJson[BigDecimal])(isRight(equalTo(BigDecimal("-123.0e123")))) &&
assert("123.0e123".fromJson[BigDecimal])(isRight(equalTo(BigDecimal("123.0e123")))) &&
assertTrue("\"Infinity\"".fromJson[BigDecimal].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[BigDecimal].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[BigDecimal].isLeft) &&
assertTrue("\"NaN\"".fromJson[BigDecimal].isLeft)
},
test("BigInteger") {
assert("170141183460469231731687303715884105728".fromJson[BigInteger])(
isRight(equalTo(new BigInteger("170141183460469231731687303715884105728")))
) &&
assert("-170141183460469231731687303715884105728".fromJson[BigInteger])(
isRight(equalTo(new BigInteger("-170141183460469231731687303715884105728")))
) &&
assertTrue("\"Infinity\"".fromJson[BigInteger].isLeft) &&
assertTrue("\"+Infinity\"".fromJson[BigInteger].isLeft) &&
assertTrue("\"-Infinity\"".fromJson[BigInteger].isLeft) &&
assertTrue("\"NaN\"".fromJson[BigInteger].isLeft)
},
Expand Down
2 changes: 1 addition & 1 deletion zio-json/shared/src/test/scala/zio/json/Gens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Gens {
Gen
.bigDecimal((BigDecimal(2).pow(128) - 1) * -1, BigDecimal(2).pow(128) - 1)
.map(_.bigDecimal)
.filter(_.toBigInteger.bitLength < 128)
.filter(_.unscaledValue.bitLength < 128)

val genUsAsciiString =
Gen.string(Gen.oneOf(Gen.char('!', '~')))
Expand Down

0 comments on commit 23db2c1

Please sign in to comment.