aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala')
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala
new file mode 100644
index 000000000..aa1fd9a90
--- /dev/null
+++ b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala
@@ -0,0 +1,299 @@
+package dotty.tools
+package dotc
+package core
+package unpickleScala2
+
+import Flags._
+
+/** 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)
+ 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): Unit = {
+ if (writeIndex == bytes.length) dble()
+ bytes(writeIndex) = b.toByte
+ 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 =
+ writeLongNat(x.toLong & 0x00000000FFFFFFFFL)
+
+ /**
+ * Like writeNat, but for longs. This is not the same as
+ * writeLong, which writes in base 256. 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 writeNatPrefix(x: Long): Unit = {
+ val y = x >>> 7
+ if (y != 0L) writeNatPrefix(y)
+ writeByte(((x & 0x7f) | 0x80).toInt)
+ }
+ val y = x >>> 7
+ if (y != 0L) writeNatPrefix(y)
+ writeByte((x & 0x7f).toInt)
+ }
+
+ /** 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): Unit = {
+ def patchNatPrefix(x: Int): Unit = {
+ writeByte(0)
+ Array.copy(bytes, pos, bytes, pos + 1, writeIndex - (pos + 1))
+ bytes(pos) = ((x & 0x7f) | 0x80).toByte
+ val y = x >>> 7
+ if (y != 0) patchNatPrefix(y)
+ }
+ bytes(pos) = (x & 0x7f).toByte
+ 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): Unit = {
+ val y = x >> 8
+ val z = x & 0xff
+ if (-y != (z >> 7)) writeLong(y)
+ writeByte(z.toInt)
+ }
+
+ // -- 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 = readLongNat().toInt
+
+ def readLongNat(): Long = {
+ var b = 0L
+ var x = 0L
+ do {
+ b = readByte()
+ x = (x << 7) + (b & 0x7f)
+ } while ((b & 0x80) != 0L)
+ 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
+ }
+
+ /** Returns the buffer as a sequence of (Int, Array[Byte]) representing
+ * (tag, data) of the individual entries. Saves and restores buffer state.
+ */
+
+ def toIndexedSeq: IndexedSeq[(Int, Array[Byte])] = {
+ val saved = readIndex
+ readIndex = 0
+ readNat() ; readNat() // discarding version
+ val result = new Array[(Int, Array[Byte])](readNat())
+
+ result.indices foreach { index =>
+ val tag = readNat()
+ val len = readNat()
+ val bytes = data.slice(readIndex, len + readIndex)
+ readIndex += len
+
+ result(index) = tag -> bytes
+ }
+
+ readIndex = saved
+ result.toIndexedSeq
+ }
+
+ /** 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)
+
+ /** Pickle = majorVersion_Nat minorVersion_Nat nbEntries_Nat {Entry}
+ * Entry = type_Nat length_Nat [actual entries]
+ *
+ * Assumes that the ..Version_Nat are already consumed.
+ *
+ * @return an array mapping entry numbers to locations in
+ * the byte array where the entries start.
+ */
+ def createIndex: Array[Int] = {
+ val index = new Array[Int](readNat()) // nbEntries_Nat
+ for (i <- 0 until index.length) {
+ index(i) = readIndex
+ readByte() // skip type_Nat
+ readIndex = readNat() + readIndex // read length_Nat, jump to next entry
+ }
+ index
+ }
+}
+
+object PickleBuffer {
+
+ private final val ScalaFlagEnd = 48
+ private final val ChunkBits = 8
+ private final val ChunkSize = 1 << ChunkBits
+ private type FlagMap = Array[Array[Long]]
+
+ private val (scalaTermFlagMap, scalaTypeFlagMap) = {
+ import scala.reflect.internal.Flags._
+
+ // The following vals are copy-pasted from reflect.internal.Flags.
+ // They are unfortunately private there, so we cannot get at them directly.
+ // Using the public method pickledToRawFlags instead looks unattractive
+ // because of performance.
+ val IMPLICIT_PKL = (1 << 0)
+ val FINAL_PKL = (1 << 1)
+ val PRIVATE_PKL = (1 << 2)
+ val PROTECTED_PKL = (1 << 3)
+ val SEALED_PKL = (1 << 4)
+ val OVERRIDE_PKL = (1 << 5)
+ val CASE_PKL = (1 << 6)
+ val ABSTRACT_PKL = (1 << 7)
+ val DEFERRED_PKL = (1 << 8)
+ val METHOD_PKL = (1 << 9)
+ val MODULE_PKL = (1 << 10)
+ val INTERFACE_PKL = (1 << 11)
+
+ val corr = Map(
+ PROTECTED_PKL -> Protected,
+ OVERRIDE_PKL -> Override,
+ PRIVATE_PKL -> Private,
+ ABSTRACT_PKL -> Abstract,
+ DEFERRED_PKL -> Deferred,
+ FINAL_PKL -> Final,
+ METHOD_PKL -> Method,
+ INTERFACE_PKL -> PureInterface,
+ MODULE_PKL -> Module,
+ IMPLICIT_PKL -> Implicit,
+ SEALED_PKL -> Sealed,
+ CASE_PKL -> Case,
+ MUTABLE -> Mutable,
+ PARAM -> Param,
+ PACKAGE -> Package,
+ MACRO -> Macro,
+ BYNAMEPARAM -> (Method, Covariant),
+ LABEL -> (Label, Contravariant),
+ ABSOVERRIDE -> AbsOverride,
+ LOCAL -> Local,
+ JAVA -> JavaDefined,
+ SYNTHETIC -> Synthetic,
+ STABLE -> Stable,
+ STATIC -> JavaStatic,
+ CASEACCESSOR -> CaseAccessor,
+ DEFAULTPARAM -> (DefaultParameterized, Trait),
+ BRIDGE -> Bridge,
+ ACCESSOR -> Accessor,
+ SUPERACCESSOR -> SuperAccessor,
+ PARAMACCESSOR -> ParamAccessor,
+ MODULEVAR -> Scala2ModuleVar,
+ LAZY -> Lazy,
+ MIXEDIN -> (MixedIn, Scala2Existential),
+ EXPANDEDNAME -> ExpandedName,
+ IMPLCLASS -> (Scala2PreSuper, ImplClass),
+ SPECIALIZED -> Specialized,
+ VBRIDGE -> VBridge,
+ VARARGS -> JavaVarargs,
+ ENUM -> Enum)
+
+ // generate initial maps from Scala flags to Dotty flags
+ val termMap, typeMap = new Array[Long](64)
+ for (idx <- 0 until ScalaFlagEnd)
+ corr get (1L << idx) match {
+ case Some((termFlag: FlagSet, typeFlag: FlagSet)) =>
+ termMap(idx) |= termFlag.bits
+ typeMap(idx) |= typeFlag.bits
+ case Some(commonFlag: FlagSet) =>
+ termMap(idx) |= commonFlag.toTermFlags.bits
+ typeMap(idx) |= commonFlag.toTypeFlags.bits
+ case _ =>
+ }
+
+ // Convert map so that it maps chunks of ChunkBits size at once
+ // instead of single bits.
+ def chunkMap(xs: Array[Long]): FlagMap = {
+ val chunked = Array.ofDim[Long](
+ (xs.length + ChunkBits - 1) / ChunkBits, ChunkSize)
+ for (i <- 0 until chunked.length)
+ for (j <- 0 until ChunkSize)
+ for (k <- 0 until ChunkBits)
+ if ((j & (1 << k)) != 0)
+ chunked(i)(j) |= xs(i * ChunkBits + k)
+ chunked
+ }
+
+ (chunkMap(termMap), chunkMap(typeMap))
+ }
+
+ def unpickleScalaFlags(sflags: Long, isType: Boolean): FlagSet = {
+ val map: FlagMap = if (isType) scalaTypeFlagMap else scalaTermFlagMap
+ val shift = ChunkBits
+ val mask = ChunkSize - 1
+ assert(6 * ChunkBits == ScalaFlagEnd)
+ FlagSet(
+ map(0)((sflags >>> (shift * 0)).toInt & mask) |
+ map(1)((sflags >>> (shift * 1)).toInt & mask) |
+ map(2)((sflags >>> (shift * 2)).toInt & mask) |
+ map(3)((sflags >>> (shift * 3)).toInt & mask) |
+ map(4)((sflags >>> (shift * 4)).toInt & mask) |
+ map(5)((sflags >>> (shift * 5)).toInt & mask)
+ )
+ }
+}