Skip to content

Commit

Permalink
Yet more efficient encoding of sum types (#1346)
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk authored Feb 27, 2025
1 parent 5bd5dd3 commit 3877ea7
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 53 deletions.
88 changes: 80 additions & 8 deletions zio-json/shared/src/main/scala-2.x/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -527,20 +527,18 @@ object DeriveJsonEncoder {
def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = {
out.write('{')
val indent_ = JsonEncoder.bump(indent)
JsonEncoder.pad(indent_, out)
val fields = this.fields
var idx = 0
var prevFields = false
val fields = this.fields
var idx = 0
var comma = false
while (idx < fields.length) {
val field = fields(idx)
idx += 1
val p = field.p.dereference(a)
if (field.skip(p)) ()
else {
if (prevFields) {
out.write(',')
JsonEncoder.pad(indent_, out)
} else prevFields = true
if (comma) out.write(',')
else comma = true
JsonEncoder.pad(indent_, out)
out.write(if (indent eq None) field.encodedName else field.prettyEncodedName)
field.encoder.unsafeEncode(p, indent_, out)
}
Expand Down Expand Up @@ -745,6 +743,80 @@ private[this] final class NestedWriter(out: Write, indent: Option[Int]) extends
i += 1
}
}

@inline override def write(c1: Char, c2: Char): Unit =
if (state == 0) out.write(c1, c2)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
}

@inline override def write(c1: Char, c2: Char, c3: Char): Unit =
if (state == 0) out.write(c1, c2, c3)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
}

@inline override def write(c1: Char, c2: Char, c3: Char, c4: Char): Unit =
if (state == 0) out.write(c1, c2, c3, c4)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
nonZeroStateWrite(c4)
}

@inline override def write(c1: Char, c2: Char, c3: Char, c4: Char, c5: Char): Unit =
if (state == 0) out.write(c1, c2, c3, c4, c5)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
nonZeroStateWrite(c4)
nonZeroStateWrite(c5)
}

@inline override def write(s: Short): Unit =
if (state == 0) out.write(s)
else {
nonZeroStateWrite((s & 0xff).toChar)
nonZeroStateWrite((s >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short): Unit =
if (state == 0) out.write(s1, s2)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short, s3: Short): Unit =
if (state == 0) out.write(s1, s2, s3)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
nonZeroStateWrite((s3 & 0xff).toChar)
nonZeroStateWrite((s3 >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short, s3: Short, s4: Short): Unit =
if (state == 0) out.write(s1, s2, s3, s4)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
nonZeroStateWrite((s3 & 0xff).toChar)
nonZeroStateWrite((s3 >> 8).toChar)
nonZeroStateWrite((s4 & 0xff).toChar)
nonZeroStateWrite((s4 >> 8).toChar)
}
}

object DeriveJsonCodec {
Expand Down
84 changes: 78 additions & 6 deletions zio-json/shared/src/main/scala-3/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -547,20 +547,18 @@ sealed class JsonEncoderDerivation(config: JsonCodecConfiguration) extends Deriv
def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = {
out.write('{')
val indent_ = JsonEncoder.bump(indent)
JsonEncoder.pad(indent_, out)
val fields = this.fields
var idx = 0
var prevFields = false
var comma = false
while (idx < fields.length) {
val field = fields(idx)
idx += 1
val p = field.p.deref(a)
if (field.skip(p)) ()
else {
if (prevFields) {
out.write(',')
JsonEncoder.pad(indent_, out)
} else prevFields = true
if (comma) out.write(',')
else comma = true
JsonEncoder.pad(indent_, out)
out.write(if (indent eq None) field.encodedName else field.prettyEncodedName)
field.encoder.unsafeEncode(p, indent_, out)
}
Expand Down Expand Up @@ -766,6 +764,80 @@ object DeriveJsonEncoder extends JsonEncoderDerivation(JsonCodecConfiguration.de
i += 1
}
}

@inline override def write(c1: Char, c2: Char): Unit =
if (state == 0) out.write(c1, c2)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
}

@inline override def write(c1: Char, c2: Char, c3: Char): Unit =
if (state == 0) out.write(c1, c2, c3)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
}

@inline override def write(c1: Char, c2: Char, c3: Char, c4: Char): Unit =
if (state == 0) out.write(c1, c2, c3, c4)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
nonZeroStateWrite(c4)
}

@inline override def write(c1: Char, c2: Char, c3: Char, c4: Char, c5: Char): Unit =
if (state == 0) out.write(c1, c2, c3, c4, c5)
else {
nonZeroStateWrite(c1)
nonZeroStateWrite(c2)
nonZeroStateWrite(c3)
nonZeroStateWrite(c4)
nonZeroStateWrite(c5)
}

