Skip to content

Commit 1f3fe9e

Browse files
committed
Add immutable HashSet and HashMap to stdlib
1 parent b899f8d commit 1f3fe9e

File tree

3 files changed

+4803
-0
lines changed

3 files changed

+4803
-0
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.collection.immutable
14+
15+
16+
import java.lang.Integer.bitCount
17+
import java.lang.Math.ceil
18+
import java.lang.System.arraycopy
19+
import language.experimental.captureChecking
20+
21+
private[collection] object Node {
22+
final val HashCodeLength = 32
23+
24+
final val BitPartitionSize = 5
25+
26+
final val BitPartitionMask = (1 << BitPartitionSize) - 1
27+
28+
final val MaxDepth = ceil(HashCodeLength.toDouble / BitPartitionSize).toInt
29+
30+
final val BranchingFactor = 1 << BitPartitionSize
31+
32+
final def maskFrom(hash: Int, shift: Int): Int = (hash >>> shift) & BitPartitionMask
33+
34+
final def bitposFrom(mask: Int): Int = 1 << mask
35+
36+
final def indexFrom(bitmap: Int, bitpos: Int): Int = bitCount(bitmap & (bitpos - 1))
37+
38+
final def indexFrom(bitmap: Int, mask: Int, bitpos: Int): Int = if (bitmap == -1) mask else indexFrom(bitmap, bitpos)
39+
40+
}
41+
42+
private[collection] abstract class Node[T <: Node[T]] {
43+
44+
def hasNodes: Boolean
45+
46+
def nodeArity: Int
47+
48+
def getNode(index: Int): T
49+
50+
def hasPayload: Boolean
51+
52+
def payloadArity: Int
53+
54+
def getPayload(index: Int): Any
55+
56+
def getHash(index: Int): Int
57+
58+
def cachedJavaKeySetHashCode: Int
59+
60+
private final def arrayIndexOutOfBounds(as: Array[_], ix:Int): ArrayIndexOutOfBoundsException =
61+
new ArrayIndexOutOfBoundsException(s"$ix is out of bounds (min 0, max ${as.length-1}")
62+
63+
protected final def removeElement(as: Array[Int], ix: Int): Array[Int] = {
64+
if (ix < 0) throw arrayIndexOutOfBounds(as, ix)
65+
if (ix > as.length - 1) throw arrayIndexOutOfBounds(as, ix)
66+
val result = new Array[Int](as.length - 1)
67+
arraycopy(as, 0, result, 0, ix)
68+
arraycopy(as, ix + 1, result, ix, as.length - ix - 1)
69+
result
70+
}
71+
72+
protected final def removeAnyElement(as: Array[Any], ix: Int): Array[Any] = {
73+
if (ix < 0) throw arrayIndexOutOfBounds(as, ix)
74+
if (ix > as.length - 1) throw arrayIndexOutOfBounds(as, ix)
75+
val result = new Array[Any](as.length - 1)
76+
arraycopy(as, 0, result, 0, ix)
77+
arraycopy(as, ix + 1, result, ix, as.length - ix - 1)
78+
result
79+
}
80+
81+
protected final def insertElement(as: Array[Int], ix: Int, elem: Int): Array[Int] = {
82+
if (ix < 0) throw arrayIndexOutOfBounds(as, ix)
83+
if (ix > as.length) throw arrayIndexOutOfBounds(as, ix)
84+
val result = new Array[Int](as.length + 1)
85+
arraycopy(as, 0, result, 0, ix)
86+
result(ix) = elem
87+
arraycopy(as, ix, result, ix + 1, as.length - ix)
88+
result
89+
}
90+
protected final def insertAnyElement(as: Array[Any], ix: Int, elem: Int): Array[Any] = {
91+
if (ix < 0) throw arrayIndexOutOfBounds(as, ix)
92+
if (ix > as.length) throw arrayIndexOutOfBounds(as, ix)
93+
val result = new Array[Any](as.length + 1)
94+
arraycopy(as, 0, result, 0, ix)
95+
result(ix) = elem
96+
arraycopy(as, ix, result, ix + 1, as.length - ix)
97+
result
98+
}
99+
}
100+
101+
/**
102+
* Base class for fixed-stack iterators that traverse a hash-trie. The iterator performs a
103+
* depth-first pre-order traversal, which yields first all payload elements of the current
104+
* node before traversing sub-nodes (left to right).
105+
*
106+
* @tparam T the trie node type we are iterating over
107+
*/
108+
private[immutable] abstract class ChampBaseIterator[T <: Node[T]] {
109+
110+
import Node.MaxDepth
111+
112+
// Note--this code is duplicated to a large extent both in
113+
// ChampBaseReverseIterator and in convert.impl.ChampStepperBase.
114+
// If you change this code, check those also in case they also
115+
// need to be modified.
116+
117+
protected var currentValueCursor: Int = 0
118+
protected var currentValueLength: Int = 0
119+
protected var currentValueNode: T = _
120+
121+
private[this] var currentStackLevel: Int = -1
122+
private[this] var nodeCursorsAndLengths: Array[Int] = _
123+
private[this] var nodes: Array[T] = _
124+
private def initNodes(): Unit = {
125+
if (nodeCursorsAndLengths eq null) {
126+
nodeCursorsAndLengths = new Array[Int](MaxDepth * 2)
127+
nodes = new Array[Node[T]](MaxDepth).asInstanceOf[Array[T]]
128+
}
129+
}
130+
131+
def this(rootNode: T) = {
132+
this()
133+
if (rootNode.hasNodes) pushNode(rootNode)
134+
if (rootNode.hasPayload) setupPayloadNode(rootNode)
135+
}
136+
137+
private final def setupPayloadNode(node: T): Unit = {
138+
currentValueNode = node
139+
currentValueCursor = 0
140+
currentValueLength = node.payloadArity
141+
}
142+
143+
private final def pushNode(node: T): Unit = {
144+
initNodes()
145+
currentStackLevel = currentStackLevel + 1
146+
147+
val cursorIndex = currentStackLevel * 2
148+
val lengthIndex = currentStackLevel * 2 + 1
149+
150+
nodes(currentStackLevel) = node
151+
nodeCursorsAndLengths(cursorIndex) = 0
152+
nodeCursorsAndLengths(lengthIndex) = node.nodeArity
153+
}
154+
155+
private final def popNode(): Unit = {
156+
currentStackLevel = currentStackLevel - 1
157+
}
158+
159+
/**
160+
* Searches for next node that contains payload values,
161+
* and pushes encountered sub-nodes on a stack for depth-first traversal.
162+
*/
163+
private final def searchNextValueNode(): Boolean = {
164+
while (currentStackLevel >= 0) {
165+
val cursorIndex = currentStackLevel * 2
166+
val lengthIndex = currentStackLevel * 2 + 1
167+
168+
val nodeCursor = nodeCursorsAndLengths(cursorIndex)
169+
val nodeLength = nodeCursorsAndLengths(lengthIndex)
170+
171+
if (nodeCursor < nodeLength) {
172+
nodeCursorsAndLengths(cursorIndex) += 1
173+
174+
val nextNode = nodes(currentStackLevel).getNode(nodeCursor)
175+
176+
if (nextNode.hasNodes) { pushNode(nextNode) }
177+
if (nextNode.hasPayload) { setupPayloadNode(nextNode) ; return true }
178+
} else {
179+
popNode()
180+
}
181+
}
182+
183+
return false
184+
}
185+
186+
final def hasNext = (currentValueCursor < currentValueLength) || searchNextValueNode()
187+
188+
}
189+
190+
/**
191+
* Base class for fixed-stack iterators that traverse a hash-trie in reverse order. The base
192+
* iterator performs a depth-first post-order traversal, traversing sub-nodes (right to left).
193+
*
194+
* @tparam T the trie node type we are iterating over
195+
*/
196+
private[immutable] abstract class ChampBaseReverseIterator[T <: Node[T]] {
197+
198+
import Node.MaxDepth
199+
200+
protected var currentValueCursor: Int = -1
201+
protected var currentValueNode: T = _
202+
203+
private[this] var currentStackLevel: Int = -1
204+
private[this] val nodeIndex: Array[Int] = new Array[Int](MaxDepth + 1)
205+
private[this] val nodeStack: Array[T] = new Array[Node[T]](MaxDepth + 1).asInstanceOf[Array[T]]
206+
207+
def this(rootNode: T) = {
208+
this()
209+
pushNode(rootNode)
210+
searchNextValueNode()
211+
}
212+
213+
private final def setupPayloadNode(node: T): Unit = {
214+
currentValueNode = node
215+
currentValueCursor = node.payloadArity - 1
216+
}
217+
218+
private final def pushNode(node: T): Unit = {
219+
currentStackLevel = currentStackLevel + 1
220+
221+
nodeStack(currentStackLevel) = node
222+
nodeIndex(currentStackLevel) = node.nodeArity - 1
223+
}
224+
225+
private final def popNode(): Unit = {
226+
currentStackLevel = currentStackLevel - 1
227+
}
228+
229+
/**
230+
* Searches for rightmost node that contains payload values,
231+
* and pushes encountered sub-nodes on a stack for depth-first traversal.
232+
*/
233+
private final def searchNextValueNode(): Boolean = {
234+
while (currentStackLevel >= 0) {
235+
val nodeCursor = nodeIndex(currentStackLevel) ; nodeIndex(currentStackLevel) = nodeCursor - 1
236+
237+
if (nodeCursor >= 0) {
238+
val nextNode = nodeStack(currentStackLevel).getNode(nodeCursor)
239+
pushNode(nextNode)
240+
} else {
241+
val currNode = nodeStack(currentStackLevel)
242+
popNode()
243+
244+
if (currNode.hasPayload) { setupPayloadNode(currNode) ; return true }
245+
}
246+
}
247+
248+
return false
249+
}
250+
251+
final def hasNext = (currentValueCursor >= 0) || searchNextValueNode()
252+
253+
}

0 commit comments

Comments
 (0)