aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/tasty/TastyReader.scala
blob: e583c47939eaaa84d7ffe5ffd617549204459eb5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
  }
}