Skip to content

Commit f3834c8

Browse files
author
Abduqodiri Qurbonzoda
committed
Make PersistentHashSetBuilder iterator fail-fast
1 parent 29a099b commit f3834c8

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableSet/PersistentHashSetBuilder.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ internal class Marker
2323
internal class PersistentHashSetBuilder<E>(private var set: PersistentHashSet<E>) : AbstractMutableSet<E>(), PersistentSet.Builder<E> {
2424
internal var marker = Marker()
2525
internal var node = set.node
26+
internal var modCount = 0
27+
28+
// Size change implies structural changes.
2629
override var size = set.size
30+
set(value) {
31+
field = value
32+
modCount++
33+
}
2734

2835
override fun build(): PersistentSet<E> {
2936
set = if (node === set.node) {

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableSet/PersistentHashSetMutableIterator.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,32 @@ package kotlinx.collections.immutable.implementations.immutableSet
1818

1919
internal class PersistentHashSetMutableIterator<E>(private val builder: PersistentHashSetBuilder<E>)
2020
: PersistentHashSetIterator<E>(builder.node), MutableIterator<E> {
21-
var lastReturned: E? = null
22-
var nextWasInvoked = false
21+
private var lastIteratedElement: E? = null
22+
private var nextWasInvoked = false
23+
private var expectedModCount = builder.modCount
2324

2425
override fun next(): E {
26+
checkForComodification()
2527
val next = super.next()
26-
lastReturned = next
28+
lastIteratedElement = next
2729
nextWasInvoked = true
2830
return next
2931
}
3032

3133
override fun remove() {
32-
if (!nextWasInvoked) {
33-
throw NoSuchElementException()
34-
}
34+
checkNextWasInvoked()
3535
if (hasNext()) {
3636
val currentElement = currentElement()
3737

38-
assert(builder.remove(lastReturned))
38+
assert(builder.remove(lastIteratedElement))
3939
resetPath(currentElement.hashCode(), builder.node, currentElement, 0)
4040
} else {
41-
assert(builder.remove(lastReturned))
41+
assert(builder.remove(lastIteratedElement))
4242
}
4343

44-
lastReturned = null
44+
lastIteratedElement = null
4545
nextWasInvoked = false
46+
expectedModCount = builder.modCount
4647
}
4748

4849
private fun resetPath(hashCode: Int, node: TrieNode<*>, element: E, pathIndex: Int) {
@@ -63,12 +64,22 @@ internal class PersistentHashSetMutableIterator<E>(private val builder: Persiste
6364
if (cell is TrieNode<*>) {
6465
resetPath(hashCode, cell, element, pathIndex + 1)
6566
} else {
66-
assert(cell == element)
67+
// assert(cell == element)
6768
pathLastIndex = pathIndex
6869
}
6970
}
7071

7172
private fun isCollision(node: TrieNode<*>): Boolean {
7273
return node.bitmap == 0
7374
}
75+
76+
private fun checkNextWasInvoked() {
77+
if (!nextWasInvoked)
78+
throw IllegalStateException()
79+
}
80+
81+
private fun checkForComodification() {
82+
if (builder.modCount != expectedModCount)
83+
throw ConcurrentModificationException()
84+
}
7485
}

0 commit comments

Comments
 (0)