zio-json icon indicating copy to clipboard operation
zio-json copied to clipboard

Async engine for ZIO JSON

Open jdegoes opened this issue 3 years ago • 3 comments

Given the Loom is still a ways out, it can make sense to investigate an async engine for JSON to see if we can achieve performance in the ballpark of the sync engine in the happy path.

Such an async engine would be push-based rather than _pull-based. This means that instead of pulling new characters from a Reader, it would be pushed characters a block at a time.

A push-based engine could adopt parallel parsing. Parallel parsing is required for fallback, but also for parsing multiple fields at the same time.

TODO

jdegoes avatar Feb 17 '22 10:02 jdegoes


class Registers(registers: Array[Any])

trait AsyncDecoding[+Value] {
  /**
   * Resets all state of the decoding to the initial values, such that the 
   * decoding is equivalent to a new one. This method can be used by single-fibered
   * code in order to accelerate performance and avoid re-creating decodings for
   * the same type of data.
   */
  def reset(): Unit 

  /**
   * Returns the number of characters consumed. If this is equal to the length of the 
   * char sequence, then the full length was consumed, but no more data is desired,
   * because the decoding processs is finished. On the other hand, if this is greater 
   * than the length of the char sequence, then the full length was consumed, and 
   * more data is desired. If this is less than the length of the char sequence, then 
   * no more data is required, and the decoding stopped short of consuming all characters 
   * in the char sequence. If there is an error, then an `AsyncDecodingError` is thrown.
   */
  def feed(chars: CharSequence): Int
}

final case class AsyncDecodingError(message: String, path: List[String] = Nil) extends Exception(message) with NoStackTrace

trait AsyncDecoder[+Value] {
  def unsafeNewDecoding(index: Int, registers: Registers): AsyncDecoding[Value]
}
object AsyncDecoder {
  val boolean: AsyncDecoder[Boolean] = 
    (index: Int, registers: Registers) => 
      new AsyncDecoding[Boolean] {
        var state: Int = 0 

        val BranchMask   = 0x3
        val BranchBitLen = 2 

        def branch(): Int = (state & BranchMask)
        def position(): Int = (state >> BranchBitLen)

        val Undecided = 1 
        val IsTrue    = 2 
        val IsFalse   = 3

        def setBranch(branch: Int): Unit = state = (state & ~BranchMask) | branch
        def setPosition(int: Int): Unit = state = (state & BranchMask) | (int << BranchBitLen)
        def setBranchAndPosition(branch: Int, pos: Int): Unit = state = branch | (pos << BranchBitLen)

        // 't' 'r' 'u' 'e'
        // 'f' 'a' 'l' 's' 'e'
        def reset(): Unit = state = 0

        def feed(chars: CharSequence): Int =
          if (chars.length == 0) ???
          else {
            branch() match {
              case Undecided => 
                chars.charAt(0) match {
                  case 't' => 
                    // TODO: Do NOT modify the state if we can read it all at once (the happy path!)
                    // TODO: Read all the chars, or pause when we get as far as we can:
                    setBranchAndPosition(IsTrue, 1)

                    ???
                  case 'f' => 
                    // TODO: Do NOT modify the state if we can read it all at once (the happy path!)
                    // TODO: Read all the chars, or pause when we get as far as we can:
                    setBranchAndPosition(IsFalse, 1)

                    ???
                  case _ => throw AsyncDecodingError("""Expected "true" or "false" boolean literal""")
                }
              case IsTrue => 
                val curPos = position()

                if (curPos == 4) registers.registers(index) = true 

                ???
              case IsFalse => 
                val curPos = position()

                ???
            }
          }
      }
}

jdegoes avatar Feb 17 '22 10:02 jdegoes

I'll take it

jkobejs avatar Feb 17 '22 11:02 jkobejs

@jkobejs Awesome! Please make sure to target the zio2 branch.

fsvehla avatar Feb 17 '22 12:02 fsvehla