Skip to content

Commit

Permalink
Yet more efficient decoding of numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk committed Feb 14, 2025
1 parent b8f214d commit aa75603
Show file tree
Hide file tree
Showing 14 changed files with 421 additions and 1,731 deletions.
8 changes: 1 addition & 7 deletions project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,7 @@ object BuildHelper {
mimaBinaryIssueFilters ++= Seq(
exclude[Problem]("zio.json.internal.*"),
exclude[Problem]("zio.json.yaml.internal.*")
) ++ (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, _)) =>
Seq(
exclude[Problem]("zio.json.JsonFieldDecoder.stringLike") // FIXME: remove after v0.7.19 release
)
case _ => Seq.empty
}),
),
mimaFailOnProblem := true
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ object SafeNumbers {
try Some(UnsafeNumbers.bigInteger(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => None }

def bigInt(num: String, max_bits: Int = 256): Option[BigInt] =
try Some(UnsafeNumbers.bigInt(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => None }

def float(num: String, max_bits: Int = 256): FloatOption =
try FloatSome(UnsafeNumbers.float(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => FloatNone }
Expand Down
88 changes: 82 additions & 6 deletions zio-json/js/src/main/scala/zio/json/internal/UnsafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,52 @@ object UnsafeNumbers {
byte_(new FastStringReader(num), true)

def byte_(in: OneCharReader, consume: Boolean): Byte = {
val n = int_(in, consume)
if (n < -128 || n > 127) throw UnsafeNumber
n.toByte
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var accum = current - '0'
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
accum = accum * 10 + (current - '0')
if (accum > 128) throw UnsafeNumber
}
if (!consume || current == -1) {
if (negate) return (-accum).toByte
else if (accum < 128) return accum.toByte
}
}
throw UnsafeNumber
}

def short(num: String): Short =
short_(new FastStringReader(num), true)

def short_(in: OneCharReader, consume: Boolean): Short = {
val n = int_(in, consume)
if (n < -32768 || n > 32767) throw UnsafeNumber
n.toShort
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var accum = current - '0'
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
accum = accum * 10 + (current - '0')
if (accum > 32768) throw UnsafeNumber
}
if (!consume || current == -1) {
if (negate) return (-accum).toShort
else if (accum < 32768) return accum.toShort
}
}
throw UnsafeNumber
}

def int(num: String): Int =
Expand Down Expand Up @@ -153,6 +187,48 @@ object UnsafeNumbers {
throw UnsafeNumber
}

def bigInt(num: String, max_bits: Int): BigInt =
bigInt_(new FastStringReader(num), true, max_bits)

def bigInt_(in: OneCharReader, consume: Boolean, max_bits: Int): BigInt = {
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var loM10 = (current - '0').toLong
var loDigits = 1
var hiM10: java.math.BigDecimal = null
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
if (loM10 < 922337203685477580L) {
loM10 = (loM10 << 3) + (loM10 << 1) + (current - '0')
loDigits += 1
} else {
if (negate) loM10 = -loM10
val bd = java.math.BigDecimal.valueOf(loM10)
if (hiM10 eq null) hiM10 = bd
else {
hiM10 = hiM10.scaleByPowerOfTen(loDigits).add(bd)
if (hiM10.unscaledValue.bitLength >= max_bits) throw UnsafeNumber
}
loM10 = (current - '0').toLong
loDigits = 1
}
}
if (!consume || current == -1) {
if (negate) loM10 = -loM10
if (hiM10 eq null) return BigInt(loM10)
val bi = hiM10.scaleByPowerOfTen(loDigits).add(java.math.BigDecimal.valueOf(loM10)).unscaledValue
if (bi.bitLength < max_bits) return new BigInt(bi)
}
}
throw UnsafeNumber
}

def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal =
bigDecimal_(new FastStringReader(num), true, max_bits)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ object SafeNumbers {
try Some(UnsafeNumbers.bigInteger(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => None }

def bigInt(num: String, max_bits: Int = 256): Option[BigInt] =
try Some(UnsafeNumbers.bigInt(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => None }

def float(num: String, max_bits: Int = 256): FloatOption =
try FloatSome(UnsafeNumbers.float(num, max_bits))
catch { case _: UnexpectedEnd | UnsafeNumber => FloatNone }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,52 @@ object UnsafeNumbers {
byte_(new FastStringReader(num), true)

def byte_(in: OneCharReader, consume: Boolean): Byte = {
val n = int_(in, consume)
if (n < -128 || n > 127) throw UnsafeNumber
n.toByte
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var accum = current - '0'
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
accum = accum * 10 + (current - '0')
if (accum > 128) throw UnsafeNumber
}
if (!consume || current == -1) {
if (negate) return (-accum).toByte
else if (accum < 128) return accum.toByte
}
}
throw UnsafeNumber
}

def short(num: String): Short =
short_(new FastStringReader(num), true)

def short_(in: OneCharReader, consume: Boolean): Short = {
val n = int_(in, consume)
if (n < -32768 || n > 32767) throw UnsafeNumber
n.toShort
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var accum = current - '0'
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
accum = accum * 10 + (current - '0')
if (accum > 32768) throw UnsafeNumber
}
if (!consume || current == -1) {
if (negate) return (-accum).toShort
else if (accum < 32768) return accum.toShort
}
}
throw UnsafeNumber
}

def int(num: String): Int =
Expand Down Expand Up @@ -153,6 +187,48 @@ object UnsafeNumbers {
throw UnsafeNumber
}

def bigInt(num: String, max_bits: Int): BigInt =
bigInt_(new FastStringReader(num), true, max_bits)

def bigInt_(in: OneCharReader, consume: Boolean, max_bits: Int): BigInt = {
var current =
if (consume) in.readChar().toInt
else in.nextNonWhitespace().toInt
val negate = current == '-'
if (negate) current = in.readChar().toInt
if (current >= '0' && current <= '9') {
var loM10 = (current - '0').toLong
var loDigits = 1
var hiM10: java.math.BigDecimal = null
while ({
current = in.read()
current >= '0' && current <= '9'
}) {
if (loM10 < 922337203685477580L) {
loM10 = loM10 * 10 + (current - '0')
loDigits += 1
} else {
if (negate) loM10 = -loM10
val bd = java.math.BigDecimal.valueOf(loM10)
if (hiM10 eq null) hiM10 = bd
else {
hiM10 = hiM10.scaleByPowerOfTen(loDigits).add(bd)
if (hiM10.unscaledValue.bitLength >= max_bits) throw UnsafeNumber
}
loM10 = (current - '0').toLong
loDigits = 1
}
}
if (!consume || current == -1) {
if (negate) loM10 = -loM10
if (hiM10 eq null) return BigInt(loM10)
val bi = hiM10.scaleByPowerOfTen(loDigits).add(java.math.BigDecimal.valueOf(loM10)).unscaledValue
if (bi.bitLength < max_bits) return new BigInt(bi)
}
}
throw UnsafeNumber
}

def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal =
bigDecimal_(new FastStringReader(num), true, max_bits)

Expand Down

This file was deleted.

Loading

0 comments on commit aa75603

Please sign in to comment.