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

com.twitter.finagle.util.BufReader.scala Maven / Gradle / Ivy

There is a newer version: 7.0.0
Show newest version
package com.twitter.finagle.util

import com.twitter.io.Buf
import java.lang.{Double => JDouble, Float => JFloat}

/**
 * A [[BufReader]] provides a stateful API to extract bytes from a
 * [[Buf]]. This conveniently allows codec implementations to
 * decode frames, specifically when they need to decode and interpret
 * the bytes as a numeric value. Unless otherwise stated, [[BufReader]]
 * implementations are not thread safe.
 */
private[finagle] trait BufReader {
  /**
   * The remainder of bytes that the reader is capable of reading.
   */
  def remaining: Int

  /**
   * Extract 8 bits and interpret as a signed integer, advancing the byte cursor by 1.
   */
  def readByte(): Byte

  /**
   * Extract 8 bits and interpret as an unsigned integer, advancing the byte cursor by 1.
   */
  def readUnsignedByte(): Short

  /**
   * Extract 16 bits and interpret as a big endian integer, advancing the byte cursor by 2.
   */
  def readShortBE(): Short

  /**
   * Extract 16 bits and interpret as a little endian integer, advancing the byte cursor by 2.
   */
  def readShortLE(): Short

  /**
   * Extract 16 bits and interpret as a big endian unsigned integer, advancing the byte cursor by 2.
   */
  def readUnsignedShortBE(): Int

  /**
   * Extract 16 bits and interpret as a little endian unsigned integer, advancing the byte cursor by 2.
   */
  def readUnsignedShortLE(): Int

  /**
   * Extract 24 bits and interpret as a big endian integer, advancing the byte cursor by 3.
   */
  def readMediumBE(): Int

  /**
   * Extract 24 bits and interpret as a little endian integer, advancing the byte cursor by 3.
   */
  def readMediumLE(): Int

  /**
   * Extract 24 bits and interpret as a big endian unsigned integer, advancing the byte cursor by 3.
   */
  def readUnsignedMediumBE(): Int

  /**
   * Extract 24 bits and interpret as a little endian unsigned integer, advancing the byte cursor by 3.
   */
  def readUnsignedMediumLE(): Int

  /**
   * Extract 32 bits and interpret as a big endian integer, advancing the byte cursor by 4.
   */
  def readIntBE(): Int

  /**
   * Extract 32 bits and interpret as a little endian int, advancing the byte cursor by 4.
   */
  def readIntLE(): Int

  /**
   * Extract 32 bits and interpret as a big endian unsigned integer, advancing the byte cursor by 4.
   */
  def readUnsignedIntBE(): Long

  /**
   * Extract 32 bits and interpret as a little endian unsigned integer, advancing the byte cursor by 4.
   */
  def readUnsignedIntLE(): Long

  /**
   * Extract 64 bits and interpret as a big endian integer, advancing the byte cursor by 8.
   */
  def readLongBE(): Long

  /**
   * Extract 64 bits and interpret as a little endian integer, advancing the byte cursor by 8.
   */
  def readLongLE(): Long

  /**
   * Extract 32 bits and interpret as a big endian floating point, advancing the byte cursor by 4.
   */
  def readFloatBE(): Float

  /**
   * Extract 32 bits and interpret as a little endian floating point, advancing the byte cursor by 4.
   */
  def readFloatLE(): Float

  /**
   * Extract 64 bits and interpret as a big endian floating point, advancing the byte cursor by 4.
   */
  def readDoubleBE(): Double

  /**
   * Extract 64 bits and interpret as a little endian floating point, advancing the byte cursor by 4.
   */
  def readDoubleLE(): Double


  /**
   * Returns a new buffer representing a slice of this buffer, delimited
   * by the indices `[cursor, remaining)`. Out of bounds indices are truncated.
   * Negative indices are not accepted.
   */
  def readBytes(n: Int): Buf


  /**
   * Skip over the next `n` bytes.
   *
   * @throws UnderflowException if there are < `n` bytes available
   */
  def skip(n: Int): Unit

  /**
   * Like `read`, but extracts the remainder of bytes from cursor
   * to the length. Note, this advances the cursor to the end of
   * the buf.
   */
  def readAll(): Buf
}

