Skip to content

Commit 466c0ce

Browse files
committed
More efficient parsing of year in java.time._ values
1 parent 11a99b7 commit 466c0ce

File tree

3 files changed

+63
-64
lines changed
  • jsoniter-scala-core

3 files changed

+63
-64
lines changed

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2446,8 +2446,9 @@ final class JsonReader private[jsoniter_scala](
24462446
var x: Float =
24472447
if (e10 == 0 && m10 < 922337203685477580L) m10.toFloat
24482448
else if (m10 < 4294967296L && e10 >= digits - 23 && e10 <= 19 - digits) {
2449-
(if (e10 < 0) m10 / pow10Doubles(-e10)
2450-
else m10 * pow10Doubles(e10)).toFloat
2449+
val pow10 = pow10Doubles
2450+
(if (e10 < 0) m10 / pow10(-e10)
2451+
else m10 * pow10(e10)).toFloat
24512452
} else toFloat(m10, e10, from, newMark, pos)
24522453
if (isNeg) x = -x
24532454
if (mark > oldMark) mark = oldMark
@@ -3373,11 +3374,11 @@ final class JsonReader private[jsoniter_scala](
33733374

33743375
private[this] def periodError(pos: Int): Nothing = decodeError("illegal period", pos)
33753376

3376-
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3377+
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError(state match {
33773378
case 0 => "expected 'Y' or 'M' or 'W' or 'D' or digit"
33783379
case 1 => "expected 'M' or 'W' or 'D' or digit"
33793380
case 2 => "expected 'W' or 'D' or digit"
3380-
case 3 => "expected 'D' or digit"
3381+
case _ => "expected 'D' or digit"
33813382
}, pos)
33823383

33833384
private[this] def durationOrPeriodStartError(isNeg: Boolean): Nothing = decodeError {
@@ -3393,11 +3394,11 @@ final class JsonReader private[jsoniter_scala](
33933394

33943395
private[this] def durationError(pos: Int): Nothing = decodeError("illegal duration", pos)
33953396

3396-
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3397+
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError(state match {
33973398
case 0 => "expected 'D' or digit"
33983399
case 1 => "expected 'H' or 'M' or 'S or '.' or digit"
33993400
case 2 => "expected 'M' or 'S or '.' or digit"
3400-
case 3 => "expected 'S or '.' or digit"
3401+
case _ => "expected 'S or '.' or digit"
34013402
}, pos)
34023403

34033404
private[this] def yearError(t: Byte, maxDigits: Int, pos: Int, isNeg: Boolean, yearDigits: Int): Nothing = {

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

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ final class JsonReader private[jsoniter_scala](
17011701
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
17021702
head = pos + 5
17031703
year
1704-
} else parseNon4DigitYearWithByte('-', 10, year + 0x30303030, pos)
1704+
} else parseNon4DigitYearWithByte('-', 10, year, pos)
17051705
} else parseInstantYearWithDash(loadMoreOrError(pos))
17061706

17071707
@tailrec
@@ -1713,27 +1713,22 @@ final class JsonReader private[jsoniter_scala](
17131713
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
17141714
head = pos + 5
17151715
year
1716-
} else parseNon4DigitYearWithByte(t, 9, year + 0x30303030, pos)
1716+
} else parseNon4DigitYearWithByte(t, 9, year, pos)
17171717
} else parseYearWithByte(t, loadMoreOrError(pos))
17181718

1719-
private[this] def parseNon4DigitYearWithByte(t: Byte, maxDigits: Int, bs: Int, p: Int): Int = {
1719+
private[this] def parseNon4DigitYearWithByte(t: Byte, maxDigits: Int, y: Int, p: Int): Int = {
1720+
val bs = y + 0x30303030
17201721
val b1 = bs.toByte
1721-
if (b1 >= '0' && b1 <= '9') fourDigitYearWithByteError(t, p, bs)
1722-
var pos = p
1722+
if (b1 != '-' && b1 != '+') fourDigitYearWithByteError(t, p, bs)
1723+
var pos = p + 1
17231724
var buf = this.buf
1724-
val b2 = (bs >> 8).toByte
1725-
val b3 = (bs >> 16).toByte
1726-
val b4 = (bs >> 24).toByte
1727-
val b5 = buf(pos + 4)
1728-
if (b1 != '-' && b1 != '+') decodeError("expected '-' or '+' or digit", pos)
1729-
if (b2 < '0' || b2 > '9') digitError(pos + 1)
1730-
if (b3 < '0' || b3 > '9') digitError(pos + 2)
1731-
if (b4 < '0' || b4 > '9') digitError(pos + 3)
1732-
if (b5 < '0' || b5 > '9') digitError(pos + 4)
1733-
var year = b2 * 1000 + b3 * 100 + b4 * 10 + b5 - 53328 // 53328 == '0' * 1111
1725+
var year = ByteArrayAccess.getInt(buf, pos) - 0x30303030
1726+
val m = (year + 0x76767676 | year) & 0x80808080 // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
1727+
if (m != 0) digitError((java.lang.Integer.numberOfTrailingZeros(m) >> 3) + pos)
1728+
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
1729+
pos += 4
17341730
var yearDigits = 4
17351731
var b: Byte = 0
1736-
pos += 5
17371732
while ({
17381733
if (pos >= tail) {
17391734
pos = loadMoreOrError(pos)
@@ -2416,8 +2411,9 @@ final class JsonReader private[jsoniter_scala](
24162411
var x: Float =
24172412
if (e10 == 0 && m10 < 922337203685477580L) m10.toFloat
24182413
else if (m10 < 4294967296L && e10 >= digits - 23 && e10 <= 19 - digits) {
2419-
(if (e10 < 0) m10 / pow10Doubles(-e10)
2420-
else m10 * pow10Doubles(e10)).toFloat
2414+
val pow10 = pow10Doubles
2415+
(if (e10 < 0) m10 / pow10(-e10)
2416+
else m10 * pow10(e10)).toFloat
24212417
} else toFloat(m10, e10, from, newMark, pos)
24222418
if (isNeg) x = -x
24232419
if (mark > oldMark) mark = oldMark
@@ -3814,24 +3810,27 @@ final class JsonReader private[jsoniter_scala](
38143810
(year & 0x3) == 0 && (year * -1030792151 - 2061584303 > -1975684958 || (year & 0xF) == 0) // year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
38153811

38163812
private[this] def fourDigitYearWithByteError(t: Byte, pos: Int, bs: Int): Nothing = {
3817-
val b2 = (bs >> 8).toByte
3818-
val b3 = (bs >> 16).toByte
3819-
val b4 = (bs >> 24).toByte
3820-
if (b2 < '0' || b2 > '9') digitError(pos + 1)
3821-
if (b3 < '0' || b3 > '9') digitError(pos + 2)
3822-
if (b4 < '0' || b4 > '9') digitError(pos + 3)
3823-
tokenError(t, pos + 4)
3813+
val b1 = bs.toByte
3814+
if (b1 >= '0' && b1 <= '9') {
3815+
val b2 = (bs >> 8).toByte
3816+
val b3 = (bs >> 16).toByte
3817+
val b4 = (bs >> 24).toByte
3818+
if (b2 < '0' || b2 > '9') digitError(pos + 1)
3819+
if (b3 < '0' || b3 > '9') digitError(pos + 2)
3820+
if (b4 < '0' || b4 > '9') digitError(pos + 3)
3821+
tokenError(t, pos + 4)
3822+
} else decodeError("expected '-' or '+' or digit", pos)
38243823
}
38253824

38263825
private[this] def digitError(pos: Int): Nothing = decodeError("expected digit", pos)
38273826

38283827
private[this] def periodError(pos: Int): Nothing = decodeError("illegal period", pos)
38293828

3830-
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3829+
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError(state match {
38313830
case 0 => "expected 'Y' or 'M' or 'W' or 'D' or digit"
38323831
case 1 => "expected 'M' or 'W' or 'D' or digit"
38333832
case 2 => "expected 'W' or 'D' or digit"
3834-
case 3 => "expected 'D' or digit"
3833+
case _ => "expected 'D' or digit"
38353834
}, pos)
38363835

38373836
private[this] def durationOrPeriodStartError(isNeg: Boolean): Nothing = decodeError {
@@ -3847,11 +3846,11 @@ final class JsonReader private[jsoniter_scala](
38473846

38483847
private[this] def durationError(pos: Int): Nothing = decodeError("illegal duration", pos)
38493848

3850-
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3849+
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError(state match {
38513850
case 0 => "expected 'D' or digit"
38523851
case 1 => "expected 'H' or 'M' or 'S or '.' or digit"
38533852
case 2 => "expected 'M' or 'S or '.' or digit"
3854-
case 3 => "expected 'S or '.' or digit"
3853+
case _ => "expected 'S or '.' or digit"
38553854
}, pos)
38563855

38573856
private[this] def yearError(t: Byte, maxDigits: Int, pos: Int, isNeg: Boolean, yearDigits: Int): Nothing = {

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

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,7 @@ final class JsonReader private[jsoniter_scala](
17011701
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
17021702
head = pos + 5
17031703
year
1704-
} else parseNon4DigitYearWithByte('-', 10, year + 0x30303030, pos)
1704+
} else parseNon4DigitYearWithByte('-', 10, year, pos)
17051705
} else parseInstantYearWithDash(loadMoreOrError(pos))
17061706

17071707
@tailrec
@@ -1713,27 +1713,22 @@ final class JsonReader private[jsoniter_scala](
17131713
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
17141714
head = pos + 5
17151715
year
1716-
} else parseNon4DigitYearWithByte(t, 9, year + 0x30303030, pos)
1716+
} else parseNon4DigitYearWithByte(t, 9, year, pos)
17171717
} else parseYearWithByte(t, loadMoreOrError(pos))
17181718

1719-
private[this] def parseNon4DigitYearWithByte(t: Byte, maxDigits: Int, bs: Int, p: Int): Int = {
1719+
private[this] def parseNon4DigitYearWithByte(t: Byte, maxDigits: Int, y: Int, p: Int): Int = {
1720+
val bs = y + 0x30303030
17201721
val b1 = bs.toByte
1721-
if (b1 >= '0' && b1 <= '9') fourDigitYearWithByteError(t, p, bs)
1722-
var pos = p
1722+
if (b1 != '-' && b1 != '+') fourDigitYearWithByteError(t, p, bs)
1723+
var pos = p + 1
17231724
var buf = this.buf
1724-
val b2 = (bs >> 8).toByte
1725-
val b3 = (bs >> 16).toByte
1726-
val b4 = (bs >> 24).toByte
1727-
val b5 = buf(pos + 4)
1728-
if (b1 != '-' && b1 != '+') decodeError("expected '-' or '+' or digit", pos)
1729-
if (b2 < '0' || b2 > '9') digitError(pos + 1)
1730-
if (b3 < '0' || b3 > '9') digitError(pos + 2)
1731-
if (b4 < '0' || b4 > '9') digitError(pos + 3)
1732-
if (b5 < '0' || b5 > '9') digitError(pos + 4)
1733-
var year = b2 * 1000 + b3 * 100 + b4 * 10 + b5 - 53328 // 53328 == '0' * 1111
1725+
var year = ByteArrayAccess.getInt(buf, pos) - 0x30303030
1726+
val m = (year + 0x76767676 | year) & 0x80808080 // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
1727+
if (m != 0) digitError((java.lang.Integer.numberOfTrailingZeros(m) >> 3) + pos)
1728+
year = (year * 2561 >> 8 & 0xFF00FF) * 6553601 >> 16
1729+
pos += 4
17341730
var yearDigits = 4
17351731
var b: Byte = 0
1736-
pos += 5
17371732
while ({
17381733
if (pos >= tail) {
17391734
pos = loadMoreOrError(pos)
@@ -2416,8 +2411,9 @@ final class JsonReader private[jsoniter_scala](
24162411
var x: Float =
24172412
if (e10 == 0 && m10 < 922337203685477580L) m10.toFloat
24182413
else if (m10 < 4294967296L && e10 >= digits - 23 && e10 <= 19 - digits) {
2419-
(if (e10 < 0) m10 / pow10Doubles(-e10)
2420-
else m10 * pow10Doubles(e10)).toFloat
2414+
val pow10 = pow10Doubles
2415+
(if (e10 < 0) m10 / pow10(-e10)
2416+
else m10 * pow10(e10)).toFloat
24212417
} else toFloat(m10, e10, from, newMark, pos)
24222418
if (isNeg) x = -x
24232419
if (mark > oldMark) mark = oldMark
@@ -3810,24 +3806,27 @@ final class JsonReader private[jsoniter_scala](
38103806
(year & 0x3) == 0 && (year * -1030792151 - 2061584303 > -1975684958 || (year & 0xF) == 0) // year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
38113807

38123808
private[this] def fourDigitYearWithByteError(t: Byte, pos: Int, bs: Int): Nothing = {
3813-
val b2 = (bs >> 8).toByte
3814-
val b3 = (bs >> 16).toByte
3815-
val b4 = (bs >> 24).toByte
3816-
if (b2 < '0' || b2 > '9') digitError(pos + 1)
3817-
if (b3 < '0' || b3 > '9') digitError(pos + 2)
3818-
if (b4 < '0' || b4 > '9') digitError(pos + 3)
3819-
tokenError(t, pos + 4)
3809+
val b1 = bs.toByte
3810+
if (b1 >= '0' && b1 <= '9') {
3811+
val b2 = (bs >> 8).toByte
3812+
val b3 = (bs >> 16).toByte
3813+
val b4 = (bs >> 24).toByte
3814+
if (b2 < '0' || b2 > '9') digitError(pos + 1)
3815+
if (b3 < '0' || b3 > '9') digitError(pos + 2)
3816+
if (b4 < '0' || b4 > '9') digitError(pos + 3)
3817+
tokenError(t, pos + 4)
3818+
} else decodeError("expected '-' or '+' or digit", pos)
38203819
}
38213820

38223821
private[this] def digitError(pos: Int): Nothing = decodeError("expected digit", pos)
38233822

38243823
private[this] def periodError(pos: Int): Nothing = decodeError("illegal period", pos)
38253824

3826-
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3825+
private[this] def periodError(state: Int, pos: Int): Nothing = decodeError(state match {
38273826
case 0 => "expected 'Y' or 'M' or 'W' or 'D' or digit"
38283827
case 1 => "expected 'M' or 'W' or 'D' or digit"
38293828
case 2 => "expected 'W' or 'D' or digit"
3830-
case 3 => "expected 'D' or digit"
3829+
case _ => "expected 'D' or digit"
38313830
}, pos)
38323831

38333832
private[this] def durationOrPeriodStartError(isNeg: Boolean): Nothing = decodeError {
@@ -3843,11 +3842,11 @@ final class JsonReader private[jsoniter_scala](
38433842

38443843
private[this] def durationError(pos: Int): Nothing = decodeError("illegal duration", pos)
38453844

3846-
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError((state: @switch) match {
3845+
private[this] def durationError(state: Int, pos: Int): Nothing = decodeError(state match {
38473846
case 0 => "expected 'D' or digit"
38483847
case 1 => "expected 'H' or 'M' or 'S or '.' or digit"
38493848
case 2 => "expected 'M' or 'S or '.' or digit"
3850-
case 3 => "expected 'S or '.' or digit"
3849+
case _ => "expected 'S or '.' or digit"
38513850
}, pos)
38523851

38533852
private[this] def yearError(t: Byte, maxDigits: Int, pos: Int, isNeg: Boolean, yearDigits: Int): Nothing = {

0 commit comments

Comments
 (0)