Skip to content

Commit fa980d9

Browse files
author
Abduqodiri Qurbonzoda
committed
Implement PersistentHashSet
1 parent 24ea59b commit fa980d9

File tree

7 files changed

+1251
-0
lines changed

7 files changed

+1251
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kotlinx.collections.immutable.implementations.immutableSet
18+
19+
import kotlinx.collections.immutable.PersistentSet
20+
import kotlinx.collections.immutable.mutate
21+
22+
internal class PersistentHashSet<E>(private val node: TrieNode<E>,
23+
override val size: Int): AbstractSet<E>(), PersistentSet<E> {
24+
override fun contains(element: E): Boolean {
25+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
26+
return node.contains(hashCode, element, 0)
27+
}
28+
29+
override fun add(element: E): PersistentSet<E> {
30+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
31+
val newNode = node.add(hashCode, element, 0)
32+
if (node === newNode) { return this }
33+
return PersistentHashSet(newNode, size + 1)
34+
}
35+
36+
override fun addAll(elements: Collection<E>): PersistentSet<E> {
37+
return this.mutate { it.addAll(elements) }
38+
}
39+
40+
override fun remove(element: E): PersistentSet<E> {
41+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
42+
val newNode = node.remove(hashCode, element, 0)
43+
if (node === newNode) { return this }
44+
if (newNode == null) { return persistentHashSetOf() }
45+
return PersistentHashSet(newNode, size - 1)
46+
}
47+
48+
override fun removeAll(elements: Collection<E>): PersistentSet<E> {
49+
return mutate { it.removeAll(elements) }
50+
}
51+
52+
override fun removeAll(predicate: (E) -> Boolean): PersistentSet<E> {
53+
return mutate { it.removeAll(predicate) }
54+
}
55+
56+
override fun clear(): PersistentSet<E> {
57+
return persistentHashSetOf()
58+
}
59+
60+
override fun iterator(): Iterator<E> {
61+
return PersistentHashSetIterator(node)
62+
}
63+
64+
override fun builder(): PersistentSet.Builder<E> {
65+
return PersistentHashSetBuilder(node, size)
66+
}
67+
68+
internal companion object {
69+
internal val EMPTY = PersistentHashSet(TrieNode.EMPTY, 0)
70+
}
71+
}
72+
73+
fun <E> persistentHashSetOf(): PersistentSet<E> {
74+
return PersistentHashSet.EMPTY
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kotlinx.collections.immutable.implementations.immutableSet
18+
19+
import kotlinx.collections.immutable.PersistentSet
20+
21+
class Marker
22+
23+
internal class PersistentHashSetBuilder<E>(var node: TrieNode<E>,
24+
override var size: Int) : AbstractMutableSet<E>(), PersistentSet.Builder<E> {
25+
internal var marker = Marker()
26+
27+
override fun build(): PersistentSet<E> {
28+
marker = Marker()
29+
return PersistentHashSet(node, size)
30+
}
31+
32+
override fun contains(element: E): Boolean {
33+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
34+
return node.contains(hashCode, element, 0)
35+
}
36+
37+
override fun add(element: E): Boolean {
38+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
39+
node = node.makeMutableFor(this)
40+
return node.mutableAdd(hashCode, element, 0, this)
41+
}
42+
43+
override fun remove(element: E): Boolean {
44+
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
45+
node = node.makeMutableFor(this)
46+
return node.mutableRemove(hashCode, element, 0, this)
47+
}
48+
49+
override fun clear() {
50+
node = TrieNode.EMPTY as TrieNode<E>
51+
size = 0
52+
}
53+
54+
override fun iterator(): MutableIterator<E> {
55+
return PersistentHashSetMutableIterator(this)
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kotlinx.collections.immutable.implementations.immutableSet
18+
19+
internal open class PersistentHashSetIterator<E>(node: TrieNode<E>) : Iterator<E> {
20+
protected val path = mutableListOf(TrieNodeIterator<E>())
21+
protected var pathLastIndex = 0
22+
private var hasNext = true
23+
24+
init {
25+
path[0].reset(node.buffer)
26+
pathLastIndex = 0
27+
ensureNextElementIsReady()
28+
}
29+
30+
private fun moveToNextNodeWithData(pathIndex: Int): Int {
31+
if (path[pathIndex].hasNextElement()) {
32+
return pathIndex
33+
}
34+
if (path[pathIndex].hasNextNode()) {
35+
val node = path[pathIndex].currentNode()
36+
37+
if (pathIndex + 1 == path.size) {
38+
path.add(TrieNodeIterator())
39+
}
40+
path[pathIndex + 1].reset(node.buffer)
41+
return moveToNextNodeWithData(pathIndex + 1)
42+
}
43+
return -1
44+
}
45+
46+
private fun ensureNextElementIsReady() {
47+
if (path[pathLastIndex].hasNextElement()) {
48+
return
49+
}
50+
for(i in pathLastIndex downTo 0) {
51+
var result = moveToNextNodeWithData(i)
52+
53+
if (result == -1 && path[i].hasNextCell()) {
54+
path[i].moveToNextCell()
55+
result = moveToNextNodeWithData(i)
56+
}
57+
if (result != -1) {
58+
pathLastIndex = result
59+
return
60+
}
61+
if (i > 0) {
62+
path[i - 1].moveToNextCell()
63+
}
64+
}
65+
hasNext = false
66+
}
67+
68+
override fun hasNext(): Boolean {
69+
return hasNext
70+
}
71+
72+
override fun next(): E {
73+
assert(hasNext())
74+
75+
val result = path[pathLastIndex].nextElement()
76+
ensureNextElementIsReady()
77+
return result
78+
}
79+
80+
protected fun currentElement(): E {
81+
assert(hasNext())
82+
return path[pathLastIndex].currentElement()
83+
}
84+
}
85+
86+
internal class TrieNodeIterator<out E> {
87+
private var buffer = emptyArray<Any?>()
88+
private var index = 0
89+
90+
fun reset(buffer: Array<Any?>, index: Int = 0) {
91+
this.buffer = buffer
92+
this.index = index
93+
}
94+
95+
fun hasNextCell(): Boolean {
96+
return index < buffer.size
97+
}
98+
99+
fun moveToNextCell() {
100+
assert(hasNextCell())
101+
index++
102+
}
103+
104+
fun hasNextElement(): Boolean {
105+
return hasNextCell() && buffer[index] !is TrieNode<*>
106+
}
107+
108+
fun currentElement(): E {
109+
assert(hasNextElement())
110+
return buffer[index] as E
111+
}
112+
113+
fun nextElement(): E {
114+
assert(hasNextElement())
115+
return buffer[index++] as E
116+
}
117+
118+
fun hasNextNode(): Boolean {
119+
return hasNextCell() && buffer[index] is TrieNode<*>
120+
}
121+
122+
fun currentNode(): TrieNode<out E> {
123+
assert(hasNextNode())
124+
return buffer[index] as TrieNode<E>
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kotlinx.collections.immutable.implementations.immutableSet
18+
19+
internal class PersistentHashSetMutableIterator<E>(private val builder: PersistentHashSetBuilder<E>)
20+
: PersistentHashSetIterator<E>(builder.node), MutableIterator<E> {
21+
var lastReturned: E? = null
22+
var nextWasInvoked = false
23+
24+
override fun next(): E {
25+
val next = super.next()
26+
lastReturned = next
27+
nextWasInvoked = true
28+
return next
29+
}
30+
31+
override fun remove() {
32+
if (!nextWasInvoked) {
33+
throw NoSuchElementException()
34+
}
35+
if (hasNext()) {
36+
val currentElement = currentElement()
37+
38+
assert(builder.remove(lastReturned))
39+
resetPath(currentElement?.hashCode() ?: NULL_HASH_CODE, builder.node, currentElement, 0)
40+
} else {
41+
assert(builder.remove(lastReturned))
42+
}
43+
44+
lastReturned = null
45+
nextWasInvoked = false
46+
}
47+
48+
private fun resetPath(hashCode: Int, node: TrieNode<*>, element: E, pathIndex: Int) {
49+
if (isCollision(node)) {
50+
val index = node.buffer.indexOf(element)
51+
assert(index != -1)
52+
path[pathIndex].reset(node.buffer, index)
53+
pathLastIndex = pathIndex
54+
return
55+
}
56+
57+
val position = 1 shl ((hashCode shr (pathIndex * LOG_MAX_BRANCHING_FACTOR)) and MAX_BRANCHING_FACTOR_MINUS_ONE)
58+
val index = Integer.bitCount(node.bitmap and (position - 1))
59+
60+
path[pathIndex].reset(node.buffer, index)
61+
62+
val cell = node.buffer[index]
63+
if (cell is TrieNode<*>) {
64+
resetPath(hashCode, cell, element, pathIndex + 1)
65+
} else {
66+
assert(cell == element)
67+
pathLastIndex = pathIndex
68+
}
69+
}
70+
71+
private fun isCollision(node: TrieNode<*>): Boolean {
72+
return node.bitmap == 0
73+
}
74+
}

0 commit comments

Comments
 (0)