private[finagle] trait ProxyBufReader extends BufReader {
  protected def reader: BufReader

  def remaining: Int = reader.remaining

  def readByte(): Byte = reader.readByte()

  def readUnsignedByte(): Short = reader.readUnsignedByte()

  def readShortBE(): Short = reader.readShortBE()

  def readShortLE(): Short = reader.readShortLE()

  def readUnsignedShortBE(): Int = reader.readUnsignedShortBE()

  def readUnsignedShortLE(): Int = reader.readUnsignedShortLE()

  def readMediumBE(): Int = reader.readMediumBE()

  def readMediumLE(): Int = reader.readMediumLE()

  def readUnsignedMediumBE(): Int = reader.readUnsignedMediumBE()

  def readUnsignedMediumLE(): Int = reader.readUnsignedMediumLE()

  def readIntBE(): Int = reader.readIntBE()

  def readIntLE(): Int = reader.readIntLE()

  def readUnsignedIntBE(): Long = reader.readUnsignedIntBE()

  def readUnsignedIntLE(): Long = reader.readUnsignedIntLE()

  def readLongBE(): Long = reader.readLongBE()

  def readLongLE(): Long = reader.readLongLE()

  def readFloatBE(): Float = reader.readFloatBE()

  def readFloatLE(): Float = reader.readFloatLE()

  def readDoubleBE(): Double = reader.readDoubleBE()

  def readDoubleLE(): Double = reader.readDoubleLE()

  def readBytes(n: Int): Buf = reader.readBytes(n)

  def skip(n: Int): Unit = reader.skip(n)

  def readAll(): Buf = reader.readAll()
}

private[finagle] object BufReader {
  /**
   * Creates a [[BufReader]] that is capable of extracting
   * numeric values in big endian order.
   */
  def apply(buf: Buf): BufReader = new BufReaderImpl(buf)

  /**
   * Indicates there aren't sufficient bytes to be read.
   */
  class UnderflowException(msg: String) extends Exception(msg)

  /**
   * The max value of a signed 24 bit "medium" integer
   */
  private[util] val SignedMediumMax = 0x800000
}

private class BufReaderImpl(underlying: Buf) extends BufReader {
  import BufReader._

  // stores a reference to the remainder `underlying`.
  private[this] var buf: Buf = underlying

  // temporarily stores numeric values which can be extracted
  // from `buf` via the `read*` methods.
  private[this] val nums = new Array[Byte](8)

  def remaining: Int = buf.length

  def readByte(): Byte = {
    if (remaining < 1) {
      throw new UnderflowException(
        s"tried to read a byte when remaining bytes was ${remaining}")
    }

    readBytes(1).write(nums, 0)
    nums(0)
  }

  def readUnsignedByte(): Short = (readByte() & 0xff).toShort

  // - Short -
  def readShortBE(): Short = {
    if (remaining < 2) {
      throw new UnderflowException(
        s"tried to read 2 bytes when remaining bytes was ${remaining}")
    }

    readBytes(2).write(nums, 0)
    (((nums(0) & 0xff) <<  8) |
    ((nums(1) & 0xff)      )).toShort
  }

  def readShortLE(): Short = {
    if (remaining < 2) {
      throw new UnderflowException(
        s"tried to read 2 bytes when remaining bytes was ${remaining}")
    }

    readBytes(2).write(nums, 0)
    (((nums(0) & 0xff)      ) |
    ((nums(1) & 0xff) <<  8)).toShort
  }

  def readUnsignedShortBE(): Int = readShortBE() & 0xffff

  def readUnsignedShortLE(): Int = readShortLE() & 0xffff

  // - Medium -
  def readMediumBE(): Int = {
    val unsigned = readUnsignedMediumBE()
    if (unsigned > SignedMediumMax) {
      unsigned | 0xff000000
    } else {
      unsigned
    }
  }

  def readMediumLE(): Int = {
    val unsigned = readUnsignedMediumLE()
    if (unsigned > SignedMediumMax) {
      unsigned | 0xff000000
    } else {
      unsigned
    }
  }

