Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async engine for ZIO JSON #576

Open
jdegoes opened this issue Feb 17, 2022 · 3 comments
Open

Async engine for ZIO JSON #576

jdegoes opened this issue Feb 17, 2022 · 3 comments

Comments

@jdegoes
Copy link
Member

jdegoes commented Feb 17, 2022

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
Copy link
Member Author

jdegoes commented Feb 17, 2022

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()

                ???
            }
          }
      }
}

@jkobejs
Copy link
Contributor

jkobejs commented Feb 17, 2022

I'll take it

@fsvehla
Copy link
Contributor

fsvehla commented Feb 17, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants