summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala
blob: 23dc22e1000a067f7fabd71242b1f955c21c8df9 (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
/* NSC -- new Scala compiler
 * Copyright 2005-2009 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc.symtab.classfile

/** Variable length byte arrays, with methods for basic pickling and unpickling.
 *
 *  @param data The initial buffer
 *  @param from The first index where defined data are found
 *  @param to   The first index where new data can be written
 */
class PickleBuffer(data: Array[Byte], from: Int, to: Int) {

  var bytes = data
  var readIndex = from
  var writeIndex = to

  /** Double bytes array */
  private def dble() {
    val bytes1 = new Array[Byte](bytes.length * 2)
    Array.copy(bytes, 0, bytes1, 0, writeIndex)
    bytes = bytes1
  }

  def ensureCapacity(capacity: Int) =
    while (bytes.length < writeIndex + capacity) dble()

  // -- Basic output routines --------------------------------------------

  /** Write a byte of data */
  def writeByte(b: Int) {
    if (writeIndex == bytes.length) dble()
    bytes(writeIndex) = b.asInstanceOf[Byte]
    writeIndex += 1
  }

  /** Write a natural number in big endian format, base 128.
   *  All but the last digits have bit 0x80 set.
   */
  def writeNat(x: Int) {
    def writeNatPrefix(x: Int) {
      val y = x >>> 7
      if (y != 0) writeNatPrefix(y)
      writeByte((x & 0x7f) | 0x80)
    }
    val y = x >>> 7
    if (y != 0) writeNatPrefix(y)
    writeByte(x & 0x7f)
  }

  /** Write a natural number <code>x</code> at position <code>pos</code>.
   *  If number is more than one byte, shift rest of array to make space.
   *
   *  @param pos ...
   *  @param x   ...
   */
  def patchNat(pos: Int, x: Int) {
    def patchNatPrefix(x: Int) {
      writeByte(0)
      Array.copy(bytes, pos, bytes, pos+1, writeIndex - (pos+1))
      bytes(pos) = ((x & 0x7f) | 0x80).asInstanceOf[Byte]
      val y = x >>> 7
      if (y != 0) patchNatPrefix(y)
    }
    bytes(pos) = (x & 0x7f).asInstanceOf[Byte]
    val y = x >>> 7
    if (y != 0) patchNatPrefix(y)
  }

  /** Write a long number <code>x</code> in signed big endian format, base 256.
   *
   *  @param x The long number to be written.
   */
  def writeLong(x: Long) {
    val y = x >> 8
    val z = x & 0xff
    if (-y != (z >> 7)) writeLong(y)
    writeByte(z.asInstanceOf[Int])
  }

  // -- Basic input routines --------------------------------------------

  /** Peek at the current byte without moving the read index */
  def peekByte(): Int = bytes(readIndex)

  /** Read a byte */
  def readByte(): Int = {
    val x = bytes(readIndex); readIndex += 1; x
  }

  /** Read a natural number in big endian format, base 128.
   *  All but the last digits have bit 0x80 set.*/
  def readNat(): Int = {
    var b = 0
    var x = 0
    do {
      b = readByte()
      x = (x << 7) + (b & 0x7f)
    } while ((b & 0x80) != 0);
    x
  }

  /** Read a long number in signed big endian format, base 256. */
  def readLong(len: Int): Long = {
    var x = 0L
    var i = 0
    while (i < len) {
      x = (x << 8) + (readByte() & 0xff)
      i += 1
    }
    val leading = 64 - (len << 3)
    x << leading >> leading
  }

  /** Perform operation <code>op</code> until the condition
   *  <code>readIndex == end</code> is satisfied.
   *  Concatenate results into a list.
   *
   *  @param end ...
   *  @param op  ...
   *  @return    ...
   */
  def until[T](end: Int, op: () => T): List[T] =
    if (readIndex == end) List() else op() :: until(end, op);

  /** Perform operation <code>op</code> the number of
   *  times specified.  Concatenate the results into a list.
   */
  def times[T](n: Int, op: ()=>T): List[T] =
    if (n == 0) List() else op() :: times(n-1, op)

  /** Create an index.
   *
   *  @return ...
   */
  def createIndex: Array[Int] = {
    val index = new Array[Int](readNat())
    for (i <- 0 until index.length) {
      index(i) = readIndex
      readByte()
      readIndex = readNat() + readIndex
    }
    index
  }
}