package dotty.tools
package dotc
package core
package tasty
import TastyBuffer._
import TastyName.NameRef
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 <address following length field> + <length-value-read>.
*/
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
}
}