aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala
blob: 13bc95028e2268caabe7136e534020c153339e01 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package dotty.tools
package dotc
package core
package tasty

import util.Util.dble

object TastyBuffer {

  /** The number of digits of the natural number `nat`, written in base 128 format. */
  def natSize(nat: Int): Int =
    if (nat < 128) 1 else natSize(nat >>> 7) + 1

  /** An address pointing to an index in a Tasty buffer's byte array */
  case class Addr(index: Int) extends AnyVal {
    def - (delta: Int): Addr = Addr(this.index - delta)
    def + (delta: Int): Addr = Addr(this.index + delta)

    def relativeTo(base: Addr): Addr = this - base.index - AddrWidth
  }

  val NoAddr = Addr(-1)

  /** The maximal number of address bytes.
   *  Since addresses are written as base-128 natural numbers,
   *  the value of 4 gives a maximal array size of 256M.
   */
  final val AddrWidth = 4
}
import TastyBuffer._

/** 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.
 */
class TastyBuffer(initialSize: Int) {

  /** The current byte array, will be expanded as needed */
  var bytes = new Array[Byte](initialSize)

  /** The number of bytes written */
  var length = 0

  // -- Output routines --------------------------------------------

  /** Write a byte of data. */
  def writeByte(b: Int): Unit = {
    if (length >= bytes.length)
      bytes = dble(bytes)
    bytes(length) = b.toByte
    length += 1
  }

  /** Write the first `n` bytes of `data`. */
  def writeBytes(data: Array[Byte], n: Int): Unit = {
    while (bytes.length < length + n) bytes = dble(bytes)
    Array.copy(data, 0, bytes, length, n)
    length += n
  }

  /** Write a natural number in big endian format, base 128.
   *  All but the last digits have bit 0x80 set.
   */
  def writeNat(x: Int): Unit =
    writeLongNat(x.toLong & 0x00000000FFFFFFFFL)

  /** Write a natural number in 2's complement big endian format, base 128.
   *  All but the last digits have bit 0x80 set.
   */
  def writeInt(x: Int): Unit =
    writeLongInt(x)

  /**
   * Like writeNat, but for longs. Note that the
   * binary representation of LongNat is identical to Nat
   * if the long value is in the range Int.MIN_VALUE to
   * Int.MAX_VALUE.
   */
  def writeLongNat(x: Long): Unit = {
    def writePrefix(x: Long): Unit = {
      val y = x >>> 7
      if (y != 0L) writePrefix(y)
      writeByte((x & 0x7f).toInt)
    }
    val y = x >>> 7
    if (y != 0L) writePrefix(y)
    writeByte(((x & 0x7f) | 0x80).toInt)
  }

  /** Like writeInt, but for longs */
  def writeLongInt(x: Long): Unit = {
    def writePrefix(x: Long): Unit = {
      val y = x >> 7
      if (y != 0L - ((x >> 6) & 1)) writePrefix(y)
      writeByte((x & 0x7f).toInt)
    }
    val y = x >> 7
    if (y != 0L - ((x >> 6) & 1)) writePrefix(y)
    writeByte(((x & 0x7f) | 0x80).toInt)
  }

  /** Write an uncompressed Long stored in 8 bytes in big endian format */
  def writeUncompressedLong(x: Long): Unit = {
    var y = x
    val bytes = new Array[Byte](8)
    for (i <- 7 to 0 by -1) {
      bytes(i) = (y & 0xff).toByte
      y = y >>> 8
    }
    writeBytes(bytes, 8)
  }

  // -- Address handling --------------------------------------------

  /** Write natural number `x` right-adjusted in a field of `width` bytes
   *  starting with address `at`.
   */
  def putNat(at: Addr, x: Int, width: Int): Unit = {
    var y = x
    var w = width
    if(at.index + w >= bytes.length)
      bytes = dble(bytes)
    var digit = y & 0x7f | 0x80
    while (w > 0) {
      w -= 1
      bytes(at.index + w) = digit.toByte
      y >>>= 7
      digit = y & 0x7f
    }
    assert(y == 0, s"number $x too large to fit in $width bytes")
  }

  /** The byte at given address */
  def getByte(at: Addr): Int = bytes(at.index)

  /** The natural number at address `at` */
  def getNat(at: Addr): Int = getLongNat(at).toInt

  /** The long natural number at address `at` */
  def getLongNat(at: Addr): Long = {
    var b = 0L
    var x = 0L
    var idx = at.index
    do {
      b = bytes(idx)
      x = (x << 7) | (b & 0x7f)
      idx += 1
    } while ((b & 0x80) == 0)
    x
  }

  /** The address (represented as a natural number) at address `at` */
  def getAddr(at: Addr) = Addr(getNat(at))

  /** The smallest address equal to or following `at` which points to a non-zero byte */
  final def skipZeroes(at: Addr): Addr =
    if (getByte(at) != 0) at else skipZeroes(at + 1)

  /** The address after the natural number found at address `at`. */
  final def skipNat(at: Addr): Addr = {
    val next = at + 1
    if ((getByte(at) & 0x80) != 0) next else skipNat(next)
  }

  /** The address referring to the end of data written so far */
  def currentAddr: Addr = Addr(length)

  /** Reserve `AddrWidth` bytes to write an address into */
  def reserveAddr(): Addr = {
    val result = currentAddr
    length += AddrWidth
    result
  }

  /** Fill reserved space at address `at` with address `target` */
  def fillAddr(at: Addr, target: Addr) =
    putNat(at, target.index, AddrWidth)

  /** Write address without leading zeroes */
  def writeAddr(addr: Addr): Unit = writeNat(addr.index)

  // -- Finalization --------------------------------------------

  /** Hook to be overridden in subclasses.
   *  Perform all actions necessary to assemble the final byte array.
   *  After `assemble` no more output actions to this buffer are permitted.
   */
  def assemble(): Unit = ()
}