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

scala.collection.immutable.TreeSeqMap.scala Maven / Gradle / Ivy

/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala
package collection
package immutable

import scala.annotation.tailrec

/** This class implements an immutable map that preserves order using
  * a hash map for the key to value mapping to provide efficient lookup,
  * and a tree for the ordering of the keys to provide efficient
  * insertion/modification order traversal and destructuring.
  *
  * By default insertion order (`TreeSeqMap.OrderBy.Insertion`)
  * is used, but modification order (`TreeSeqMap.OrderBy.Modification`)
  * can be used instead if so specified at creation.
  *
  * The `orderingBy(orderBy: TreeSeqMap.OrderBy): TreeSeqMap[K, V]` method
  * can be used to switch to the specified ordering for the returned map.
  *
  * A key can be manually refreshed (i.e. placed at the end) via the
  * `refresh(key: K): TreeSeqMap[K, V]` method (regardless of the ordering in
  * use).
  *
  * Internally, an ordinal counter is increased for each insertion/modification
  * and then the current ordinal is used as key in the tree map. After 2^32^
  * insertions/modifications the entire map is copied (thus resetting the ordinal
  * counter).
  *
  *  @tparam K the type of the keys contained in this map.
  *  @tparam V the type of the values associated with the keys in this map.
  * @define coll immutable tree seq map
  * @define Coll `immutable.TreeSeqMap`
  */
final class TreeSeqMap[K, +V] private (
    private val ordering: TreeSeqMap.Ordering[K],
    private val mapping: TreeSeqMap.Mapping[K, V],
    private val ordinal: Int,
    val orderedBy: TreeSeqMap.OrderBy)
  extends AbstractMap[K, V]
    with SeqMap[K, V]
    with MapOps[K, V, TreeSeqMap, TreeSeqMap[K, V]]
    with StrictOptimizedIterableOps[(K, V), Iterable, TreeSeqMap[K, V]]
    with StrictOptimizedMapOps[K, V, TreeSeqMap, TreeSeqMap[K, V]]
    with MapFactoryDefaults[K, V, TreeSeqMap, Iterable] {

  import TreeSeqMap._

  override protected[this] def className: String = "TreeSeqMap"

  override def mapFactory: MapFactory[TreeSeqMap] = TreeSeqMap

  override val size = mapping.size

  override def knownSize: Int = size

  override def isEmpty = size == 0

  /*
  // This should have been overridden in 2.13.0 but wasn't so it will have to wait since it is not forwards compatible
  // Now handled in inherited method from scala.collection.MapFactoryDefaults instead.
  override def empty = TreeSeqMap.empty[K, V](orderedBy)
  */

  def orderingBy(orderBy: OrderBy): TreeSeqMap[K, V] = {
    if (orderBy == this.orderedBy) this
    else if (isEmpty) TreeSeqMap.empty(orderBy)
    else new TreeSeqMap(ordering, mapping, ordinal, orderBy)
  }

  def updated[V1 >: V](key: K, value: V1): TreeSeqMap[K, V1] = {
    mapping.get(key) match {
      case e if ordinal == -1 && (orderedBy == OrderBy.Modification || e.isEmpty) =>
        // Reinsert into fresh instance to restart ordinal counting, expensive but only done after 2^32 updates.
        TreeSeqMap.empty[K, V1](orderedBy) ++ this + (key -> value)
      case Some((o, _)) if orderedBy == OrderBy.Insertion =>
        new TreeSeqMap(
          ordering.include(o, key),
          mapping.updated[(Int, V1)](key, (o, value)),
          ordinal, // Do not increment the ordinal since the key is already present, i.e. o <= ordinal.
          orderedBy)
      case Some((o, _)) =>
        val o1 = increment(ordinal)
        new TreeSeqMap(
          ordering.exclude(o).append(o1, key),
          mapping.updated[(Int, V1)](key, (o1, value)),
          o1,
          orderedBy)
      case None =>
        val o1 = increment(ordinal)
        new TreeSeqMap(
          ordering.append(o1, key),
          mapping.updated[(Int, V1)](key, (o1, value)),
          o1,
          orderedBy)
    }
  }

  def removed(key: K): TreeSeqMap[K, V] = {
    mapping.get(key) match {
      case Some((o, _)) =>
        new TreeSeqMap(
          ordering.exclude(o),
          mapping.removed(key),
          ordinal,
          orderedBy)
      case None =>
        this
    }
  }

  def refresh(key: K): TreeSeqMap[K, V] = {
    mapping.get(key) match {
      case Some((o, _)) =>
        val o1 = increment(ordinal)
        new TreeSeqMap(
          ordering.exclude(o).append(o1, key),
          mapping,
          o1,
          orderedBy)
      case None =>
        this
    }
  }

  def get(key: K): Option[V] = mapping.get(key).map(value)

  def iterator: Iterator[(K, V)] = new AbstractIterator[(K, V)] {
    private[this] val iter = ordering.iterator

    override def hasNext: Boolean = iter.hasNext

    override def next(): (K, V) = binding(iter.next())
  }

  override def keysIterator: Iterator[K] = new AbstractIterator[K] {
    private[this] val iter = ordering.iterator

    override def hasNext: Boolean = iter.hasNext

    override def next(): K = iter.next()
  }

  override def valuesIterator: Iterator[V] = new AbstractIterator[V] {
    private[this] val iter = ordering.iterator

    override def hasNext: Boolean = iter.hasNext

    override def next(): V = value(binding(iter.next()))
  }

  override def contains(key: K): Boolean = mapping.contains(key)

  override def head: (K, V) = binding(ordering.head)

  override def headOption = ordering.headOption.map(binding)

  override def last: (K, V) = binding(ordering.last)

  override def lastOption: Option[(K, V)] = ordering.lastOption.map(binding)

  override def tail: TreeSeqMap[K, V] = {
    val (head, tail) = ordering.headTail
    new TreeSeqMap(tail, mapping.removed(head), ordinal, orderedBy)
  }

  override def init: TreeSeqMap[K, V] = {
    val (init, last) = ordering.initLast
    new TreeSeqMap(init, mapping.removed(last), ordinal, orderedBy)
  }

  override def slice(from: Int, until: Int): TreeSeqMap[K, V] = {
    val sz = size
    if (sz == 0 || from >= until) TreeSeqMap.empty[K, V](orderedBy)
    else {
      val sz = size
      val f = if (from >= 0) from else 0
      val u = if (until <= sz) until else sz
      val l = u - f
      if (l <= 0) TreeSeqMap.empty[K, V](orderedBy)
      else if (l > sz / 2) {
        // Remove front and rear incrementally if majority of elements are to be kept
        val (front, rest) = ordering.splitAt(f)
        val (ong, rear) = rest.splitAt(l)
        var mng = this.mapping
        val frontIter = front.iterator
        while (frontIter.hasNext) {
          mng = mng - frontIter.next()
        }
        val rearIter = rear.iterator
        while (rearIter.hasNext) {
          mng = mng - rearIter.next()
        }
        new TreeSeqMap(ong, mng, ordinal, orderedBy)
      } else {
        // Populate with builder otherwise
        val bdr = newBuilder[K, V](orderedBy)
        val iter = ordering.iterator
        var i = 0
        while (i < f) {
          iter.next()
          i += 1
        }
        while (i < u) {
          val k = iter.next()
          bdr.addOne((k, mapping(k)._2))
          i += 1
        }
        bdr.result()
      }
    }
  }

  override def map[K2, V2](f: ((K, V)) => (K2, V2)): TreeSeqMap[K2, V2] = {
    val bdr = newBuilder[K2, V2](orderedBy)
    val iter = ordering.iterator
    while (iter.hasNext) {
      val k = iter.next()
      val (_, v) = mapping(k)
      val (k2, v2) = f((k, v))
      bdr.addOne((k2, v2))
    }
    bdr.result()
  }

  override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): TreeSeqMap[K2, V2] = {
    val bdr = newBuilder[K2, V2](orderedBy)
    val iter = ordering.iterator
    while (iter.hasNext) {
      val k = iter.next()
      val (_, v) = mapping(k)
      val jter = f((k, v)).iterator
      while (jter.hasNext) {
        val (k2, v2) = jter.next()
        bdr.addOne((k2, v2))
      }
    }
    bdr.result()
  }

  override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): TreeSeqMap[K2, V2] = {
    val bdr = newBuilder[K2, V2](orderedBy)
    val iter = ordering.iterator
    while (iter.hasNext) {
      val k = iter.next()
      val (_, v) = mapping(k)
      pf.runWith({ case (k2, v2) => bdr.addOne((k2, v2)) })((k, v))
    }
    bdr.result()
  }

  override def concat[V2 >: V](suffix: IterableOnce[(K, V2)]): TreeSeqMap[K, V2] = {
    var ong: Ordering[K] = ordering
    var mng: Mapping[K, V2] = mapping
    var ord = increment(ordinal)
    val iter = suffix.iterator
    while (iter.hasNext) {
      val (k, v2) = iter.next()
      mng.get(k) match {
        case Some((o, v)) =>
          if (orderedBy == OrderBy.Insertion && v != v2) mng = mng.updated(k, (o, v2))
          else if (orderedBy == OrderBy.Modification) {
            mng = mng.updated(k, (ord, v2))
            ong = ong.exclude(o).append(ord, k)
            ord = increment(ord)
          }
        case None =>
          mng = mng.updated(k, (ord, v2))
          ong = ong.append(ord, k)
          ord = increment(ord)
      }
    }
    new TreeSeqMap[K, V2](ong, mng, ord, orderedBy)
  }

  @`inline` private[this] def value(p: (_, V)) = p._2
  @`inline` private[this] def binding(k: K) = mapping(k).copy(_1 = k)
}
object TreeSeqMap extends MapFactory[TreeSeqMap] {
  sealed trait OrderBy
  object OrderBy {
    case object Insertion extends OrderBy
    case object Modification extends OrderBy
  }

  private val EmptyByInsertion = new TreeSeqMap[Nothing, Nothing](Ordering.empty, HashMap.empty, 0, OrderBy.Insertion)
  private val EmptyByModification = new TreeSeqMap[Nothing, Nothing](Ordering.empty, HashMap.empty, 0, OrderBy.Modification)
  val Empty = EmptyByInsertion
  def empty[K, V]: TreeSeqMap[K, V] = empty(OrderBy.Insertion)
  def empty[K, V](orderBy: OrderBy): TreeSeqMap[K, V] = {
    if (orderBy == OrderBy.Modification) EmptyByModification
    else EmptyByInsertion
  }.asInstanceOf[TreeSeqMap[K, V]]

  def from[K, V](it: collection.IterableOnce[(K, V)]): TreeSeqMap[K, V] =
    it match {
      case om: TreeSeqMap[K, V] => om
      case _ => (newBuilder[K, V] ++= it).result()
    }

  @inline private def increment(ord: Int) = if (ord == Int.MaxValue) Int.MinValue else ord + 1

  def newBuilder[K, V]: mutable.Builder[(K, V), TreeSeqMap[K, V]] = newBuilder(OrderBy.Insertion)
  def newBuilder[K, V](orderedBy: OrderBy): mutable.Builder[(K, V), TreeSeqMap[K, V]] = new Builder[K, V](orderedBy)

  final class Builder[K, V](orderedBy: OrderBy) extends mutable.Builder[(K, V), TreeSeqMap[K, V]] {
    private[this] val bdr = new MapBuilderImpl[K, (Int, V)]
    private[this] var ong = Ordering.empty[K]
    private[this] var ord = 0
    private[this] var aliased: TreeSeqMap[K, V] = _

    override def addOne(elem: (K, V)): this.type = addOne(elem._1, elem._2)
    def addOne(key: K, value: V): this.type = {
      if (aliased ne null) {
        aliased = aliased.updated(key, value)
      } else {
        bdr.getOrElse(key, null) match {
          case (o, v) =>
            if (orderedBy == OrderBy.Insertion && v != value) bdr.addOne(key, (o, value))
            else if (orderedBy == OrderBy.Modification) {
              bdr.addOne(key, (ord, value))
              ong = ong.exclude(o).appendInPlace(ord, key)
              ord = increment(ord)
            }
          case null =>
            bdr.addOne(key, (ord, value))
            ong = ong.appendInPlace(ord, key)
            ord = increment(ord)
        }
      }
      this
    }

    override def clear(): Unit = {
      ong = Ordering.empty
      ord = 0
      bdr.clear()
      aliased = null
    }

    override def result(): TreeSeqMap[K, V] = {
      if (aliased eq null) {
        aliased = new TreeSeqMap(ong, bdr.result(), ord, orderedBy)
      }
      aliased
    }
  }

  private type Mapping[K, +V] = Map[K, (Int, V)]
  @annotation.unused
  private val Mapping = Map

  /* The ordering implementation below is an adapted version of immutable.IntMap. */
  private[immutable] object Ordering {
    import scala.collection.generic.BitOperations.Int._

    @inline private[immutable] def toBinaryString(i: Int): String = s"$i/${i.toBinaryString}"

    def empty[T] : Ordering[T] = Zero

    def apply[T](elems: (Int, T)*): Ordering[T] =
      elems.foldLeft(empty[T])((x, y) => x.include(y._1, y._2))

    // Iterator over a non-empty Ordering.
    final class Iterator[+V](it: Ordering[V]) {
      // Basically this uses a simple stack to emulate conversion over the tree. However
      // because we know that Ints are at least 32 bits we can have at most 32 Bins and
      // one Tip sitting on the tree at any point. Therefore we know the maximum stack
      // depth is 33
      private[this] var index = 0
      private[this] val buffer = new Array[AnyRef](33)

      private[this] def pop = {
        index -= 1
        buffer(index).asInstanceOf[Ordering[V]]
      }

      private[this] def push[V2 >: V](x: Ordering[V2]): Unit = {
        buffer(index) = x.asInstanceOf[AnyRef]
        index += 1
      }

      if (it != Zero) push(it)

      def hasNext = index != 0
      @tailrec
      def next(): V =
        pop match {
          case Bin(_,_, Tip(_, v), right) =>
            push(right)
            v
          case Bin(_, _, left, right) =>
            push(right)
            push(left)
            next()
          case Tip(_, v) => v
          // This should never happen. We don't allow Ordering.Zero in subtrees of the Ordering
          // and don't return an Ordering.Iterator for Ordering.Zero.
          case Zero => throw new IllegalStateException("empty subtree not allowed")
        }
    }

    object Iterator {
      val Empty = new Iterator[Nothing](Ordering.empty[Nothing])
      def empty[V]: Iterator[V] = Empty.asInstanceOf[Iterator[V]]
    }

    case object Zero extends Ordering[Nothing] {
      // Important! Without this equals method in place, an infinite
      // loop from Map.equals => size => pattern-match-on-Nil => equals
      // develops.  Case objects and custom equality don't mix without
      // careful handling.
      override def equals(that : Any): Boolean = that match {
        case _: this.type => true
        case _: Ordering[_] => false // The only empty Orderings are eq Nil
        case _ => super.equals(that)
      }
      protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = sb ++= s"${prefix}Ø"
    }

    final case class Tip[+T](ord: Int, value: T) extends Ordering[T] {
      def withValue[S](s: S) =
        if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[Tip[S]]
        else Tip(ord, s)
      protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = sb ++= s"${prefix}Tip(${toBinaryString(ord)} -> $value)\n"
    }

    final case class Bin[+T](prefix: Int, mask: Int, left: Ordering[T], var right: Ordering[T] @scala.annotation.unchecked.uncheckedVariance) extends Ordering[T] {
      def bin[S](left: Ordering[S], right: Ordering[S]): Ordering[S] = {
        if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[Bin[S]]
        else Bin[S](prefix, mask, left, right)
      }
      protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit = {
        sb ++= s"${prefix}Bin(${toBinaryString(this.prefix)}:${toBinaryString(mask)})\n"
        left.format(sb, subPrefix + "├── ", subPrefix + "│   ")
        right.format(sb, subPrefix + "└── ", subPrefix + "    ")
      }
    }

    private def branchMask(i: Int, j: Int) = highestOneBit(i ^ j)

    private def join[T](p1: Int, t1: Ordering[T], p2: Int, t2: Ordering[T]): Ordering[T] = {
      val m = branchMask(p1, p2)
      val p = mask(p1, m)
      if (zero(p1, m)) Bin(p, m, t1, t2)
      else Bin(p, m, t2, t1)
    }

    private def bin[T](prefix: Int, mask: Int, left: Ordering[T], right: Ordering[T]): Ordering[T] = (left, right) match {
      case (l, Zero) => l
      case (Zero, r) => r
      case (l, r) => Bin(prefix, mask, l, r)
    }
  }

  sealed abstract class Ordering[+T] {
    import Ordering._
    import scala.annotation.tailrec
    import scala.collection.generic.BitOperations.Int._

    override final def toString: String = format
    final def format: String = {
      val sb = new StringBuilder
      format(sb, "", "")
      sb.toString()
    }
    protected def format(sb: StringBuilder, prefix: String, subPrefix: String): Unit

    @tailrec
    final def head: T = this match {
      case Zero => throw new NoSuchElementException("head of empty map")
      case Tip(k, v) => v
      case Bin(_, _, l, _) => l.head
    }

    @tailrec
    final def headOption: Option[T] = this match {
      case Zero => None
      case Tip(_, v) => Some(v)
      case Bin(_, _, l, _) => l.headOption
    }

    @tailrec
    final def last: T = this match {
      case Zero => throw new NoSuchElementException("last of empty map")
      case Tip(_, v) => v
      case Bin(_, _, _, r) => r.last
    }

    @tailrec
    final def lastOption: Option[T] = this match {
      case Zero => None
      case Tip(_, v) => Some(v)
      case Bin(_, _, _, r) => r.lastOption
    }

    @tailrec
    final def ordinal: Int = this match {
      case Zero => 0
      case Tip(o, _) => o
      case Bin(_, _, _, r) => r.ordinal
    }

    final def tail: Ordering[T] = this match {
      case Zero => throw new NoSuchElementException("tail of empty map")
      case Tip(_, _) => Zero
      case Bin(p, m, l, r) => bin(p, m, l.tail, r)
    }

    final def headTail: (T, Ordering[T]) = this match {
      case Zero => throw new NoSuchElementException("init of empty map")
      case Tip(_, v) => (v, Zero)
      case Bin(p, m, l, r) =>
        val (head, tail) = l.headTail
        (head, bin(p, m, tail, r))
    }

    final def init: Ordering[T] = this match {
      case Zero => throw new NoSuchElementException("init of empty map")
      case Tip(_, _) => Zero
      case Bin(p, m, l, r) =>
        bin(p, m, l, r.init)
    }

    final def initLast: (Ordering[T], T) = this match {
      case Zero => throw new NoSuchElementException("init of empty map")
      case Tip(_, v) => (Zero, v)
      case Bin(p, m, l, r) =>
        val (init, last) = r.initLast
        (bin(p, m, l, init), last)
    }

    final def iterator: Iterator[T] = this match {
      case Zero => Iterator.empty
      case _ => new Iterator(this)
    }

    final def include[S >: T](ordinal: Int, value: S): Ordering[S] = this match {
      case Zero =>
        Tip(ordinal, value)
      case Tip(o, _) =>
        if (ordinal == o) Tip(ordinal, value)
        else join(ordinal, Tip(ordinal, value), o, this)
      case Bin(p, m, l, r) =>
        if (!hasMatch(ordinal, p, m)) join(ordinal, Tip(ordinal, value), p, this)
        else if (zero(ordinal, m)) Bin(p, m, l.include(ordinal, value), r)
        else Bin(p, m, l, r.include(ordinal, value))
    }

    final def append[S >: T](ordinal: Int, value: S): Ordering[S] = this match {
      case Zero =>
        Tip(ordinal, value)
      case Tip(o, _) =>
        if (ordinal == o) Tip(ordinal, value)
        else join(ordinal, Tip(ordinal, value), o, this)
      case Bin(p, m, l, r) =>
        if (!hasMatch(ordinal, p, m)) join(ordinal, Tip(ordinal, value), p, this)
        else if (zero(ordinal, m)) throw new IllegalArgumentException(s"Append called with ordinal out of range: $ordinal is not greater than current max ordinal ${this.ordinal}")
        else Bin(p, m, l, r.append(ordinal, value))
    }

    @inline private[collection] final def appendInPlace[S >: T](ordinal: Int, value: S): Ordering[S] = appendInPlace1(null, ordinal, value)
    private[collection] final def appendInPlace1[S >: T](parent: Bin[S], ordinal: Int, value: S): Ordering[S] = this match {
      case Zero =>
        Tip(ordinal, value)
      case Tip(o, _) if o >= ordinal =>
        throw new IllegalArgumentException(s"Append called with ordinal out of range: $o is not greater than current max ordinal ${this.ordinal}")
      case Tip(o, _) if parent == null =>
        join(ordinal, Tip(ordinal, value), o, this)
      case Tip(o, _) =>
        parent.right = join(ordinal, Tip(ordinal, value), o, this)
        parent
      case b @ Bin(p, m, _, r) =>
        if (!hasMatch(ordinal, p, m)) {
          val b2 = join(ordinal, Tip(ordinal, value), p, this)
          if (parent != null) {
            parent.right = b2
            parent
          } else b2
        } else if (zero(ordinal, m)) throw new IllegalArgumentException(s"Append called with ordinal out of range: $ordinal is not greater than current max ordinal ${this.ordinal}")
        else {
          r.appendInPlace1(b, ordinal, value)
          this
        }
    }

    final def exclude(ordinal: Int): Ordering[T] = this match {
      case Zero =>
        Zero
      case Tip(o, _) =>
        if (ordinal == o) Zero
        else this
      case Bin(p, m, l, r) =>
        if (!hasMatch(ordinal, p, m)) this
        else if (zero(ordinal, m)) bin(p, m, l.exclude(ordinal), r)
        else bin(p, m, l, r.exclude(ordinal))
    }

    final def splitAt(n: Int): (Ordering[T], Ordering[T]) = {
      var rear = Ordering.empty[T]
      var i = n
      (modifyOrRemove { (o, v) =>
        i -= 1
        if (i >= 0) Some(v)
        else {
          rear = rear.appendInPlace(o, v)
          None
        }
      }, rear)
    }

    /**
      * A combined transform and filter function. Returns an `Ordering` such that
      * for each `(key, value)` mapping in this map, if `f(key, value) == None`
      * the map contains no mapping for key, and if `f(key, value) == Some(x)` the
      * map contains `(key, x)`.
      *
      * @tparam S  The type of the values in the resulting `LongMap`.
      * @param f   The transforming function.
      * @return    The modified map.
      */
    final def modifyOrRemove[S](f: (Int, T) => Option[S]): Ordering[S] = this match {
      case Zero => Zero
      case Tip(key, value) =>
        f(key, value) match {
          case None => Zero
          case Some(value2) =>
            // hack to preserve sharing
            if (value.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) this.asInstanceOf[Ordering[S]]
            else Tip(key, value2)
        }
      case Bin(prefix, mask, left, right) =>
        val l = left.modifyOrRemove(f)
        val r = right.modifyOrRemove(f)
        if ((left eq l) && (right eq r)) this.asInstanceOf[Ordering[S]]
        else bin(prefix, mask, l, r)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy