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

fastparse.byte.ByteUtils.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package fastparse.byte
import acyclic.file
import scodec.bits.ByteVector
import fastparse.utils.Utils.IsReachable
import fastparse.core.ParseCtx
import scala.collection.mutable

/**
  * Parsers and parsing-related tools that only really apply to byte parsers,
  * and not FastParse as a whole.
  */
object ByteUtils{
  def prettyBytes(bytes: ByteVector,
                  markers: Seq[Int] = Seq(-1),
                  contextRows: Int = 8) = {
    val invalidIndices = markers.filter(
      x => x < -1 /*-1 is special case*/ || x >= bytes.length
    )
    require(
      invalidIndices.isEmpty,
      s"Cannot show indices $markers outside input bounds 0 -> ${bytes.length}"
    )

    val maxIndexWidth = bytes.length.toString.length
    val gutter = 4
    val output = new StringBuffer
    val emptyGutter = " " * (maxIndexWidth + gutter)
    output.append(emptyGutter)
    output.append(
      0.until(math.min(16, bytes.length.toInt))
        .map(x => x.toString.padTo(2, ' '))
        .mkString(" ")
        .trim
    )
    output.append('\n')

    val sortedIndices = markers.sorted
    val groupedIndices = mutable.Buffer(mutable.Buffer(sortedIndices.head))
    for (index <- sortedIndices.tail){
      if (index / 16 - groupedIndices.last.last / 16 < contextRows){
        groupedIndices.last.append(index)
      }else{
        groupedIndices.append(mutable.Buffer(index))
      }
    }


    if (groupedIndices.head.head / 16 - contextRows > 0) {
      output.append('\n')
      output.append(emptyGutter)
      output.append("...")
    }
    for(grouped <- groupedIndices){
      val startRow = grouped.head / 16 - contextRows
      val endRow = grouped.last / 16 + contextRows
      for{
        i <- startRow to endRow
        sliced = bytes.slice(math.max(0, i * 16), math.max(0, (i + 1) * 16))
        if sliced.nonEmpty
      }{

        val prettyRow = ByteReprOps.prettyPrint(sliced)
        output.append('\n')
        output.append((i * 16).toString.padTo(maxIndexWidth + gutter, ' '))
        output.append(prettyRow)

        val markers = Array.fill(prettyRow.length)(' ')
        var lastMarker = -1
        for (index <- grouped if index / 16 == i && index >= 0){
          lastMarker = (index % 16) * 3
          markers(lastMarker) = '^'
        }
        if (lastMarker != -1) {
          output.append("\n")
          output.append(emptyGutter)
          output.append(new String(markers).take(lastMarker + 1))
        }
      }
      if (endRow < bytes.length / 16) {
        output.append('\n')
        output.append(emptyGutter)
        output.append("...")
      }
    }

    output.toString
  }
  private[this] type Parser[+T] = fastparse.core.Parser[T, Byte, ByteVector]


  class GenericIntegerParser[T](n: Int, creator: (IsReachable[Byte], Int) => T)
                               (implicit name: sourcecode.Name) extends Parser[T]()(ByteReprOps){

    override def toString = name.value

    def parseRec(cfg: ParseCtx[Byte,ByteVector], index: Int) = {
      if (!cfg.input.isReachable(n + index - 1)) fail(cfg.failure, index)
      else success(cfg.success, creator(cfg.input, index), index + n, Set.empty, false)

    }
  }

  protected[this] def inputToByte(input: IsReachable[Byte], n: Int): Byte = input(n)
  /**
    * Parses an 8-bit signed Byte
    */
  val Int8: Parser[Byte] = new GenericIntegerParser(1, inputToByte)
  /**
    * Parses an 8-bit un-signed Byte, stuffed into a Short
    */
  val UInt8: Parser[Short] = new GenericIntegerParser(1, (input, n) =>
    (inputToByte(input, n) & 0xff).toShort
  )


  trait EndianByteParsers {

    protected[this] def inputToShort(input: IsReachable[Byte], n: Int): Short
    protected[this] def inputToInt(input: IsReachable[Byte], n: Int): Int
    protected[this] def inputToLong(input: IsReachable[Byte], n: Int): Long


    /**
      * Parses an 16-bit signed Short
      */
    val Int16: Parser[Short] = new GenericIntegerParser(2, inputToShort)
    /**
      * Parses an 32-bit signed Short
      */
    val Int32: Parser[Int] = new GenericIntegerParser(4, inputToInt)
    /**
      * Parses an 64-bit signed Short
      */
    val Int64: Parser[Long] = new GenericIntegerParser(8, inputToLong)

    /**
      * Parses an 16-bit signed Short, stuffed into an Int
      */
    val UInt16: Parser[Int] = new GenericIntegerParser(2, (input, n) =>
      inputToShort(input, n)  & 0xffff
    )

    /**
      * Parses an 32-bit signed Int, stuffed into a Long
      */
    val UInt32: Parser[Long] = new GenericIntegerParser(4, (input, n) =>
      inputToInt(input, n) & 0xffffffffl
    )

    /**
      * Parses an 32-bit signed Float
      */
    val Float32: Parser[Float] = new GenericIntegerParser(4, (input, n) =>
      java.lang.Float.intBitsToFloat(inputToInt(input, n))
    )

    /**
      * Parses an 32-bit signed Double
      */
    val Float64: Parser[Double] = new GenericIntegerParser(8, (input, n) =>
      java.lang.Double.longBitsToDouble(inputToLong(input, n))
    )
  }
  object EndianByteParsers{
    /**
      * Parsers for parsing 16, 32 and 64 bit integers in little-endian format
      */
    object LE extends EndianByteParsers {
      def inputToLong(input: IsReachable[Byte], n: Int) = {
        ((input(n+7) & 0xffL) << 56) | ((input(n+6) & 0xffL) << 48) |
        ((input(n+5) & 0xffL) << 40) | ((input(n+4) & 0xffL) << 32 ) |
        ((input(n+3) & 0xffL) << 24) | ((input(n+2) & 0xffL) << 16) |
        ((input(n+1) & 0xffL) << 8) | (input(n) & 0xffL)
      }
      def inputToInt(input: IsReachable[Byte], n: Int) = {
        ((input(n+3) & 0xff) << 24) | ((input(n+2) & 0xff) << 16) |
        ((input(n+1) & 0xff) << 8) | (input(n) & 0xff)
      }
      def inputToShort(input: IsReachable[Byte], n: Int) = {
        (((input(n+1) & 0xff) << 8) | (input(n) & 0xff)).toShort
      }
    }

    /**
      * Parsers for parsing 16, 32 and 64 bit integers in big-endian format
      */
    object BE extends EndianByteParsers {
      def inputToLong(input: IsReachable[Byte], n: Int) = {
        ((input(n) & 0xffL) << 56) | ((input(n+1) & 0xffL) << 48) |
        ((input(n+2) & 0xffL) << 40) | ((input(n+3) & 0xffL) << 32 ) |
        ((input(n+4) & 0xffL) << 24) | ((input(n+5) & 0xffL) << 16) |
        ((input(n+6) & 0xffL) << 8) | (input(n+7) & 0xffL)
      }
      def inputToInt(input: IsReachable[Byte], n: Int) = {
        ((input(n) & 0xff) << 24) | ((input(n+1) & 0xff) << 16) |
        ((input(n+2) & 0xff) << 8) | (input(n+3) & 0xff)
      }
      def inputToShort(input: IsReachable[Byte], n: Int) = {
        (((input(n) & 0xff) << 8) | (input(n+1) & 0xff)).toShort
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy