summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala
blob: b1707a995f2e6cc9cbdcc24a0f5bab6f1e48f8bd (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
/* NSC -- new Scala compiler
 * Copyright 2005-2006 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: unit = {
    val bytes1 = new Array[byte](bytes.length * 2)
    System.arraycopy(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): unit = {
    if (writeIndex == bytes.length) dble
    bytes(writeIndex) = b.asInstanceOf[byte]
    writeIndex = 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): unit = {
    def writeNatPrefix(x: int): unit = {
      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 at `pos'
   *  If number is more than one byte, shift rest of array to make space.
   */
  def patchNat(pos: int, x: int): unit = {
    def patchNatPrefix(x: int): unit = {
      writeByte(0)
      System.arraycopy(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 in signed big endian format, base 256. */
  def writeLong(x: long): unit = {
    val y = x >> 8
    val z = x & 0xff
    if (-y != (z >> 7)) writeLong(y)
    writeByte(z.asInstanceOf[int])
  }

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

  /** Read a byte */
  def readByte(): int = {
    val x = bytes(readIndex); 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 = i + 1
    }
    val leading = 64 - (len << 3)
    x << leading >> leading
  }

  /** Perform operation `op' until readIndex == end.
   *  Concatenate results into a list.
   */
  def until[T](end: int, op: () => T): List[T] =
    if (readIndex == end) List() else op() :: until(end, op);

  /** Create an index */
  def createIndex: Array[int] = {
    val index = new Array[int](readNat())
    for (val i <- Iterator.range(0, index.length)) {
      index(i) = readIndex
      readByte()
      readIndex = readNat() + readIndex
    }
    index
  }
}