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

eu.joaocosta.minart.internal.ByteReader.scala Maven / Gradle / Ivy

package eu.joaocosta.minart.internal

import java.io.InputStream

/** Helper methods to read binary data from an input stream.
  */
private[minart] trait ByteReader[ByteSeq] {
  type ParseResult[T]   = Either[String, (ByteSeq, T)]
  type ParseState[E, T] = State[ByteSeq, E, T]

  /** Generates a sequence of bytes from a byte stream */
  def fromInputStream(is: InputStream): ByteSeq

  /** Checks if a byte sequence is empty */
  def isEmpty(seq: ByteSeq): Boolean

  /** Skip N Bytes */
  def skipBytes(n: Int): ParseState[Nothing, Unit]

  /** Read 1 Byte */
  val readByte: ParseState[String, Option[Int]]

  /** Read N Bytes */
  def readBytes(n: Int): ParseState[Nothing, Array[Int]]

  /** Read N Bytes */
  def readRawBytes(n: Int): ParseState[Nothing, Array[Byte]]

  /** Reads data while a predicate is true */
  def readWhile(p: Int => Boolean): ParseState[Nothing, List[Int]]

  /** Does nothing */
  val noop: ParseState[Nothing, Unit] = skipBytes(0)

  /** Read a String from N Bytes */
  def readString(n: Int): ParseState[Nothing, String] =
    readBytes(n).map { bytes => bytes.map(_.toChar).mkString("") }

  /** Read a Integer N Bytes (Little Endian) */
  def readLENumber(n: Int): ParseState[Nothing, Int] = readBytes(n).map { bytes =>
    bytes.zipWithIndex.map { case (num, idx) => num.toInt << (idx * 8) }.sum
  }

  /** Read a Integer N Bytes as a Long (Little Endian) */
  def readLENumberLong(n: Int): ParseState[Nothing, Long] = readBytes(n).map { bytes =>
    bytes.zipWithIndex.map { case (num, idx) => num.toLong << (idx * 8) }.sum
  }

  /** Read a Integer N Bytes (Big Endian) */
  def readBENumber(n: Int): ParseState[Nothing, Int] = readBytes(n).map { bytes =>
    bytes.reverse.zipWithIndex.map { case (num, idx) => num.toInt << (idx * 8) }.sum
  }

  /** Read a Integer N Bytes as a Long (Big Endian) */
  def readBENumberLong(n: Int): ParseState[Nothing, Long] = readBytes(n).map { bytes =>
    bytes.reverse.zipWithIndex.map { case (num, idx) => num.toLong << (idx * 8) }.sum
  }
}

private[minart] object ByteReader {

  class CustomInputStream(inner: InputStream) extends InputStream {
    var hasBuffer: Boolean                  = false
    var buffer: Int                         = 0
    override def available(): Int           = inner.available() + (if (hasBuffer) 1 else 0)
    override def close(): Unit              = inner.close()
    override def mark(readLimit: Int): Unit = ()
    override def markSupported(): Boolean   = false
    override def read() = {
      if (!hasBuffer) inner.read()
      else {
        hasBuffer = false
        buffer
      }
    }
    override def read(b: Array[Byte]): Int = {
      if (!hasBuffer || b.isEmpty) inner.read(b)
      else {
        hasBuffer = false
        b(0) = buffer.toByte
        inner.read(b, 1, b.size - 1)
      }
    }
    override def reset(): Unit = ()
    override def skip(n: Long): Long = {
      if (!hasBuffer || n == 0) inner.skip(n)
      else {
        hasBuffer = false
        inner.skip(n - 1) + 1
      }
    }

    // Extra methods
    def isEmpty(): Boolean =
      if (!hasBuffer) {
        val next = inner.read()
        if (next == -1) true
        else {
          buffer = next
          hasBuffer = true
          false
        }
      } else false

    def setBuffer(value: Int): Unit = {
      buffer = value
      hasBuffer = true
    }

  }

  object InputStreamByteReader extends ByteReader[CustomInputStream] {
    def fromInputStream(is: InputStream): CustomInputStream = new CustomInputStream(is)

    def isEmpty(seq: CustomInputStream): Boolean = seq.isEmpty()

    def skipBytes(n: Int): ParseState[Nothing, Unit] =
      State.modify { bytes =>
        bytes.skip(n)
        bytes
      }

    val readByte: ParseState[String, Option[Int]] = State { bytes =>
      val value = bytes.read()
      bytes -> (if (value >= -1) Some(value) else None)
    }

    def readBytes(n: Int): ParseState[Nothing, Array[Int]] = State { bytes =>
      val byteArr    = Array.ofDim[Byte](n)
      val resultSize = bytes.read(byteArr)
      bytes -> byteArr.map(b => java.lang.Byte.toUnsignedInt(b))
    }

    def readRawBytes(n: Int): ParseState[Nothing, Array[Byte]] = State { bytes =>
      val byteArr    = Array.ofDim[Byte](n)
      val resultSize = bytes.read(byteArr)
      bytes -> byteArr
    }

    def readWhile(p: Int => Boolean): ParseState[Nothing, List[Int]] = State { bytes =>
      val buffer = List.newBuilder[Int]
      var value  = bytes.read()
      while (value != -1 && p(value)) {
        buffer += value
        value = bytes.read()
      }
      if (value != -1) bytes.setBuffer(value)
      bytes -> buffer.result()
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy