summaryrefslogtreecommitdiff
path: root/src/library/scala/collection/immutable/TrieIterator.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/library/scala/collection/immutable/TrieIterator.scala')
-rw-r--r--src/library/scala/collection/immutable/TrieIterator.scala219
1 files changed, 219 insertions, 0 deletions
diff --git a/src/library/scala/collection/immutable/TrieIterator.scala b/src/library/scala/collection/immutable/TrieIterator.scala
new file mode 100644
index 0000000000..088b280b8a
--- /dev/null
+++ b/src/library/scala/collection/immutable/TrieIterator.scala
@@ -0,0 +1,219 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.collection
+package immutable
+
+import HashMap.{ HashTrieMap, HashMapCollision1, HashMap1 }
+import HashSet.{ HashTrieSet, HashSetCollision1, HashSet1 }
+import annotation.unchecked.{ uncheckedVariance => uV }
+import scala.annotation.tailrec
+
+/** Abandons any pretense of type safety for speed. You can't say I
+ * didn't try: see r23934.
+ */
+private[collection] abstract class TrieIterator[+T](elems: Array[Iterable[T]]) extends Iterator[T] {
+ outer =>
+
+ private[immutable] def getElem(x: AnyRef): T
+
+ def initDepth = 0
+ def initArrayStack: Array[Array[Iterable[T @uV]]] = new Array[Array[Iterable[T]]](6)
+ def initPosStack = new Array[Int](6)
+ def initArrayD: Array[Iterable[T @uV]] = elems
+ def initPosD = 0
+ def initSubIter: Iterator[T] = null // to traverse collision nodes
+
+ private[this] var depth = initDepth
+ private[this] var arrayStack: Array[Array[Iterable[T @uV]]] = initArrayStack
+ private[this] var posStack = initPosStack
+ private[this] var arrayD: Array[Iterable[T @uV]] = initArrayD
+ private[this] var posD = initPosD
+ private[this] var subIter = initSubIter
+
+ private[this] def getElems(x: Iterable[T]): Array[Iterable[T]] = (x match {
+ case x: HashTrieMap[a, b] => x.elems
+ case x: HashTrieSet[T] => x.elems
+ }).asInstanceOf[Array[Iterable[T]]]
+
+ private[this] def collisionToArray(x: Iterable[T]): Array[Iterable[T]] = (x match {
+ case x: HashMapCollision1[_, _] => x.kvs.map(x => HashMap(x)).toArray
+ case x: HashSetCollision1[_] => x.ks.map(x => HashSet(x)).toArray
+ }).asInstanceOf[Array[Iterable[T]]]
+
+ private type SplitIterators = ((Iterator[T], Int), Iterator[T])
+
+ private def isTrie(x: AnyRef) = x match {
+ case _: HashTrieMap[_,_] | _: HashTrieSet[_] => true
+ case _ => false
+ }
+ private def isContainer(x: AnyRef) = x match {
+ case _: HashMap1[_, _] | _: HashSet1[_] => true
+ case _ => false
+ }
+
+ final class DupIterator(xs: Array[Iterable[T]]) extends {
+ override val initDepth = outer.depth
+ override val initArrayStack: Array[Array[Iterable[T @uV]]] = outer.arrayStack
+ override val initPosStack = outer.posStack
+ override val initArrayD: Array[Iterable[T @uV]] = outer.arrayD
+ override val initPosD = outer.posD
+ override val initSubIter = outer.subIter
+ } with TrieIterator[T](xs) {
+ final override def getElem(x: AnyRef): T = outer.getElem(x)
+ }
+
+ def dupIterator: TrieIterator[T] = new DupIterator(elems)
+
+ private[this] def newIterator(xs: Array[Iterable[T]]) = new TrieIterator(xs) {
+ final override def getElem(x: AnyRef): T = outer.getElem(x)
+ }
+
+ private[this] def iteratorWithSize(arr: Array[Iterable[T]]): (Iterator[T], Int) =
+ (newIterator(arr), arr map (_.size) sum)
+
+ private[this] def arrayToIterators(arr: Array[Iterable[T]]): SplitIterators = {
+ val (fst, snd) = arr.splitAt(arr.length / 2)
+
+ (iteratorWithSize(snd), newIterator(fst))
+ }
+ private[this] def splitArray(ad: Array[Iterable[T]]): SplitIterators =
+ if (ad.length > 1) arrayToIterators(ad)
+ else ad(0) match {
+ case _: HashMapCollision1[_, _] | _: HashSetCollision1[_] =>
+ arrayToIterators(collisionToArray(ad(0)))
+ case _ =>
+ splitArray(getElems(ad(0)))
+ }
+
+ def hasNext = (subIter ne null) || depth >= 0
+ def next: T = {
+ if (subIter ne null) {
+ val el = subIter.next
+ if (!subIter.hasNext)
+ subIter = null
+ el
+ } else
+ next0(arrayD, posD)
+ }
+
+ @tailrec private[this] def next0(elems: Array[Iterable[T]], i: Int): T = {
+ if (i == elems.length-1) { // reached end of level, pop stack
+ depth -= 1
+ if (depth >= 0) {
+ arrayD = arrayStack(depth)
+ posD = posStack(depth)
+ arrayStack(depth) = null
+ } else {
+ arrayD = null
+ posD = 0
+ }
+ } else
+ posD += 1
+
+ val m = elems(i)
+
+ // Note: this block is over twice as fast written this way as it is
+ // as a pattern match. Haven't started looking into why that is, but
+ // it's pretty sad the pattern matcher is that much slower.
+ if (isContainer(m))
+ getElem(m) // push current pos onto stack and descend
+ else if (isTrie(m)) {
+ if (depth >= 0) {
+ arrayStack(depth) = arrayD
+ posStack(depth) = posD
+ }
+ depth += 1
+ arrayD = getElems(m)
+ posD = 0
+ next0(getElems(m), 0)
+ }
+ else {
+ subIter = m.iterator
+ next
+ }
+ // The much slower version:
+ //
+ // m match {
+ // case _: HashMap1[_, _] | _: HashSet1[_] =>
+ // getElem(m) // push current pos onto stack and descend
+ // case _: HashTrieMap[_,_] | _: HashTrieSet[_] =>
+ // if (depth >= 0) {
+ // arrayStack(depth) = arrayD
+ // posStack(depth) = posD
+ // }
+ // depth += 1
+ // arrayD = getElems(m)
+ // posD = 0
+ // next0(getElems(m), 0)
+ // case _ =>
+ // subIter = m.iterator
+ // next
+ // }
+ }
+
+ // assumption: contains 2 or more elements
+ // splits this iterator into 2 iterators
+ // returns the 1st iterator, its number of elements, and the second iterator
+ def split: SplitIterators = {
+ // 0) simple case: no elements have been iterated - simply divide arrayD
+ if (arrayD != null && depth == 0 && posD == 0)
+ return splitArray(arrayD)
+
+ // otherwise, some elements have been iterated over
+ // 1) collision case: if we have a subIter, we return subIter and elements after it
+ if (subIter ne null) {
+ val buff = subIter.toBuffer
+ subIter = null
+ ((buff.iterator, buff.length), this)
+ }
+ else {
+ // otherwise find the topmost array stack element
+ if (depth > 0) {
+ // 2) topmost comes before (is not) arrayD
+ // steal a portion of top to create a new iterator
+ val topmost = arrayStack(0)
+ if (posStack(0) == arrayStack(0).length - 1) {
+ // 2a) only a single entry left on top
+ // this means we have to modify this iterator - pop topmost
+ val snd = Array[Iterable[T]](arrayStack(0).last)
+ val szsnd = snd(0).size
+ // modify this - pop
+ depth -= 1
+ 1 until arrayStack.length foreach (i => arrayStack(i - 1) = arrayStack(i))
+ arrayStack(arrayStack.length - 1) = Array[Iterable[T]](null)
+ posStack = posStack.tail ++ Array[Int](0)
+ // we know that `this` is not empty, since it had something on the arrayStack and arrayStack elements are always non-empty
+ ((newIterator(snd), szsnd), this)
+ } else {
+ // 2b) more than a single entry left on top
+ val (fst, snd) = arrayStack(0).splitAt(arrayStack(0).length - (arrayStack(0).length - posStack(0) + 1) / 2)
+ arrayStack(0) = fst
+ (iteratorWithSize(snd), this)
+ }
+ } else {
+ // 3) no topmost element (arrayD is at the top)
+ // steal a portion of it and update this iterator
+ if (posD == arrayD.length - 1) {
+ // 3a) positioned at the last element of arrayD
+ val m = arrayD(posD)
+ arrayToIterators(
+ if (isTrie(m)) getElems(m)
+ else collisionToArray(m)
+ )
+ }
+ else {
+ // 3b) arrayD has more free elements
+ val (fst, snd) = arrayD.splitAt(arrayD.length - (arrayD.length - posD + 1) / 2)
+ arrayD = fst
+ (iteratorWithSize(snd), this)
+ }
+ }
+ }
+ }
+} \ No newline at end of file