All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.memcached.protocol.text.server.ServerDecoder.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.finagle.memcached.protocol.text.server

import com.twitter.finagle.memcached.protocol.ClientError
import com.twitter.finagle.memcached.protocol.text._
import com.twitter.finagle.memcached.util.ParserUtils
import com.twitter.io.Buf
import scala.collection.immutable

private[memcached] object ServerDecoder {

  private sealed trait State
  private case object AwaitingCommand extends State
  private case class AwaitingData(tokens: Seq[Buf], bytesNeeded: Int) extends State

  // Constant for the length of a byte array that will contain a String representation of an Int,
  // which is used in the Decoder class when converting a Buf to an Int
  private val MaxLengthOfIntString = Int.MinValue.toString.length
}

/**
 * Decodes Buf-encoded Commands into Decodings. Used by the server.
 *
 * @note Class contains mutable state. Not thread-safe.
 */
private[finagle] class ServerDecoder(storageCommands: immutable.Set[Buf]) extends Decoder {
  import ServerDecoder._

  private[this] var state: State = AwaitingCommand

  def decode(buffer: Buf): Decoding =
    state match {
      case AwaitingCommand =>
        decodeLine(buffer, needsData, awaitData) { tokens =>
          Tokens(tokens)
        }
      case AwaitingData(tokens, bytesNeeded) =>
        decodeData(bytesNeeded, buffer) { data =>
          state = AwaitingCommand

          val commandName = tokens.head
          if (commandName.equals(Buf.Utf8("cas"))) // cas command
            TokensWithData(tokens.slice(0, 5), data, Some(tokens(5)))
          else // other commands
            TokensWithData(tokens, data)
        }
    }

  private[this] def awaitData(tokens: Seq[Buf], bytesNeeded: Int) {
    state = AwaitingData(tokens, bytesNeeded)
  }

  private[this] def needsData(tokens: Seq[Buf]): Int = {
    val commandName = tokens.head
    if (storageCommands.contains(commandName)) {
      validateStorageCommand(tokens)
      val dataLengthAsBuf = tokens(4)
      dataLengthAsBuf.write(byteArrayForBuf2Int, 0)
      ParserUtils.byteArrayStringToInt(byteArrayForBuf2Int, dataLengthAsBuf.length)
    } else -1
  }

  private[this] def validateStorageCommand(tokens: Seq[Buf]) = {
    if (tokens.size < 5) throw new ClientError("Too few arguments")
    if (tokens.size > 6) throw new ClientError("Too many arguments")
    if (!ParserUtils.isDigits(tokens(4))) throw new ClientError("Bad frame length")
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy