diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7e0867f087eb..562c905e81e4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -7157,7 +7157,7 @@ object Types extends TypeUtils { object VarianceMap: /** An immutable map representing the variance of keys of type `K` */ - opaque type VarianceMap[K <: AnyRef] <: AnyRef = SimpleIdentityMap[K, Integer] + opaque type VarianceMap[K <: AnyRef] = SimpleIdentityMap[K, Integer] def empty[K <: AnyRef]: VarianceMap[K] = SimpleIdentityMap.empty[K] extension [K <: AnyRef](vmap: VarianceMap[K]) /** The backing map used to implement this VarianceMap. */ diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 998cb0b945e4..6a5640df21aa 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -1220,7 +1220,7 @@ class Inliner(val call: tpd.Tree)(using Context): // inlining.println(i"drop unused $bindings%, % in $tree") val (termBindings, typeBindings) = bindings.partition(_.symbol.isTerm) if (typeBindings.nonEmpty) { - val typeBindingsSet = typeBindings.foldLeft[SimpleIdentitySet[Symbol]](SimpleIdentitySet.empty)(_ + _.symbol) + val typeBindingsSet = SimpleIdentitySet(typeBindings.iterator.map(_.symbol)) val inlineTypeBindings = new TreeTypeMap( typeMap = new TypeMap() { override def apply(tp: Type): Type = tp match { diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index f28532d564eb..4874d7b12a82 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -590,7 +590,7 @@ object Inferencing { constraint.upper(param).foreach(p => traverse(constraint.typeVarOfParam(p))) case _ => } - if (vmap1 eq vmap) vmap else propagate(vmap1) + if (vmap1 == vmap) vmap else propagate(vmap1) } propagate(accu(accu(VarianceMap.empty, tp), pt.finalResultType)) @@ -669,7 +669,7 @@ trait Inferencing { this: Typer => // `qualifying`. val ownedVars = state.ownedVars - if (ownedVars ne locked) && !ownedVars.isEmpty then + if (ownedVars != locked) && !ownedVars.isEmpty then val qualifying = (ownedVars -- locked).toList if (!qualifying.isEmpty) { typr.println(i"interpolate $tree: ${tree.tpe.widen} in $state, pt = $pt, owned vars = ${state.ownedVars.toList}%, %, qualifying = ${qualifying.toList}%, %, previous = ${locked.toList}%, % / ${state.constraint}") @@ -748,7 +748,7 @@ trait Inferencing { this: Typer => end toInstantiate def typeVarsIn(xs: ToInstantiate): TypeVars = - xs.foldLeft(SimpleIdentitySet.empty: TypeVars)((tvs, tvi) => tvs + tvi._1) + SimpleIdentitySet(xs.iterator.map(_._1)) /** Filter list of proposed instantiations so that they don't constrain further * the current constraint. diff --git a/compiler/src/dotty/tools/dotc/util/SimpleIdentityMap.scala b/compiler/src/dotty/tools/dotc/util/SimpleIdentityMap.scala index 2f202bc05921..8bd1575c1d2a 100644 --- a/compiler/src/dotty/tools/dotc/util/SimpleIdentityMap.scala +++ b/compiler/src/dotty/tools/dotc/util/SimpleIdentityMap.scala @@ -1,246 +1,139 @@ package dotty.tools.dotc.util -import collection.mutable.ListBuffer +import scala.collection.mutable.ListBuffer /** A simple linked map with `eq` as the key comparison, optimized for small maps. - * It has linear complexity for `apply`, `updated`, and `remove`. + * It has linear complexity for `apply`, `updated`, and `remove`. */ -abstract class SimpleIdentityMap[K <: AnyRef, +V <: AnyRef] extends (K => V | Null) { - final def isEmpty: Boolean = this eq SimpleIdentityMap.myEmpty - def size: Int - def apply(k: K): V | Null - def remove(k: K): SimpleIdentityMap[K, V] - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V1] - def contains(k: K): Boolean = apply(k) != null - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] - def foreachBinding(f: (K, V) => Unit): Unit - def forallBinding(f: (K, V) => Boolean): Boolean - def map2[T](f: (K, V) => T): List[T] = { - val buf = new ListBuffer[T] - foreachBinding((k, v) => buf += f(k, v)) - buf.toList - } - def keys: List[K] = map2((k, v) => k) - def toList: List[(K, V)] = map2((k, v) => (k, v)) - override def toString: String = { - def assocToString(key: K, value: V) = s"$key -> $value" - map2(assocToString) mkString ("(", ", ", ")") - } -} +final class SimpleIdentityMap[K <: AnyRef, +V <: AnyRef](bindings: Array[AnyRef]) extends AnyVal { + private def key(i: Int): K = bindings(i).asInstanceOf[K] -object SimpleIdentityMap { + private def value(i: Int): V = bindings(i + 1).asInstanceOf[V] - private val CompactifyThreshold = 4 + def isEmpty: Boolean = + bindings.length == 0 - private object myEmpty extends SimpleIdentityMap[AnyRef, Nothing] { - def size = 0 - def apply(k: AnyRef) = null - def remove(k: AnyRef) = this - def updated[V1 <: AnyRef](k: AnyRef, v: V1) = new Map1(k, v) - def mapValuesNow[V1 <: AnyRef](f: (AnyRef, V1) => V1) = this - def foreachBinding(f: (AnyRef, Nothing) => Unit) = () - def forallBinding(f: (AnyRef, Nothing) => Boolean) = true - } + def size: Int = bindings.length / 2 + + //Stats.record(s"SimpleIdentityMap/$size") - def empty[K <: AnyRef]: SimpleIdentityMap[K, Nothing] = myEmpty.asInstanceOf[SimpleIdentityMap[K, Nothing]] - - class Map1[K <: AnyRef, +V <: AnyRef] (k1: K, v1: V) extends SimpleIdentityMap[K, V] { - def size: Int = 1 - def apply(k: K): V | Null = - if (k eq k1) v1 - else null - def remove(k: K): SimpleIdentityMap[K, V] = - if (k eq k1) empty - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V1] = - if (k eq k1) new Map1(k, v) - else new Map2(k1, v1, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { - val w1 = f(k1, v1) - if (v1 eq w1) this else new Map1(k1, w1) + def apply(k: K): V | Null = { + var i = 0 + while (i < bindings.length) { + if (bindings(i) eq k) return value(i) + i += 2 } - def foreachBinding(f: (K, V) => Unit): Unit = f(k1, v1) - def forallBinding(f: (K, V) => Boolean): Boolean = f(k1, v1) + null } - class Map2[K <: AnyRef, +V <: AnyRef] (k1: K, v1: V, k2: K, v2: V) extends SimpleIdentityMap[K, V] { - def size: Int = 2 - def apply(k: K): V | Null = - if (k eq k1) v1 - else if (k eq k2) v2 - else null - def remove(k: K): SimpleIdentityMap[K, V] = - if (k eq k1) new Map1(k2, v2) - else if (k eq k2) new Map1(k1, v1) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V1] = - if (k eq k1) new Map2(k, v, k2, v2) - else if (k eq k2) new Map2(k1, v1, k, v) - else new Map3(k1, v1, k2, v2, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { - val w1 = f(k1, v1); val w2 = f(k2, v2) - if ((v1 eq w1) && (v2 eq w2)) this - else new Map2(k1, w1, k2, w2) + def remove(k: K): SimpleIdentityMap[K, V] = { + var i = 0 + while (i < bindings.length) { + if (bindings(i) eq k) + return { + if (size == SimpleIdentityMap.CompactifyThreshold) { + var m: SimpleIdentityMap[K, V] = SimpleIdentityMap.empty[K] + for (j <- 0 until bindings.length by 2) + if (j != i) m = m.updated(key(j), value(j)) + m + } + else { + val bindings1 = new Array[AnyRef](bindings.length - 2) + System.arraycopy(bindings, 0, bindings1, 0, i) + System.arraycopy(bindings, i + 2, bindings1, i, bindings1.length - i) + new SimpleIdentityMap(bindings1) + } + } + i += 2 } - def foreachBinding(f: (K, V) => Unit): Unit = { f(k1, v1); f(k2, v2) } - def forallBinding(f: (K, V) => Boolean): Boolean = f(k1, v1) && f(k2, v2) + this } - class Map3[K <: AnyRef, +V <: AnyRef] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V) extends SimpleIdentityMap[K, V] { - def size: Int = 3 - def apply(k: K): V | Null = - if (k eq k1) v1 - else if (k eq k2) v2 - else if (k eq k3) v3 - else null - def remove(k: K): SimpleIdentityMap[K, V] = - if (k eq k1) new Map2(k2, v2, k3, v3) - else if (k eq k2) new Map2(k1, v1, k3, v3) - else if (k eq k3) new Map2(k1, v1, k2, v2) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V1] = - if (k eq k1) new Map3(k, v, k2, v2, k3, v3) - else if (k eq k2) new Map3(k1, v1, k, v, k3, v3) - else if (k eq k3) new Map3(k1, v1, k2, v2, k, v) - else new Map4(k1, v1, k2, v2, k3, v3, k, v) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { - val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3) - if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3)) this - else new Map3(k1, w1, k2, w2, k3, w3) + def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V] = { + var i = 0 + while (i < bindings.length) { + if (bindings(i) eq k) + return { + if (v eq bindings(i + 1)) this + else { + val bindings1 = bindings.clone + bindings1(i + 1) = v + new SimpleIdentityMap(bindings1) + } + } + i += 2 } - def foreachBinding(f: (K, V) => Unit): Unit = { f(k1, v1); f(k2, v2); f(k3, v3) } - def forallBinding(f: (K, V) => Boolean): Boolean = f(k1, v1) && f(k2, v2) && f(k3, v3) + val bindings2 = new Array[AnyRef](bindings.length + 2) + System.arraycopy(bindings, 0, bindings2, 0, bindings.length) + bindings2(bindings.length) = k + bindings2(bindings.length + 1) = v + new SimpleIdentityMap(bindings2) } - class Map4[K <: AnyRef, +V <: AnyRef] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V, k4: K, v4: V) extends SimpleIdentityMap[K, V] { - def size: Int = 4 - def apply(k: K): V | Null = - if (k eq k1) v1 - else if (k eq k2) v2 - else if (k eq k3) v3 - else if (k eq k4) v4 - else null - def remove(k: K): SimpleIdentityMap[K, V] = - if (k eq k1) new Map3(k2, v2, k3, v3, k4, v4) - else if (k eq k2) new Map3(k1, v1, k3, v3, k4, v4) - else if (k eq k3) new Map3(k1, v1, k2, v2, k4, v4) - else if (k eq k4) new Map3(k1, v1, k2, v2, k3, v3) - else this - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V1] = - if (k eq k1) new Map4(k, v, k2, v2, k3, v3, k4, v4) - else if (k eq k2) new Map4(k1, v1, k, v, k3, v3, k4, v4) - else if (k eq k3) new Map4(k1, v1, k2, v2, k, v, k4, v4) - else if (k eq k4) new Map4(k1, v1, k2, v2, k3, v3, k, v) - else new MapMore(Array[AnyRef](k1, v1, k2, v2, k3, v3, k4, v4, k, v)) - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { - val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3); val w4 = f(k4, v4) - if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3) && (v4 eq w4)) this - else new Map4(k1, w1, k2, w2, k3, w3, k4, w4) + def contains(k: K): Boolean = { + var i = 0 + while (i < bindings.length) { + if (bindings(i) eq k) return true + i += 2 } - def foreachBinding(f: (K, V) => Unit): Unit = { f(k1, v1); f(k2, v2); f(k3, v3); f(k4, v4) } - def forallBinding(f: (K, V) => Boolean): Boolean = f(k1, v1) && f(k2, v2) && f(k3, v3) && f(k4, v4) + false } - class MapMore[K <: AnyRef, +V <: AnyRef](bindings: Array[AnyRef]) extends SimpleIdentityMap[K, V] { - private def key(i: Int): K = bindings(i).asInstanceOf[K] - private def value(i: Int): V = bindings(i + 1).asInstanceOf[V] - - def size: Int = bindings.length / 2 - Stats.record(s"SimpleIdentityMap/$size") - - def apply(k: K): V | Null = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) return value(i) - i += 2 - } - null + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { + var bindings1: Array[AnyRef] = bindings + var i = 0 + while (i < bindings.length) { + val v = value(i) + val v1 = f(key(i), v) + if ((v1 ne v) && (bindings1 eq bindings)) + bindings1 = bindings.clone + bindings1(i) = bindings(i) + bindings1(i + 1) = v1 + i += 2 } + if (bindings1 eq bindings) this else new SimpleIdentityMap(bindings1) + } - def remove(k: K): SimpleIdentityMap[K, V] = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) - return { - if (size == CompactifyThreshold) { - var m: SimpleIdentityMap[K, V] = empty[K] - for (j <- 0 until bindings.length by 2) - if (j != i) m = m.updated(key(j), value(j)) - m - } - else { - val bindings1 = new Array[AnyRef](bindings.length - 2) - System.arraycopy(bindings, 0, bindings1, 0, i) - System.arraycopy(bindings, i + 2, bindings1, i, bindings1.length - i) - new MapMore(bindings1) - } - } - i += 2 - } - this + def foreachBinding(f: (K, V) => Unit): Unit = { + var i = 0 + while (i < bindings.length) { + f(key(i), value(i)) + i += 2 } + } - def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleIdentityMap[K, V] = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) - return { - if (v eq bindings(i + 1)) this - else { - val bindings1 = bindings.clone - bindings1(i + 1) = v - new MapMore(bindings1) - } - } - i += 2 - } - val bindings2 = new Array[AnyRef](bindings.length + 2) - System.arraycopy(bindings, 0, bindings2, 0, bindings.length) - bindings2(bindings.length) = k - bindings2(bindings.length + 1) = v - new MapMore(bindings2) + def forallBinding(f: (K, V) => Boolean): Boolean = { + var i = 0 + while (i < bindings.length) { + if (!f(key(i), value(i))) + return false + i += 2 } + true + } - override def contains(k: K): Boolean = { - var i = 0 - while (i < bindings.length) { - if (bindings(i) eq k) return true - i += 2 - } - false - } + private def map2[T](f: (K, V) => T): List[T] = { + val buf = new ListBuffer[T] + foreachBinding((k, v) => buf += f(k, v)) + buf.toList + } - def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleIdentityMap[K, V1] = { - var bindings1: Array[AnyRef] = bindings - var i = 0 - while (i < bindings.length) { - val v = value(i) - val v1 = f(key(i), v) - if ((v1 ne v) && (bindings1 eq bindings)) - bindings1 = bindings.clone - bindings1(i) = bindings(i) - bindings1(i + 1) = v1 - i += 2 - } - if (bindings1 eq bindings) this else new MapMore(bindings1) - } + def keys: List[K] = map2((k, v) => k) - def foreachBinding(f: (K, V) => Unit): Unit = { - var i = 0 - while (i < bindings.length) { - f(key(i), value(i)) - i += 2 - } - } + def toList: List[(K, V)] = map2((k, v) => (k, v)) - def forallBinding(f: (K, V) => Boolean): Boolean = { - var i = 0 - while (i < bindings.length) { - if (!f(key(i), value(i))) - return false - i += 2 - } - return true - } + override def toString: String = { + def assocToString(key: K, value: V) = s"$key -> $value" + + map2(assocToString) mkString("(", ", ", ")") } + +} + +object SimpleIdentityMap { + private val emptyMap = new SimpleIdentityMap(Array.empty[AnyRef]) + + private val CompactifyThreshold = 4 + + def empty[K <: AnyRef]: SimpleIdentityMap[K, Nothing] = emptyMap.asInstanceOf[SimpleIdentityMap[K, Nothing]] } diff --git a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala index 654681b2edef..3c0ffcde323a 100644 --- a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala +++ b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala @@ -3,30 +3,79 @@ package dotty.tools.dotc.util import collection.mutable /** A simple linked set with `eq` as the comparison, optimized for small sets. - * It has linear complexity for `contains`, `+`, and `-`. + * It has linear complexity for `contains`, `+`, and `-`. */ -abstract class SimpleIdentitySet[+Elem <: AnyRef] { - def size: Int - def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] - def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] - def contains[E >: Elem <: AnyRef](x: E): Boolean - def foreach(f: Elem => Unit): Unit - def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean +final class SimpleIdentitySet[+Elem <: AnyRef](private val xs: Array[AnyRef]) { + def size: Int = xs.length + + def +[E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] = + if (contains(x)) this + else { + val xs1 = new Array[AnyRef](size + 1) + System.arraycopy(xs, 0, xs1, 0, size) + xs1(size) = x + new SimpleIdentitySet[E](xs1) + } + + def -[E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] = { + var i = 0 + while (i < size && (xs(i) `ne` x)) i += 1 + if (i == size) this + else if (size == 4) + if (i == 0) SimpleIdentitySet(xs(1).asInstanceOf[Elem], xs(2).asInstanceOf[Elem], xs(3).asInstanceOf[Elem]) + else if (i == 1) SimpleIdentitySet(xs(0).asInstanceOf[Elem], xs(2).asInstanceOf[Elem], xs(3).asInstanceOf[Elem]) + else if (i == 2) SimpleIdentitySet(xs(0).asInstanceOf[Elem], xs(1).asInstanceOf[Elem], xs(3).asInstanceOf[Elem]) + else SimpleIdentitySet(xs(0).asInstanceOf[Elem], xs(1).asInstanceOf[Elem], xs(2).asInstanceOf[Elem]) + else { + val xs1 = new Array[AnyRef](size - 1) + System.arraycopy(xs, 0, xs1, 0, i) + System.arraycopy(xs, i + 1, xs1, i, size - (i + 1)) + new SimpleIdentitySet(xs1) + } + } + + def contains[E >: Elem <: AnyRef](x: E): Boolean = { + var i = 0 + while (i < size && (xs(i) `ne` x)) i += 1 + i < size + } + + def foreach(f: Elem => Unit): Unit = { + var i = 0 + while (i < size) { + f(xs(i).asInstanceOf[Elem]); i += 1 + } + } + + def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = + xs.asInstanceOf[Array[E]].exists(p) + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = var acc: SimpleIdentitySet[B] = SimpleIdentitySet.empty foreach(x => acc += f(x)) acc + def flatMap[B <: AnyRef](f: Elem => SimpleIdentitySet[B]): SimpleIdentitySet[B] = var acc: SimpleIdentitySet[B] = SimpleIdentitySet.empty foreach(x => acc ++= f(x)) acc - def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A - def toList: List[Elem] - def nth(n: Int): Elem - final def isEmpty: Boolean = size == 0 + def /:[A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = + xs.asInstanceOf[Array[E]].foldLeft(z)(f) - final def iterator: Iterator[Elem] = Iterator.tabulate(size)(nth) + def toList: List[Elem] = { + val buf = new mutable.ListBuffer[Elem] + foreach(buf += _) + buf.toList + } + + def nth(n: Int): Elem = + if 0 <= n && n < size then xs(n).asInstanceOf[Elem] + else throw new IndexOutOfBoundsException(n.toString) + + def isEmpty: Boolean = size == 0 + + def iterator: Iterator[Elem] = Iterator.tabulate(size)(nth) def forall[E >: Elem <: AnyRef](p: E => Boolean): Boolean = !exists(!p(_)) @@ -34,253 +83,88 @@ abstract class SimpleIdentitySet[+Elem <: AnyRef] { val z: SimpleIdentitySet[Elem] = SimpleIdentitySet.empty (z /: this)((s, x) => if p(x) then s + x else s) - def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = - if (this.size == 0) that - else if (that.size == 0) this - else ((this: SimpleIdentitySet[E]) /: that)(_ + _) + def ++[E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = + if that.isEmpty then return this + var toAdd: mutable.ArrayBuffer[AnyRef] | Null = null + var i = 0 + val limit = that.xs.length + while (i < limit) { + val elem = that.xs(i) + if (!contains(elem)) { + if (toAdd == null) toAdd = new mutable.ArrayBuffer + toAdd += elem + } + i += 1 + } + if (toAdd == null) this + else { + val numAdded = toAdd.size + val xs1 = new Array[AnyRef](size + numAdded) + System.arraycopy(xs, 0, xs1, 0, size) + var i = 0 + while (i < numAdded) { + xs1(i + size) = toAdd(i) + i += 1 + } + new SimpleIdentitySet[E](xs1) + } - def -- [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = - if (that.size == 0) this - else - ((SimpleIdentitySet.empty: SimpleIdentitySet[E]) /: this) { (s, x) => - if (that.contains(x)) s else s + x + def --[E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = + if that.isEmpty then return this + // optimize assuming they are similar + // by starting from empty set and adding elements + var toAdd: mutable.ArrayBuffer[AnyRef] | Null = null + val thisSize = this.size + val thatSize = that.size + val thatElems = that.xs + var i = 0 + var searchStart = 0 + while (i < thisSize) { + val elem = this.xs(i) + var j = searchStart // search thatElems in round-robin fashion, starting one after latest hit + var missing = false + while (!missing && (elem ne thatElems(j))) { + j += 1 + if (j == thatSize) j = 0 + missing = j == searchStart } + if (missing) { + if (toAdd == null) toAdd = new mutable.ArrayBuffer + toAdd += elem + } + else searchStart = (j + 1) % thatSize + i += 1 + } + if (toAdd == null) SimpleIdentitySet.empty + else new SimpleIdentitySet[E](toAdd.toArray) - def ** [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = + def **[E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = if this.size == 0 then this else if that.size == 0 then that else this.filter(that.contains) - def == [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): Boolean = - (this eq that) || this.size == that.size && forall(that.contains) + def ==[E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): Boolean = + (this.xs eq that.xs) || this.size == that.size && forall(that.contains) - def != [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): Boolean = + def !=[E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): Boolean = !(this == that) override def toString: String = toList.mkString("{", ", ", "}") } object SimpleIdentitySet { + private val emptySet = new SimpleIdentitySet(Array.empty[AnyRef]) def apply[Elem <: AnyRef](elems: Elem*): SimpleIdentitySet[Elem] = - elems.foldLeft(empty: SimpleIdentitySet[Elem])(_ + _) + new SimpleIdentitySet[Elem](elems.toArray) + + def apply[Elem <: AnyRef](elems: Iterator[Elem]): SimpleIdentitySet[Elem] = + new SimpleIdentitySet[Elem](elems.toArray) extension [E <: AnyRef](xs: SimpleIdentitySet[E]) def intersect(ys: SimpleIdentitySet[E]): SimpleIdentitySet[E] = xs.filter(ys.contains) - object empty extends SimpleIdentitySet[Nothing] { - def size: Int = 0 - def + [E <: AnyRef](x: E): SimpleIdentitySet[E] = - new Set1[E](x) - def - [E <: AnyRef](x: E): SimpleIdentitySet[Nothing] = - this - def contains[E <: AnyRef](x: E): Boolean = false - def foreach(f: Nothing => Unit): Unit = () - def exists[E <: AnyRef](p: E => Boolean): Boolean = false - override def map[B <: AnyRef](f: Nothing => B): SimpleIdentitySet[B] = empty - def /: [A, E <: AnyRef](z: A)(f: (A, E) => A): A = z - def toList = Nil - def nth(n: Int): Nothing = throw new IndexOutOfBoundsException(n.toString) - } - - private class Set1[+Elem <: AnyRef](x0: AnyRef) extends SimpleIdentitySet[Elem] { - def size = 1 - def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] = - if (contains(x)) this else new Set2[E](x0, x) - def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] = - if (x `eq` x0) empty else this - def contains[E >: Elem <: AnyRef](x: E): Boolean = x `eq` x0 - def foreach(f: Elem => Unit): Unit = f(x0.asInstanceOf[Elem]) - def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = - p(x0.asInstanceOf[E]) - override def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = - Set1(f(x0.asInstanceOf[Elem])) - def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = - f(z, x0.asInstanceOf[E]) - def toList = x0.asInstanceOf[Elem] :: Nil - def nth(n: Int) = - if n == 0 then x0.asInstanceOf[Elem] - else throw new IndexOutOfBoundsException(n.toString) - } - - private class Set2[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef) extends SimpleIdentitySet[Elem] { - def size = 2 - def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] = - if (contains(x)) this else new Set3(x0, x1, x) - def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] = - if (x `eq` x0) new Set1(x1) - else if (x `eq` x1) new Set1(x0) - else this - def contains[E >: Elem <: AnyRef](x: E): Boolean = (x `eq` x0) || (x `eq` x1) - def foreach(f: Elem => Unit): Unit = { f(x0.asInstanceOf[Elem]); f(x1.asInstanceOf[Elem]) } - def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = - p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E]) - override def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = - val y0 = f(x0.asInstanceOf[Elem]) - val y1 = f(x1.asInstanceOf[Elem]) - if y0 eq y1 then Set1(y0) else Set2(y0, y1) - def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = - f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]) - def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: Nil - def nth(n: Int) = n match - case 0 => x0.asInstanceOf[Elem] - case 1 => x1.asInstanceOf[Elem] - case _ => throw new IndexOutOfBoundsException(n.toString) - } - - private class Set3[+Elem <: AnyRef](x0: AnyRef, x1: AnyRef, x2: AnyRef) extends SimpleIdentitySet[Elem] { - def size = 3 - def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] = - if (contains(x)) this - else { - val xs = new Array[AnyRef](4) - xs(0) = x0 - xs(1) = x1 - xs(2) = x2 - xs(3) = x - new SetN[E](xs) - } - def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] = - if (x `eq` x0) new Set2(x1, x2) - else if (x `eq` x1) new Set2(x0, x2) - else if (x `eq` x2) new Set2(x0, x1) - else this - def contains[E >: Elem <: AnyRef](x: E): Boolean = (x `eq` x0) || (x `eq` x1) || (x `eq` x2) - def foreach(f: Elem => Unit): Unit = { - f(x0.asInstanceOf[Elem]); f(x1.asInstanceOf[Elem]); f(x2.asInstanceOf[Elem]) - } - def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = - p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E]) || p(x2.asInstanceOf[E]) - override def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = - val y0 = f(x0.asInstanceOf[Elem]) - val y1 = f(x1.asInstanceOf[Elem]) - val y2 = f(x2.asInstanceOf[Elem]) - if y1 eq y0 then - if y2 eq y0 then Set1(y0) else Set2(y0, y2) - else if (y2 eq y0) || (y2 eq y1) then Set2(y0, y1) - else Set3(y0, y1, y2) - def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = - f(f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]), x2.asInstanceOf[E]) - def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: x2.asInstanceOf[Elem] :: Nil - def nth(n: Int) = n match - case 0 => x0.asInstanceOf[Elem] - case 1 => x1.asInstanceOf[Elem] - case 2 => x2.asInstanceOf[Elem] - case _ => throw new IndexOutOfBoundsException(n.toString) - } - - private class SetN[+Elem <: AnyRef](val xs: Array[AnyRef]) extends SimpleIdentitySet[Elem] { - def size = xs.length - def + [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[E] = - if (contains(x)) this - else { - val xs1 = new Array[AnyRef](size + 1) - System.arraycopy(xs, 0, xs1, 0, size) - xs1(size) = x - new SetN[E](xs1) - } - def - [E >: Elem <: AnyRef](x: E): SimpleIdentitySet[Elem] = { - var i = 0 - while (i < size && (xs(i) `ne` x)) i += 1 - if (i == size) this - else if (size == 4) - if (i == 0) new Set3(xs(1), xs(2), xs(3)) - else if (i == 1) new Set3(xs(0), xs(2), xs(3)) - else if (i == 2) new Set3(xs(0), xs(1), xs(3)) - else new Set3(xs(0), xs(1), xs(2)) - else { - val xs1 = new Array[AnyRef](size - 1) - System.arraycopy(xs, 0, xs1, 0, i) - System.arraycopy(xs, i + 1, xs1, i, size - (i + 1)) - new SetN(xs1) - } - } - def contains[E >: Elem <: AnyRef](x: E): Boolean = { - var i = 0 - while (i < size && (xs(i) `ne` x)) i += 1 - i < size - } - def foreach(f: Elem => Unit): Unit = { - var i = 0 - while (i < size) { f(xs(i).asInstanceOf[Elem]); i += 1 } - } - def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = - xs.asInstanceOf[Array[E]].exists(p) - def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = - xs.asInstanceOf[Array[E]].foldLeft(z)(f) - def toList: List[Elem] = { - val buf = new mutable.ListBuffer[Elem] - foreach(buf += _) - buf.toList - } - def nth(n: Int) = - if 0 <= n && n < size then xs(n).asInstanceOf[Elem] - else throw new IndexOutOfBoundsException(n.toString) - override def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = - that match { - case that: SetN[?] => - var toAdd: mutable.ArrayBuffer[AnyRef] | Null = null - var i = 0 - val limit = that.xs.length - while (i < limit) { - val elem = that.xs(i) - if (!contains(elem)) { - if (toAdd == null) toAdd = new mutable.ArrayBuffer - toAdd += elem - } - i += 1 - } - if (toAdd == null) this - else { - val numAdded = toAdd.size - val xs1 = new Array[AnyRef](size + numAdded) - System.arraycopy(xs, 0, xs1, 0, size) - var i = 0 - while (i < numAdded) { - xs1(i + size) = toAdd(i) - i += 1 - } - new SetN[E](xs1) - } - case _ => super.++(that) - } - override def -- [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] = - that match { - case that: SetN[?] => - // both sets are large, optimize assuming they are similar - // by starting from empty set and adding elements - var toAdd: mutable.ArrayBuffer[AnyRef] | Null = null - val thisSize = this.size - val thatSize = that.size - val thatElems = that.xs - var i = 0 - var searchStart = 0 - while (i < thisSize) { - val elem = this.xs(i) - var j = searchStart // search thatElems in round robin fashion, starting one after latest hit - var missing = false - while (!missing && (elem ne thatElems(j))) { - j += 1 - if (j == thatSize) j = 0 - missing = j == searchStart - } - if (missing) { - if (toAdd == null) toAdd = new mutable.ArrayBuffer - toAdd += elem - } - else searchStart = (j + 1) % thatSize - i += 1 - } - if (toAdd == null) empty - else toAdd.size match { - case 1 => new Set1[E](toAdd(0)) - case 2 => new Set2[E](toAdd(0), toAdd(1)) - case 3 => new Set3[E](toAdd(0), toAdd(1), toAdd(2)) - case _ => new SetN[E](toAdd.toArray) - } - case _ => // this set is large, that set is small: reduce from above using `-` - ((this: SimpleIdentitySet[E]) /: that)(_ - _) - } - } + def empty: SimpleIdentitySet[Nothing] = + emptySet }