Skip to content

Instantly share code, notes, and snippets.

@phenan
Created May 19, 2017 06:20
Show Gist options
  • Save phenan/9bda0c19599be203d0d2c46cc2d3b737 to your computer and use it in GitHub Desktop.
Save phenan/9bda0c19599be203d0d2c46cc2d3b737 to your computer and use it in GitHub Desktop.
Parser combinators for binary files (especially for Java class file)
import java.io._
import scala.util.Try
class ByteReader private (in: DataInputStream) {
def u1: Int = read(1, in.readUnsignedByte())
def u2: Int = read(2, in.readUnsignedShort())
def s1: Int = read(1, in.readByte().toInt)
def s2: Int = read(2, in.readShort().toInt)
def s4: Int = read(4, in.readInt())
def s8: Long = read(8, in.readLong())
def f4: Float = read(4, in.readFloat())
def f8: Double = read(8, in.readDouble())
def bytes(size: Int): Array[Byte] = {
val bs = new Array[Byte](size)
read(size, in.read(bs))
bs
}
def utf: String = new String(bytes(u2), "UTF-8")
def padding(n: Int): Array[Byte] = {
val rem = (pos % n).toInt
if (rem == 0) Array.empty
else bytes(n - rem)
}
def position: Long = pos
private def close() { in.close() }
private def read[T] (size: Int, reader: => T): T = {
pos += size
reader
}
private var pos: Long = 0
}
object ByteReader {
def openFile[T] (file: String)(p: ByteParser[T]): Try[T] = open(new File(file))(p)
def openString[T] (string: String)(p: ByteParser[T]): Try[T] = open(string.getBytes("UTF-8"))(p)
def open[T] (file: File)(p: ByteParser[T]): Try[T] = {
Try(new FileInputStream(file)).flatMap(in => open(in)(p))
}
def open[T] (bytes: Array[Byte])(p: ByteParser[T]): Try[T] = {
Try(new ByteArrayInputStream(bytes)).flatMap(in => open(in)(p))
}
def open[T] (in: InputStream)(p: ByteParser[T]): Try[T] = for {
reader <- Try(new ByteReader(new DataInputStream(new BufferedInputStream(in))))
ret <- p.perform(reader)
_ <- Try(reader.close())
} yield ret
}
trait ByteParsers {
val u1 = ByteParser(_.u1)
val u2 = ByteParser(_.u2)
val s1 = ByteParser(_.s1)
val s2 = ByteParser(_.s2)
val s4 = ByteParser(_.s4)
val s8 = ByteParser(_.s8)
val f4 = ByteParser(_.f4)
val f8 = ByteParser(_.f8)
val utf = ByteParser(_.utf)
def ret [T] (v: => T): ByteParser[T] = ByteParser.pure(Try(v))
def pure [T] (v: => Try[T]): ByteParser[T] = ByteParser.pure(v)
def failure (e: Exception): ByteParser[Nothing] = ByteParser.failure(e)
def bytes (n: Int) = ByteParser(_.bytes(n))
def padding (n: Int) = ByteParser(_.padding(n))
def list [T] (p: ByteParser[T]): ByteParser[List[T]] = u2 >>= p.rep
def ref [T] (p: ByteParser[T]): ByteParser[T] = ByteParser.ref(p)
def parseFile[T] (file: String)(parser: ByteParser[T]): Try[T] = ByteReader.openFile(file)(parser)
def parseString[T] (string: String)(parser: ByteParser[T]): Try[T] = ByteReader.openString(string)(parser)
def parse[T] (file: File)(parser: ByteParser[T]): Try[T] = ByteReader.open(file)(parser)
def parse[T] (bytes: Array[Byte])(parser: ByteParser[T]): Try[T] = ByteReader.open(bytes)(parser)
def parse[T] (stream: InputStream)(parser: ByteParser[T]): Try[T] = ByteReader.open(stream)(parser)
}
trait ByteParsers {
val u1 = ByteParser(_.u1)
val u2 = ByteParser(_.u2)
val s1 = ByteParser(_.s1)
val s2 = ByteParser(_.s2)
val s4 = ByteParser(_.s4)
val s8 = ByteParser(_.s8)
val f4 = ByteParser(_.f4)
val f8 = ByteParser(_.f8)
val utf = ByteParser(_.utf)
def ret [T] (v: => T): ByteParser[T] = ByteParser.pure(Try(v))
def pure [T] (v: => Try[T]): ByteParser[T] = ByteParser.pure(v)
def failure (e: Exception): ByteParser[Nothing] = ByteParser.failure(e)
def bytes (n: Int) = ByteParser(_.bytes(n))
def padding (n: Int) = ByteParser(_.padding(n))
def list [T] (p: ByteParser[T]): ByteParser[List[T]] = u2 >>= p.rep
def ref [T] (p: ByteParser[T]): ByteParser[T] = ByteParser.ref(p)
def parseFile[T] (file: String)(parser: ByteParser[T]): Try[T] = ByteReader.openFile(file)(parser)
def parseString[T] (string: String)(parser: ByteParser[T]): Try[T] = ByteReader.openString(string)(parser)
def parse[T] (file: File)(parser: ByteParser[T]): Try[T] = ByteReader.open(file)(parser)
def parse[T] (bytes: Array[Byte])(parser: ByteParser[T]): Try[T] = ByteReader.open(bytes)(parser)
def parse[T] (stream: InputStream)(parser: ByteParser[T]): Try[T] = ByteReader.open(stream)(parser)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment