Skip to content

Commit

Permalink
More efficient encoding to AST (#1292)
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk authored Feb 6, 2025
1 parent 6446a1c commit 84cc739
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 51 deletions.
26 changes: 12 additions & 14 deletions zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,20 +204,18 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
override def toJsonAST(a: A): Either[String, Json] = encoder.toJsonAST(a)
}

implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString, Json.Bool.apply)
implicit val symbol: JsonEncoder[Symbol] = string.contramap(_.name)
implicit val byte: JsonEncoder[Byte] = explicit(_.toString, n => Json.Num(n))
implicit val short: JsonEncoder[Short] = explicit(_.toString, n => Json.Num(n))
implicit val int: JsonEncoder[Int] = explicit(_.toString, n => Json.Num(n))
implicit val long: JsonEncoder[Long] = explicit(_.toString, n => Json.Num(n))
implicit val bigInteger: JsonEncoder[java.math.BigInteger] =
explicit(_.toString, n => Json.Num(new java.math.BigDecimal(n)))
implicit val scalaBigInt: JsonEncoder[BigInt] =
explicit(_.toString, n => Json.Num(new java.math.BigDecimal(n.bigInteger)))
implicit val double: JsonEncoder[Double] = explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val float: JsonEncoder[Float] = explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val bigDecimal: JsonEncoder[java.math.BigDecimal] = explicit(_.toString, Json.Num.apply)
implicit val scalaBigDecimal: JsonEncoder[BigDecimal] = explicit(_.toString, n => Json.Num(n.bigDecimal))
implicit val boolean: JsonEncoder[Boolean] = explicit(_.toString, Json.Bool.apply)
implicit val symbol: JsonEncoder[Symbol] = string.contramap(_.name)
implicit val byte: JsonEncoder[Byte] = explicit(_.toString, Json.Num.apply)
implicit val short: JsonEncoder[Short] = explicit(_.toString, Json.Num.apply)
implicit val int: JsonEncoder[Int] = explicit(_.toString, Json.Num.apply)
implicit val long: JsonEncoder[Long] = explicit(_.toString, Json.Num.apply)
implicit val bigInteger: JsonEncoder[java.math.BigInteger] = explicit(_.toString, Json.Num.apply)
implicit val scalaBigInt: JsonEncoder[BigInt] = explicit(_.toString, Json.Num.apply)
implicit val double: JsonEncoder[Double] = explicit(SafeNumbers.toString, Json.Num.apply)
implicit val float: JsonEncoder[Float] = explicit(SafeNumbers.toString, Json.Num.apply)
implicit val bigDecimal: JsonEncoder[java.math.BigDecimal] = explicit(_.toString, n => new Json.Num(n))
implicit val scalaBigDecimal: JsonEncoder[BigDecimal] = explicit(_.toString, Json.Num.apply)

implicit def option[A](implicit A: JsonEncoder[A]): JsonEncoder[Option[A]] = new JsonEncoder[Option[A]] {
def unsafeEncode(oa: Option[A], indent: Option[Int], out: Write): Unit =
Expand Down
85 changes: 48 additions & 37 deletions zio-json/shared/src/main/scala/zio/json/ast/ast.scala
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ object Json {
Json.Obj(Chunk.fromArray(array))
}

override def asObject: Some[Json.Obj] = Some(this)
override def asObject: Some[Json.Obj] = new Some(this)
override def mapObject(f: Json.Obj => Json.Obj): Json.Obj = f(this)
override def mapObjectKeys(f: String => String): Json.Obj = Json.Obj(fields.map(e => f(e._1) -> e._2))
override def mapObjectValues(f: Json => Json): Json.Obj = mapValues(f)
Expand All @@ -393,16 +393,16 @@ object Json {

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Obj =
json match {
case obj @ Obj(_) => obj
case _ => Lexer.error("Not an object", trace)
case obj: Obj => obj
case _ => Lexer.error("Not an object", trace)
}
}
private lazy val obje = JsonEncoder.keyValueChunk[String, Json]
implicit val encoder: JsonEncoder[Obj] = new JsonEncoder[Obj] {
def unsafeEncode(a: Obj, indent: Option[Int], out: Write): Unit =
obje.unsafeEncode(a.fields, indent, out)

override final def toJsonAST(a: Obj): Either[String, Json] = Right(a)
override final def toJsonAST(a: Obj): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Obj] = JsonCodec(encoder, decoder)
Expand All @@ -424,7 +424,7 @@ object Json {
} ++ leftover)
}

override def asArray: Some[Chunk[Json]] = Some(elements)
override def asArray: Some[Chunk[Json]] = new Some(elements)
override def mapArray(f: Chunk[Json] => Chunk[Json]): Json.Arr = Json.Arr(f(elements))
override def mapArrayValues(f: Json => Json): Json.Arr = Json.Arr(elements.map(f))
}
Expand All @@ -446,22 +446,22 @@ object Json {

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Arr =
json match {
case arr @ Arr(_) => arr
case _ => Lexer.error("Not an array", trace)
case arr: Arr => arr
case _ => Lexer.error("Not an array", trace)
}
}
private lazy val arre = JsonEncoder.chunk[Json]
implicit val encoder: JsonEncoder[Arr] = new JsonEncoder[Arr] {
def unsafeEncode(a: Arr, indent: Option[Int], out: Write): Unit =
arre.unsafeEncode(a.elements, indent, out)

override final def toJsonAST(a: Arr): Either[String, Json] = Right(a)
override final def toJsonAST(a: Arr): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Arr] = JsonCodec(encoder, decoder)
}
final case class Bool(value: Boolean) extends Json {
override def asBoolean: Some[Boolean] = Some(value)
override def asBoolean: Some[Boolean] = new Some(value)
override def mapBoolean(f: Boolean => Boolean): Json.Bool = Json.Bool(f(value))
}

Expand All @@ -479,22 +479,22 @@ object Json {

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Bool =
json match {
case b @ Bool(_) => b
case _ => Lexer.error("Not a bool value", trace)
case b: Bool => b
case _ => Lexer.error("Not a bool value", trace)
}
}
implicit val encoder: JsonEncoder[Bool] = new JsonEncoder[Bool] {
def unsafeEncode(a: Bool, indent: Option[Int], out: Write): Unit =
JsonEncoder.boolean.unsafeEncode(a.value, indent, out)

override final def toJsonAST(a: Bool): Either[String, Json] = Right(a)
override final def toJsonAST(a: Bool): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Bool] = JsonCodec(encoder, decoder)
}
final case class Str(value: String) extends Json {
override def asString: Some[String] = Some(value)
override def mapString(f: String => String): Json.Str = Json.Str(f(value))
override def asString: Some[String] = new Some(value)
override def mapString(f: String => String): Json.Str = new Json.Str(f(value))
}
object Str {
implicit val decoder: JsonDecoder[Str] = new JsonDecoder[Str] {
Expand All @@ -503,47 +503,59 @@ object Json {

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Str =
json match {
case s @ Str(_) => s
case _ => Lexer.error("Not a string value", trace)
case s: Str => s
case _ => Lexer.error("Not a string value", trace)
}
}
implicit val encoder: JsonEncoder[Str] = new JsonEncoder[Str] {
def unsafeEncode(a: Str, indent: Option[Int], out: Write): Unit =
JsonEncoder.string.unsafeEncode(a.value, indent, out)

override final def toJsonAST(a: Str): Either[String, Json] = Right(a)
override final def toJsonAST(a: Str): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Str] = JsonCodec(encoder, decoder)
}
final case class Num(value: java.math.BigDecimal) extends Json {
override def asNumber: Some[Json.Num] = Some(this)
override def mapNumber(f: java.math.BigDecimal => java.math.BigDecimal): Json.Num = Json.Num(f(value))
override def asNumber: Some[Json.Num] = new Some(this)
override def mapNumber(f: java.math.BigDecimal => java.math.BigDecimal): Json.Num = new Json.Num(f(value))
}
object Num {
def apply(value: Byte): Num = Num(BigDecimal(value.toInt).bigDecimal)
def apply(value: Short): Num = Num(BigDecimal(value.toInt).bigDecimal)
def apply(value: Int): Num = Num(BigDecimal(value).bigDecimal)
def apply(value: Long): Num = Num(BigDecimal(value).bigDecimal)
def apply(value: BigDecimal): Num = Num(value.bigDecimal)
def apply(value: Float): Num = Num(BigDecimal.decimal(value).bigDecimal)
def apply(value: Double): Num = Num(BigDecimal(value).bigDecimal)
@inline def apply(value: Byte): Num = apply(value.toInt)
@inline def apply(value: Short): Num = apply(value.toInt)
def apply(value: Int): Num = new Num({
if (value < 512 && value > -512) new java.math.BigDecimal(value)
else BigDecimal(value).bigDecimal
})
def apply(value: Long): Num = new Num({
if (value < 512 && value > -512) new java.math.BigDecimal(value)
else BigDecimal(value).bigDecimal
})
@inline def apply(value: BigDecimal): Num = new Num(value.bigDecimal)
def apply(value: BigInt): Num =
if (value.isValidLong) apply(value.toLong)
else new Json.Num(new java.math.BigDecimal(value.bigInteger))
def apply(value: java.math.BigInteger): Num =
if (value.bitCount < 64) apply(value.longValue)
else new Json.Num(new java.math.BigDecimal(value))
def apply(value: Float): Num = new Num(new java.math.BigDecimal(value.toString))
def apply(value: Double): Num = new Num(new java.math.BigDecimal(value))

implicit val decoder: JsonDecoder[Num] = new JsonDecoder[Num] {
def unsafeDecode(trace: List[JsonError], in: RetractReader): Num =
Num(JsonDecoder.bigDecimal.unsafeDecode(trace, in))

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Num =
json match {
case n @ Num(_) => n
case _ => Lexer.error("Not a number", trace)
case n: Num => n
case _ => Lexer.error("Not a number", trace)
}
}
implicit val encoder: JsonEncoder[Num] = new JsonEncoder[Num] {
def unsafeEncode(a: Num, indent: Option[Int], out: Write): Unit =
JsonEncoder.bigDecimal.unsafeEncode(a.value, indent, out)

override final def toJsonAST(a: Num): Either[String, Num] = Right(a)
override final def toJsonAST(a: Num): Either[String, Num] = new Right(a)
}

implicit val codec: JsonCodec[Num] = JsonCodec(encoder, decoder)
Expand All @@ -557,22 +569,21 @@ object Json {
Null
}

override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Null.type =
json match {
case Null => Null
case _ => Lexer.error("Not null", trace)
}
override final def unsafeFromJsonAST(trace: List[JsonError], json: Json): Null.type = {
if (json ne Null) Lexer.error("Not null", trace)
Null
}
}
implicit val encoder: JsonEncoder[Null.type] = new JsonEncoder[Null.type] {
def unsafeEncode(a: Null.type, indent: Option[Int], out: Write): Unit =
out.write("null")

override final def toJsonAST(a: Null.type): Either[String, Json] = Right(a)
override final def toJsonAST(a: Null.type): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Null.type] = JsonCodec(encoder, decoder)

override def asNull: Some[Unit] = Some(())
override def asNull: Some[Unit] = new Some(())
}

implicit val decoder: JsonDecoder[Json] = new JsonDecoder[Json] {
Expand Down Expand Up @@ -606,7 +617,7 @@ object Json {
case Null => Null.encoder.unsafeEncode(Null, indent, out)
}

override final def toJsonAST(a: Json): Either[String, Json] = Right(a)
override final def toJsonAST(a: Json): Either[String, Json] = new Right(a)
}

implicit val codec: JsonCodec[Json] = JsonCodec(encoder, decoder)
Expand Down

0 comments on commit 84cc739

Please sign in to comment.