All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.dotc.util.SimpleIdentitySet.scala Maven / Gradle / Ivy

The newest version!
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 `-`.
 */
abstract class SimpleIdentitySet[+Elem <: AnyRef] {
  def size: Int
  final def isEmpty: Boolean = size == 0
  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
  def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A
  def toList: List[Elem]
  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.size == 0) this
    else
      ((SimpleIdentitySet.empty: SimpleIdentitySet[E]) /: this) { (s, x) =>
        if (that.contains(x)) s else s + x
      }
  override def toString: String = toList.mkString("(", ", ", ")")
}

object SimpleIdentitySet {
  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
    def /: [A, E <: AnyRef](z: A)(f: (A, E) => A): A = z
    def toList = Nil
  }

  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])
    def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A =
      f(z, x0.asInstanceOf[E])
    def toList = x0.asInstanceOf[Elem] :: Nil
  }

  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])
    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
  }

  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])
    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
  }

  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 =
      (z /: xs.asInstanceOf[Array[E]])(f)
    def toList: List[Elem] = {
      val buf = new mutable.ListBuffer[Elem]
      foreach(buf += _)
      buf.toList
    }
    override def ++ [E >: Elem <: AnyRef](that: SimpleIdentitySet[E]): SimpleIdentitySet[E] =
      that match {
        case that: SetN[_] =>
          var toAdd: mutable.ArrayBuffer[AnyRef] = 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
          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)(_ - _)
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy