package dotty.tools package dotc package core package tasty import TastyBuffer._ import collection.mutable /** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, * and that supports reading and patching addresses represented as natural numbers. * * @param bytes The array containing data * @param start The position from which to read * @param end The position one greater than the last byte to be read * @param base The index referenced by the logical zero address Addr(0) */ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = 0) { def this(bytes: Array[Byte]) = this(bytes, 0, bytes.length) private var bp: Int = start def addr(idx: Int) = Addr(idx - base) def index(addr: Addr) = addr.index + base /** The address of the first byte to read, respectively byte that was read */ def startAddr: Addr = addr(start) /** The address of the next byte to read */ def currentAddr: Addr = addr(bp) /** the address one greater than the last brte to read */ def endAddr: Addr = addr(end) /** Have all bytes been read? */ def isAtEnd: Boolean = bp == end /** A new reader over the same array with the same address base, but with * specified start and end positions */ def subReader(start: Addr, end: Addr): TastyReader = new TastyReader(bytes, index(start), index(end), base) /** Read a byte of data. */ def readByte(): Int = { val result = bytes(bp) & 0xff bp += 1 result } /** Returns the next byte of data as a natural number without advancing the read position */ def nextByte: Int = bytes(bp) & 0xff /** Read the next `n` bytes of `data`. */ def readBytes(n: Int): Array[Byte] = { val result = new Array[Byte](n) Array.copy(bytes, bp, result, 0, n) bp += n result } /** Read a natural number fitting in an Int in big endian format, base 128. * All but the last digits have bit 0x80 set. */ def readNat(): Int = readLongNat.toInt /** Read an integer number in 2's complement big endian format, base 128. * All but the last digits have bit 0x80 set. */ def readInt(): Int = readLongInt.toInt /** Read a natural number fitting in a Long in big endian format, base 128. * All but the last digits have bit 0x80 set. */ def readLongNat(): Long = { var b = 0L var x = 0L do { b = bytes(bp) x = (x << 7) | (b & 0x7f) bp += 1 } while ((b & 0x80) == 0) x } /** Read a long integer number in 2's complement big endian format, base 128. */ def readLongInt(): Long = { var b = bytes(bp) var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6. bp += 1 while ((b & 0x80) == 0) { b = bytes(bp) x = (x << 7) | (b & 0x7f) bp += 1 } x } /** Read an uncompressed Long stored in 8 bytes in big endian format */ def readUncompressedLong(): Long = { var x: Long = 0 for (i <- 0 to 7) x = (x << 8) | (readByte() & 0xff) x } /** Read a natural number and return as a NameRef */ def readNameRef() = NameRef(readNat()) /** Read a natural number and return as an address */ def readAddr() = Addr(readNat()) /** Read a length number and return the absolute end address implied by it, * given as
+ . */ def readEnd(): Addr = addr(readNat() + bp) /** Set read position to the one pointed to by `addr` */ def goto(addr: Addr): Unit = bp = index(addr) /** Perform `op` until `end` address is reached and collect results in a list. */ def until[T](end: Addr)(op: => T): List[T] = { val buf = new mutable.ListBuffer[T] while (bp < index(end)) buf += op assert(bp == index(end)) buf.toList } /** If before given `end` address, the result of `op`, otherwise `default` */ def ifBefore[T](end: Addr)(op: => T, default: T): T = if (bp < index(end)) op else default /** Perform `op` while cindition `cond` holds and collect results in a list. */ def collectWhile[T](cond: => Boolean)(op: => T): List[T] = { val buf = new mutable.ListBuffer[T] while (cond) buf += op buf.toList } }