Skip to content

Commit 3a005fa

Browse files
committed
More efficient reading of BigInt and BigDecimal values
1 parent c6716c7 commit 3a005fa

File tree

3 files changed

+88
-130
lines changed
  • jsoniter-scala-core

3 files changed

+88
-130
lines changed

jsoniter-scala-core/js/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonReader.scala

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,10 +2515,10 @@ final class JsonReader private[jsoniter_scala](
25152515
else nextByte(head)
25162516
if (isToken && b == 'n') readNullOrNumberError(default, head)
25172517
else {
2518-
var isNeg = false
2518+
var s = 0
25192519
if (b == '-') {
25202520
b = nextByte(head)
2521-
isNeg = true
2521+
s = -1
25222522
}
25232523
if (b < '0' || b > '9') numberError()
25242524
if (isToken && b == '0') {
@@ -2546,7 +2546,7 @@ final class JsonReader private[jsoniter_scala](
25462546
if (mark == 0) from -= newMark
25472547
if (mark > oldMark) mark = oldMark
25482548
if (pos - from >= digitsLimit) digitsLimitError(from + digitsLimit - 1)
2549-
toBigInt(buf, from, pos, isNeg)
2549+
toBigInt(buf, from, pos, s)
25502550
}
25512551
}
25522552
}
@@ -2558,10 +2558,10 @@ final class JsonReader private[jsoniter_scala](
25582558
else nextByte(head)
25592559
if (isToken && b == 'n') readNullOrNumberError(default, head)
25602560
else {
2561-
var isNeg = false
2561+
var s = 0
25622562
if (b == '-') {
25632563
b = nextByte(head)
2564-
isNeg = true
2564+
s = -1
25652565
}
25662566
if (b < '0' || b > '9') numberError()
25672567
var pos = head
@@ -2661,8 +2661,7 @@ final class JsonReader private[jsoniter_scala](
26612661
x = x * 10 + (buf(from) - '0')
26622662
from += 1
26632663
}
2664-
if (isNeg) x = -x
2665-
java.math.BigDecimal.valueOf(x.toLong, scale + fracLen)
2664+
java.math.BigDecimal.valueOf(((x ^ s) - s).toLong, scale + fracLen)
26662665
} else if (digits < 19) {
26672666
var x = (buf(from) - '0').toLong
26682667
from += 1
@@ -2675,18 +2674,18 @@ final class JsonReader private[jsoniter_scala](
26752674
x = x * 10 + (buf(from) - '0')
26762675
from += 1
26772676
}
2678-
if (isNeg) x = -x
2677+
if (s != 0) x = -x
26792678
java.math.BigDecimal.valueOf(x, scale + fracLen)
2680-
} else toBigDecimal(buf, from, fracLimit, isNeg, scale)
2681-
.add(toBigDecimal(buf, fracPos, limit, isNeg, scale + fracLen))
2682-
} else toBigDecimal(buf, from, from + digits, isNeg, scale)
2679+
} else toBigDecimal(buf, from, fracLimit, s, scale)
2680+
.add(toBigDecimal(buf, fracPos, limit, s, scale + fracLen))
2681+
} else toBigDecimal(buf, from, from + digits, s, scale)
26832682
if (mc.getPrecision < digits) x = x.plus(mc)
26842683
if (Math.abs(x.scale) >= scaleLimit) scaleLimitError()
26852684
new BigDecimal(x, mc)
26862685
}
26872686
}
26882687

2689-
private[this] def toBigInt(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean): BigInt = {
2688+
private[this] def toBigInt(buf: Array[Byte], p: Int, limit: Int, s: Int): BigInt = {
26902689
val len = limit - p
26912690
if (len < 19) {
26922691
var pos = p
@@ -2702,24 +2701,23 @@ final class JsonReader private[jsoniter_scala](
27022701
x1 = x1 * 10 + (buf(pos) - '0')
27032702
pos += 1
27042703
}
2705-
if (isNeg) x1 = -x1
2704+
if (s != 0) x1 = -x1
27062705
BigInt(x1)
27072706
} else new BigInt({
2708-
if (len <= 36) toBigDecimal36(buf, p, limit, isNeg, 0).unscaledValue()
2709-
else if (len <= 308) toBigInteger308(buf, p, limit, isNeg)
2707+
if (len <= 36) toBigDecimal36(buf, p, limit, s, 0).unscaledValue()
2708+
else if (len <= 308) toBigInteger308(buf, p, limit, s)
27102709
else {
27112710
// Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
27122711
// with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
27132712
// https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
27142713
val mid = len >> 1
27152714
val midPos = limit - mid
2716-
toBigDecimal(buf, p, midPos, isNeg, -mid).add(toBigDecimal(buf, midPos, limit, isNeg, 0)).unscaledValue()
2715+
toBigDecimal(buf, p, midPos, s, -mid).add(toBigDecimal(buf, midPos, limit, s, 0)).unscaledValue()
27172716
}
27182717
})
27192718
}
27202719

2721-
private[this] def toBigDecimal(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean,
2722-
scale: Int): java.math.BigDecimal = {
2720+
private[this] def toBigDecimal(buf: Array[Byte], p: Int, limit: Int, s: Int, scale: Int): java.math.BigDecimal = {
27232721
val len = limit - p
27242722
if (len < 19) {
27252723
var pos = p
@@ -2735,22 +2733,21 @@ final class JsonReader private[jsoniter_scala](
27352733
x1 = x1 * 10 + (buf(pos) - '0')
27362734
pos += 1
27372735
}
2738-
if (isNeg) x1 = -x1
2736+
if (s != 0) x1 = -x1
27392737
java.math.BigDecimal.valueOf(x1, scale)
2740-
} else if (len <= 36) toBigDecimal36(buf, p, limit, isNeg, scale)
2741-
else if (len <= 308) new java.math.BigDecimal(toBigInteger308(buf, p, limit, isNeg), scale)
2738+
} else if (len <= 36) toBigDecimal36(buf, p, limit, s, scale)
2739+
else if (len <= 308) new java.math.BigDecimal(toBigInteger308(buf, p, limit, s), scale)
27422740
else {
27432741
// Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
27442742
// with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
27452743
// https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
27462744
val mid = len >> 1
27472745
val midPos = limit - mid
2748-
toBigDecimal(buf, p, midPos, isNeg, scale - mid).add(toBigDecimal(buf, midPos, limit, isNeg, scale))
2746+
toBigDecimal(buf, p, midPos, s, scale - mid).add(toBigDecimal(buf, midPos, limit, s, scale))
27492747
}
27502748
}
27512749

2752-
private[this] def toBigDecimal36(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean,
2753-
scale: Int): java.math.BigDecimal = {
2750+
private[this] def toBigDecimal36(buf: Array[Byte], p: Int, limit: Int, s: Int, scale: Int): java.math.BigDecimal = {
27542751
var pos = p
27552752
var x = buf(pos) - '0'
27562753
pos += 1
@@ -2775,14 +2772,14 @@ final class JsonReader private[jsoniter_scala](
27752772
(buf(pos + 12) * 10 + buf(pos + 13)) * 10000 +
27762773
(buf(pos + 14) * 10 + buf(pos + 15)) * 100 +
27772774
buf(pos + 16) * 10 + buf(pos + 17)) - 5333333333333333328L // 5333333333333333328L == '0' * 111111111111111111L
2778-
if (isNeg) {
2775+
if (s != 0) {
27792776
x1 = -x1
27802777
x2 = -x2
27812778
}
27822779
java.math.BigDecimal.valueOf(x1, scale - 18).add(java.math.BigDecimal.valueOf(x2, scale))
27832780
}
27842781

2785-
private[this] def toBigInteger308(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean): java.math.BigInteger = {
2782+
private[this] def toBigInteger308(buf: Array[Byte], p: Int, limit: Int, s: Int): java.math.BigInteger = {
27862783
val len = limit - p
27872784
val last = (len * 0.10381025296523008).toInt // (len * Math.log(10) / Math.log(1L << 32)).toInt
27882785
var magnitude = this.magnitude
@@ -2835,10 +2832,7 @@ final class JsonReader private[jsoniter_scala](
28352832
bs(j + 3) = w.toByte
28362833
i += 1
28372834
}
2838-
val signum =
2839-
if (isNeg) -1
2840-
else 1
2841-
new java.math.BigInteger(signum, bs)
2835+
new java.math.BigInteger(s | 1, bs)
28422836
}
28432837

28442838
private[this] def readNullOrNumberError[@sp A](default: A, pos: Int): A =

jsoniter-scala-core/jvm/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonReader.scala

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,10 +2472,10 @@ final class JsonReader private[jsoniter_scala](
24722472
else nextByte(head)
24732473
if (isToken && b == 'n') readNullOrNumberError(default, head)
24742474
else {
2475-
var isNeg = false
2475+
var s = 0
24762476
if (b == '-') {
24772477
b = nextByte(head)
2478-
isNeg = true
2478+
s = -1
24792479
}
24802480
if (b < '0' || b > '9') numberError()
24812481
if (isToken && b == '0') {
@@ -2519,7 +2519,7 @@ final class JsonReader private[jsoniter_scala](
25192519
if (mark == 0) from -= newMark
25202520
if (mark > oldMark) mark = oldMark
25212521
if (pos - from >= digitsLimit) digitsLimitError(from + digitsLimit - 1)
2522-
toBigInt(buf, from, pos, isNeg)
2522+
toBigInt(buf, from, pos, s)
25232523
}
25242524
}
25252525
}
@@ -2531,10 +2531,10 @@ final class JsonReader private[jsoniter_scala](
25312531
else nextByte(head)
25322532
if (isToken && b == 'n') readNullOrNumberError(default, head)
25332533
else {
2534-
var isNeg = false
2534+
var s = 0
25352535
if (b == '-') {
25362536
b = nextByte(head)
2537-
isNeg = true
2537+
s = -1
25382538
}
25392539
if (b < '0' || b > '9') numberError()
25402540
var pos = head
@@ -2670,18 +2670,17 @@ final class JsonReader private[jsoniter_scala](
26702670
x = x * 10 + (buf(from) - '0')
26712671
from += 1
26722672
}
2673-
if (isNeg) x = -x
2674-
java.math.BigDecimal.valueOf(x, scale + fracLen)
2675-
} else toBigDecimal(buf, from, fracLimit, isNeg, scale)
2676-
.add(toBigDecimal(buf, fracPos, limit, isNeg, scale + fracLen))
2677-
} else toBigDecimal(buf, from, from + digits, isNeg, scale)
2673+
java.math.BigDecimal.valueOf((x ^ s) - s, scale + fracLen)
2674+
} else toBigDecimal(buf, from, fracLimit, s, scale)
2675+
.add(toBigDecimal(buf, fracPos, limit, s, scale + fracLen))
2676+
} else toBigDecimal(buf, from, from + digits, s, scale)
26782677
if (mc.getPrecision < digits) x = x.plus(mc)
26792678
if (Math.abs(x.scale) >= scaleLimit) scaleLimitError()
26802679
new BigDecimal(x, mc)
26812680
}
26822681
}
26832682

2684-
private[this] def toBigInt(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean): BigInt = {
2683+
private[this] def toBigInt(buf: Array[Byte], p: Int, limit: Int, s: Int): BigInt = {
26852684
val len = limit - p
26862685
if (len < 19) {
26872686
var pos = p
@@ -2691,24 +2690,22 @@ final class JsonReader private[jsoniter_scala](
26912690
x = x * 10 + (buf(pos) - '0')
26922691
pos += 1
26932692
}
2694-
if (isNeg) x = -x
2695-
BigInt(x)
2696-
} else if (len <= 36) toBigInt36(buf, p, limit, isNeg)
2693+
BigInt((x ^ s) - s)
2694+
} else if (len <= 36) toBigInt36(buf, p, limit, s)
26972695
else new BigInt({
2698-
if (len <= 308) toBigInteger308(buf, p, limit, isNeg)
2696+
if (len <= 308) toBigInteger308(buf, p, limit, s)
26992697
else {
27002698
// Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
27012699
// with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
27022700
// https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
27032701
val mid = len >> 1
27042702
val midPos = limit - mid
2705-
toBigDecimal(buf, p, midPos, isNeg, -mid).add(toBigDecimal(buf, midPos, limit, isNeg, 0)).unscaledValue()
2703+
toBigDecimal(buf, p, midPos, s, -mid).add(toBigDecimal(buf, midPos, limit, s, 0)).unscaledValue()
27062704
}
27072705
})
27082706
}
27092707

2710-
private[this] def toBigDecimal(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean,
2711-
scale: Int): java.math.BigDecimal = {
2708+
private[this] def toBigDecimal(buf: Array[Byte], p: Int, limit: Int, s: Int, scale: Int): java.math.BigDecimal = {
27122709
val len = limit - p
27132710
if (len < 19) {
27142711
var pos = p
@@ -2718,21 +2715,20 @@ final class JsonReader private[jsoniter_scala](
27182715
x = x * 10 + (buf(pos) - '0')
27192716
pos += 1
27202717
}
2721-
if (isNeg) x = -x
2722-
java.math.BigDecimal.valueOf(x, scale)
2723-
} else if (len <= 36) toBigDecimal36(buf, p, limit, isNeg, scale)
2724-
else if (len <= 308) new java.math.BigDecimal(toBigInteger308(buf, p, limit, isNeg), scale)
2718+
java.math.BigDecimal.valueOf((x ^ s) - s, scale)
2719+
} else if (len <= 36) toBigDecimal36(buf, p, limit, s, scale)
2720+
else if (len <= 308) new java.math.BigDecimal(toBigInteger308(buf, p, limit, s), scale)
27252721
else {
27262722
// Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
27272723
// with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
27282724
// https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
27292725
val mid = len >> 1
27302726
val midPos = limit - mid
2731-
toBigDecimal(buf, p, midPos, isNeg, scale - mid).add(toBigDecimal(buf, midPos, limit, isNeg, scale))
2727+
toBigDecimal(buf, p, midPos, s, scale - mid).add(toBigDecimal(buf, midPos, limit, s, scale))
27322728
}
27332729
}
27342730

2735-
private[this] def toBigInt36(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean): BigInt = {
2731+
private[this] def toBigInt36(buf: Array[Byte], p: Int, limit: Int, s: Int): BigInt = {
27362732
val firstBlockLimit = limit - 18
27372733
var pos = p
27382734
var x1 = (buf(pos) - '0').toLong
@@ -2749,28 +2745,22 @@ final class JsonReader private[jsoniter_scala](
27492745
(dec >> 8 & 0xFF000000FFL) * 42949672960001000L + (dec >> 24 & 0xFF000000FFL) * 429496729600010L >> 32
27502746
} + buf(pos + 17) - 48000000048L
27512747
val q = x1 * 1000000000000000000L
2752-
var l = q + x2
2748+
val l = q + x2
27532749
val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
2754-
if (l >= 0 && h == 0) {
2755-
if (isNeg) l = -l
2756-
BigInt(l)
2757-
} else {
2758-
val signum =
2759-
if (isNeg) -1
2760-
else 1
2750+
if (l >= 0 && h == 0) BigInt((l ^ s) - s)
2751+
else {
27612752
var magnitude = this.magnitude
27622753
if (magnitude eq null) {
27632754
magnitude = new Array[Byte](128)
27642755
this.magnitude = magnitude
27652756
}
27662757
ByteArrayAccess.setLongReversed(magnitude, 0, h)
27672758
ByteArrayAccess.setLongReversed(magnitude, 8, l)
2768-
new BigInt(new java.math.BigInteger(signum, magnitude, 0, 16))
2759+
new BigInt(new java.math.BigInteger(s | 1, magnitude, 0, 16))
27692760
}
27702761
}
27712762

2772-
private[this] def toBigDecimal36(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean,
2773-
scale: Int): java.math.BigDecimal = {
2763+
private[this] def toBigDecimal36(buf: Array[Byte], p: Int, limit: Int, s: Int, scale: Int): java.math.BigDecimal = {
27742764
val firstBlockLimit = limit - 18
27752765
var pos = p
27762766
var x1 = (buf(pos) - '0').toLong
@@ -2787,27 +2777,22 @@ final class JsonReader private[jsoniter_scala](
27872777
(dec >> 8 & 0xFF000000FFL) * 42949672960001000L + (dec >> 24 & 0xFF000000FFL) * 429496729600010L >> 32
27882778
} + buf(pos + 17) - 48000000048L
27892779
val q = x1 * 1000000000000000000L
2790-
var l = q + x2
2780+
val l = q + x2
27912781
val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
2792-
if (l >= 0 && h == 0) {
2793-
if (isNeg) l = -l
2794-
java.math.BigDecimal.valueOf(l, scale)
2795-
} else {
2796-
val signum =
2797-
if (isNeg) -1
2798-
else 1
2782+
if (l >= 0 && h == 0) java.math.BigDecimal.valueOf((l ^ s) - s, scale)
2783+
else {
27992784
var magnitude = this.magnitude
28002785
if (magnitude eq null) {
28012786
magnitude = new Array[Byte](128)
28022787
this.magnitude = magnitude
28032788
}
28042789
ByteArrayAccess.setLongReversed(magnitude, 0, h)
28052790
ByteArrayAccess.setLongReversed(magnitude, 8, l)
2806-
new java.math.BigDecimal(new java.math.BigInteger(signum, magnitude, 0, 16), scale)
2791+
new java.math.BigDecimal(new java.math.BigInteger(s | 1, magnitude, 0, 16), scale)
28072792
}
28082793
}
28092794

2810-
private[this] def toBigInteger308(buf: Array[Byte], p: Int, limit: Int, isNeg: Boolean): java.math.BigInteger = {
2795+
private[this] def toBigInteger308(buf: Array[Byte], p: Int, limit: Int, s: Int): java.math.BigInteger = {
28112796
val len = limit - p
28122797
val last = (len * 222930821L >> 32).toInt << 3 // (len * Math.log(10) / Math.log(1L << 64)).toInt * 8
28132798
var magnitude = this.magnitude
@@ -2861,10 +2846,7 @@ final class JsonReader private[jsoniter_scala](
28612846
ByteArrayAccess.setLongReversed(magnitude, i, ByteArrayAccess.getLong(magnitude, i))
28622847
i += 8
28632848
}
2864-
val signum =
2865-
if (isNeg) -1
2866-
else 1
2867-
new java.math.BigInteger(signum, magnitude, 0, last + 8)
2849+
new java.math.BigInteger(s | 1, magnitude, 0, last + 8)
28682850
}
28692851

28702852
@tailrec

0 commit comments

Comments
 (0)