  def readUnsignedMediumBE(): Int = {
    if (remaining < 3) {
      throw new UnderflowException(
        s"tried to read 3 bytes when remaining bytes was ${remaining}")
    }

    readBytes(3).write(nums, 0)
    ((nums(0) & 0xff) << 16) |
    ((nums(1) & 0xff) <<  8) |
    ((nums(2) & 0xff)      )
  }

  def readUnsignedMediumLE(): Int = {
    if (remaining < 3) {
      throw new UnderflowException(
        s"tried to read 3 bytes when remaining bytes was ${remaining}")
    }

    readBytes(3).write(nums, 0)
    ((nums(0) & 0xff)      ) |
    ((nums(1) & 0xff) <<  8) |
    ((nums(2) & 0xff) << 16)
  }

  // - Int -
  def readIntBE(): Int = {
    if (remaining < 4) {
      throw new UnderflowException(
        s"tried to read 4 bytes when remaining bytes was ${remaining}")
    }

    readBytes(4).write(nums, 0)
    ((nums(0) & 0xff) << 24) |
    ((nums(1) & 0xff) << 16) |
    ((nums(2) & 0xff) <<  8) |
    ((nums(3) & 0xff)      )
  }

  def readIntLE(): Int = {
    if (remaining < 4) {
      throw new UnderflowException(
        s"tried to read 4 bytes when remaining bytes was ${remaining}")
    }

    readBytes(4).write(nums, 0)
    ((nums(0) & 0xff)      ) |
    ((nums(1) & 0xff) <<  8) |
    ((nums(2) & 0xff) << 16) |
    ((nums(3) & 0xff) << 24)
  }

  def readUnsignedIntBE(): Long = readIntBE() & 0xffffffffL

  def readUnsignedIntLE(): Long = readIntLE() & 0xffffffffL

  // - Long -
  def readLongBE(): Long = {
    if (remaining < 8) {
      throw new UnderflowException(
        s"tried to read 8 bytes when remaining bytes was ${remaining}")
    }

    readBytes(8).write(nums, 0)
    ((nums(0) & 0xff).toLong << 56) |
    ((nums(1) & 0xff).toLong << 48) |
    ((nums(2) & 0xff).toLong << 40) |
    ((nums(3) & 0xff).toLong << 32) |
    ((nums(4) & 0xff).toLong << 24) |
    ((nums(5) & 0xff).toLong << 16) |
    ((nums(6) & 0xff).toLong <<  8) |
    ((nums(7) & 0xff).toLong      )
  }

  def readLongLE(): Long = {
    if (remaining < 8) {
      throw new UnderflowException(
        s"tried to read 8 bytes when remaining bytes was ${remaining}")
    }

    readBytes(8).write(nums, 0)
    ((nums(0) & 0xff).toLong      ) |
    ((nums(1) & 0xff).toLong <<  8) |
    ((nums(2) & 0xff).toLong << 16) |
    ((nums(3) & 0xff).toLong << 24) |
    ((nums(4) & 0xff).toLong << 32) |
    ((nums(5) & 0xff).toLong << 40) |
    ((nums(6) & 0xff).toLong << 48) |
    ((nums(7) & 0xff).toLong << 56)
  }

  // - Floating Point -
  def readFloatBE(): Float = JFloat.intBitsToFloat(readIntBE())

  def readDoubleBE(): Double = JDouble.longBitsToDouble(readLongBE())

  def readFloatLE(): Float = JFloat.intBitsToFloat(readIntLE())

  def readDoubleLE(): Double = JDouble.longBitsToDouble(readLongLE())

  def readBytes(n: Int): Buf = {
    val b = buf.slice(0, n)
    buf = buf.slice(n, buf.length)
    b
  }

  def skip(n: Int): Unit = {
    if (n < 0) {
      throw new IllegalArgumentException(s"Invalid number of bytes to skip: $n. Must be >= 0")
    }
    if (remaining < n) {
      throw new UnderflowException(
        s"Tried to skip $n bytes when remaining bytes was $remaining")
    }
    if (n > 0) {
      buf = buf.slice(n, buf.length)
    }
  }

  def readAll(): Buf = {
    val b = buf.slice(0, buf.length)
    buf = Buf.Empty
    b
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy