Skip to content

Commit

Permalink
More efficient serialization of java.time._ values for JVMs
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk committed Apr 1, 2022
1 parent 6ece346 commit 04b0f5e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1608,10 +1608,7 @@ final class JsonWriter private[jsoniter_scala](
if ((second | nano) != 0) {
buf(pos) = ':'
pos = write2Digits(second, pos + 1, buf, ds)
if (nano != 0) {
buf(pos) = '.'
pos = writeNanos(nano, pos + 1, buf, ds)
}
if (nano != 0) pos = writeNanos(nano, pos, buf, ds)
}
pos
}
Expand All @@ -1622,16 +1619,16 @@ final class JsonWriter private[jsoniter_scala](
pos = write2Digits(minute, pos + 1, buf, ds)
buf(pos) = ':'
pos = write2Digits(second, pos + 1, buf, ds)
if (nano != 0) {
buf(pos) = '.'
writeNanos(nano, pos + 1, buf, ds)
} else pos
if (nano != 0) writeNanos(nano, pos, buf, ds)
else pos
}

private[this] def writeNanos(q0: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Int = {
var pos = p
buf(pos) = '.'
val q1 = q0 / 10000000
val r1 = q0 - q1 * 10000000
var pos = write2Digits(q1, p, buf, ds)
pos = write2Digits(q1, pos + 1, buf, ds)
val q2 = r1 / 100000
val r2 = r1 - q2 * 100000
val d = ds(q2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1293,11 +1293,8 @@ final class JsonWriter private[jsoniter_scala](
val buf = this.buf
val ds = digits
buf(pos) = '"'
pos = writeLocalDate(year, month, day, pos + 1, buf, ds)
buf(pos) = 'T'
pos = writeLocalTime(hour, minute, second, x.getNano, pos + 1, buf, ds)
buf(pos) = 'Z'
buf(pos + 1) = '"'
pos = writeLocalTime(hour, minute, second, x.getNano, writeLocalDate(year, month, day, pos + 1, buf, ds), buf, ds)
ByteArrayAccess.setShort(buf, pos, 0x225A)
pos + 2
}

Expand All @@ -1307,22 +1304,21 @@ final class JsonWriter private[jsoniter_scala](
}

private[this] def writeLocalDate(x: LocalDate): Unit = count = {
var pos = ensureBufCapacity(18) // 18 == LocalDate.MAX.toString.length + 2
var pos = ensureBufCapacity(19) // 19 == java.time.Year.MAX_VALUE.toString.length + 9
val buf = this.buf
val ds = digits
buf(pos) = '"'
pos = writeLocalDate(x, pos + 1, buf, digits)
buf(pos) = '"'
pos + 1
pos = writeYear(x.getYear, pos + 1, buf, ds)
ByteArrayAccess.setLong(buf, pos, ds(x.getDayOfMonth).toLong << 32 | ds(x.getMonthValue) << 8 | 0x2200002D00002DL)
pos + 7
}

private[this] def writeLocalDateTime(x: LocalDateTime): Unit = count = {
var pos = ensureBufCapacity(37) // 37 == LocalDateTime.MAX.toString.length + 2
val buf = this.buf
val ds = digits
buf(pos) = '"'
pos = writeLocalDate(x.toLocalDate, pos + 1, buf, ds)
buf(pos) = 'T'
pos = writeLocalTime(x.toLocalTime, pos + 1, buf, ds)
pos = writeLocalTime(x.toLocalTime, writeLocalDate(x.toLocalDate, pos + 1, buf, ds), buf, ds)
buf(pos) = '"'
pos + 1
}
Expand Down Expand Up @@ -1350,9 +1346,8 @@ final class JsonWriter private[jsoniter_scala](
val buf = this.buf
val ds = digits
buf(pos) = '"'
pos = writeLocalDate(x.toLocalDate, pos + 1, buf, ds)
buf(pos) = 'T'
pos = writeOffset(x.getOffset, writeLocalTime(x.toLocalTime, pos + 1, buf, ds), buf, ds)
pos = writeOffset(x.getOffset,
writeLocalTime(x.toLocalTime, writeLocalDate(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds)
buf(pos) = '"'
pos + 1
}
Expand Down Expand Up @@ -1433,9 +1428,8 @@ final class JsonWriter private[jsoniter_scala](
var buf = this.buf
val ds = digits
buf(pos) = '"'
pos = writeLocalDate(x.toLocalDate, pos + 1, buf, ds)
buf(pos) = 'T'
pos = writeOffset(x.getOffset, writeLocalTime(x.toLocalTime, pos + 1, buf, ds), buf, ds)
pos = writeOffset(x.getOffset,
writeLocalTime(x.toLocalTime, writeLocalDate(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds)
val zone = x.getZone
if (!zone.isInstanceOf[ZoneOffset]) {
val zoneId = zone.getId
Expand Down Expand Up @@ -1495,16 +1489,14 @@ final class JsonWriter private[jsoniter_scala](

private[this] def writeLocalDate(x: LocalDate, p: Int, buf: Array[Byte], ds: Array[Short]): Int = {
val pos = writeYear(x.getYear, p, buf, ds)
ByteArrayAccess.setInt(buf, pos, ds(x.getMonthValue) << 8 | 0x2D00002D)
ByteArrayAccess.setShort(buf, pos + 4, ds(x.getDayOfMonth))
pos + 6
ByteArrayAccess.setLong(buf, pos, ds(x.getDayOfMonth).toLong << 32 | ds(x.getMonthValue) << 8 | 0x5400002D00002DL)
pos + 7
}

private[this] def writeLocalDate(year: Int, month: Int, day: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Int = {
val pos = writeYear(year, p, buf, ds)
ByteArrayAccess.setInt(buf, pos, ds(month) << 8 | 0x2D00002D)
ByteArrayAccess.setShort(buf, pos + 4, ds(day))
pos + 6
ByteArrayAccess.setLong(buf, pos, ds(day).toLong << 32 | ds(month) << 8 | 0x5400002D00002DL)
pos + 7
}

private[this] def writeYear(year: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int =
Expand All @@ -1521,29 +1513,23 @@ final class JsonWriter private[jsoniter_scala](
}

private[this] def writeLocalTime(x: LocalTime, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = {
val d = ds(x.getHour) | ds(x.getMinute).toLong << 24
val second = x.getSecond
val nano = x.getNano
val d = ds(x.getHour) | ds(x.getMinute).toLong << 24 | 0x3A00003A0000L
if ((second | nano) == 0) {
ByteArrayAccess.setLong(buf, pos, d | 0x3A0000)
ByteArrayAccess.setLong(buf, pos, d)
pos + 5
} else {
ByteArrayAccess.setLong(buf, pos, d | ds(second).toLong << 48 | 0x3A00003A0000L)
ByteArrayAccess.setLong(buf, pos, ds(second).toLong << 48 | d)
if (nano == 0) pos + 8
else {
buf(pos + 8) = '.'
writeNanos(nano, pos + 9, buf, ds)
}
else writeNanos(nano, pos + 8, buf, ds)
}
}

private[this] def writeLocalTime(hour: Int, minute: Int, second: Int, nano: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = {
ByteArrayAccess.setLong(buf, pos, ds(hour) | ds(minute).toLong << 24 | ds(second).toLong << 48 | 0x3A00003A0000L)
if (nano == 0) pos + 8
else {
buf(pos + 8) = '.'
writeNanos(nano, pos + 9, buf, ds)
}
else writeNanos(nano, pos + 8, buf, ds)
}

private[this] def writeNanos(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = {
Expand All @@ -1552,15 +1538,26 @@ final class JsonWriter private[jsoniter_scala](
val p2 = r1 * 175921861L
val q2 = (p2 >> 44).toInt // divide a positive int by 100000
val d = ds(q2)
ByteArrayAccess.setInt(buf, pos, ds(q1) | d << 16)
if ((p2 & 0xFFFF8000000L) == 0 && d <= 0x3039) pos + 3 // check if nanos are divisible by 1000000
else {
var m = ds(q1) << 8 | d.toLong << 24 | 0x300000000000002EL
if ((p2 & 0xFFFF8000000L) == 0 && d <= 0x3039) { // check if nanos are divisible by 1000000
ByteArrayAccess.setInt(buf, pos, m.toInt)
pos + 4
} else {
val r2 = r1 - q2 * 100000
val p3 = r2 * 2199023256L
val q3 = (p3 >> 41).toInt // divide a positive int by 1000
ByteArrayAccess.setShort(buf, pos + 4, ds(q3))
if ((p3 & 0x1FF80000000L) == 0) pos + 6 // check if r2 divisible by 1000
else write3Digits(r2 - q3 * 1000, pos + 6, buf, ds)
m |= ds(q3).toLong << 40
if ((p3 & 0x1FF80000000L) == 0) { // check if r2 divisible by 1000
ByteArrayAccess.setLong(buf, pos, m)
pos + 7
} else {
val r4 = r2 - q3 * 1000
val q4 = r4 * 1311 >> 17 // divide a small positive int by 100
val r5 = r4 - q4 * 100
ByteArrayAccess.setLong(buf, pos, q4.toLong << 56 | m)
ByteArrayAccess.setShort(buf, pos + 8, ds(r5))
pos + 10
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1556,10 +1556,7 @@ final class JsonWriter private[jsoniter_scala](
if ((second | nano) != 0) {
buf(pos) = ':'
pos = write2Digits(second, pos + 1, buf, ds)
if (nano != 0) {
buf(pos) = '.'
pos = writeNanos(nano, pos + 1, buf, ds)
}
if (nano != 0) pos = writeNanos(nano, pos, buf, ds)
}
pos
}
Expand All @@ -1570,16 +1567,16 @@ final class JsonWriter private[jsoniter_scala](
pos = write2Digits(minute, pos + 1, buf, ds)
buf(pos) = ':'
pos = write2Digits(second, pos + 1, buf, ds)
if (nano != 0) {
buf(pos) = '.'
writeNanos(nano, pos + 1, buf, ds)
} else pos
if (nano != 0) writeNanos(nano, pos, buf, ds)
else pos
}

private[this] def writeNanos(q0: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Int = {
var pos = p
buf(pos) = '.'
val q1 = (q0 * 1801439851L >> 54).toInt // divide a positive int by 10000000
val r1 = q0 - q1 * 10000000
var pos = write2Digits(q1, p, buf, ds)
pos = write2Digits(q1, pos + 1, buf, ds)
val p2 = r1 * 175921861L
val q2 = (p2 >> 44).toInt // divide a positive int by 100000
val d = ds(q2)
Expand Down

0 comments on commit 04b0f5e

Please sign in to comment.