@inline override def write(s: Short): Unit =
if (state == 0) out.write(s)
else {
nonZeroStateWrite((s & 0xff).toChar)
nonZeroStateWrite((s >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short): Unit =
if (state == 0) out.write(s1, s2)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short, s3: Short): Unit =
if (state == 0) out.write(s1, s2, s3)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
nonZeroStateWrite((s3 & 0xff).toChar)
nonZeroStateWrite((s3 >> 8).toChar)
}

@inline override def write(s1: Short, s2: Short, s3: Short, s4: Short): Unit =
if (state == 0) out.write(s1, s2, s3, s4)
else {
nonZeroStateWrite((s1 & 0xff).toChar)
nonZeroStateWrite((s1 >> 8).toChar)
nonZeroStateWrite((s2 & 0xff).toChar)
nonZeroStateWrite((s2 >> 8).toChar)
nonZeroStateWrite((s3 & 0xff).toChar)
nonZeroStateWrite((s3 >> 8).toChar)
nonZeroStateWrite((s4 & 0xff).toChar)
nonZeroStateWrite((s4 >> 8).toChar)
}
}
}

Expand Down
62 changes: 23 additions & 39 deletions zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -414,14 +414,11 @@ private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 {

private[this] def unsafeEncodePadded(as: Array[A], indent: Option[Int], out: Write): Unit = {
val indent_ = bump(indent)
pad(indent_, out)
val len = as.length
var i = 0
val len = as.length
var i = 0
while (i < len) {
if (i != 0) {
out.write(',')
pad(indent_, out)
}
if (i != 0) out.write(',')
pad(indent_, out)
A.unsafeEncode(as(i), indent_, out)
i += 1
}
Expand Down Expand Up @@ -473,27 +470,20 @@ private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 {
}

private[this] def unsafeEncodeCompact(as: List[A], indent: Option[Int], out: Write): Unit = {
var as_ = as
var first = true
var as_ = as
while (as_ ne Nil) {
if (first) first = false
else out.write(',')
if (as_ ne as) out.write(',')
A.unsafeEncode(as_.head, indent, out)
as_ = as_.tail
}
}

private[this] def unsafeEncodePadded(as: List[A], indent: Option[Int], out: Write): Unit = {
val indent_ = bump(indent)
pad(indent_, out)
var as_ = as
var first = true
var as_ = as
while (as_ ne Nil) {
if (first) first = false
else {
out.write(',')
pad(indent_, out)
}
if (as_ ne as) out.write(',')
pad(indent_, out)
A.unsafeEncode(as_.head, indent_, out)
as_ = as_.tail
}
Expand Down Expand Up @@ -560,24 +550,21 @@ private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 {

private[this] def unsafeEncodeCompact(as: T[A], indent: Option[Int], out: Write): Unit =
as.foreach {
var first = true
var comma = false
a =>
if (first) first = false
else out.write(',')
if (comma) out.write(',')
else comma = true
A.unsafeEncode(a, indent, out)
}

private[this] def unsafeEncodePadded(as: T[A], indent: Option[Int], out: Write): Unit = {
val indent_ = bump(indent)
pad(indent_, out)
as.foreach {
var first = true
var comma = false
a =>
if (first) first = false
else {
out.write(',')
pad(indent_, out)
}
if (comma) out.write(',')
else comma = true
pad(indent_, out)
A.unsafeEncode(a, indent_, out)
}
pad(indent, out)
Expand Down Expand Up @@ -618,11 +605,11 @@ private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 {

private[this] def unsafeEncodeCompact(kvs: T[K, A], indent: Option[Int], out: Write): Unit =
kvs.foreach {
var first = true
var comma = false
kv =>
if (!A.isNothing(kv._2)) {
if (first) first = false
else out.write(',')
if (comma) out.write(',')
else comma = true
string.unsafeEncode(K.unsafeEncodeField(kv._1), indent, out)
out.write(':')
A.unsafeEncode(kv._2, indent, out)
Expand All @@ -631,16 +618,13 @@ private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 {

private[this] def unsafeEncodePadded(kvs: T[K, A], indent: Option[Int], out: Write): Unit = {
val indent_ = bump(indent)
pad(indent_, out)
kvs.foreach {
var first = true
var comman = false
kv =>
if (!A.isNothing(kv._2)) {
if (first) first = false
else {
out.write(',')
pad(indent_, out)
}
if (comman) out.write(',')
else comman = true
pad(indent_, out)
string.unsafeEncode(K.unsafeEncodeField(kv._1), indent_, out)
out.write(" : ")
A.unsafeEncode(kv._2, indent_, out)
Expand Down

0 comments on commit 3877ea7

Please sign in to comment.