Skip to content

Commit 48d91c4

Browse files
Abdukodiri KurbonzodaAbduqodiri Qurbonzoda
authored andcommitted
Get rid of premature node copying in set
1 parent 5402dd3 commit 48d91c4

File tree

2 files changed

+79
-56
lines changed

2 files changed

+79
-56
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,17 @@ internal class PersistentHashSetBuilder<E>(private var set: PersistentHashSet<E>
4141
}
4242

4343
override fun add(element: E): Boolean {
44+
val size = this.size
4445
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
45-
node = node.makeMutableFor(this)
46-
return node.mutableAdd(hashCode, element, 0, this)
46+
node = node.mutableAdd(hashCode, element, 0, this)
47+
return size != this.size
4748
}
4849

4950
override fun remove(element: E): Boolean {
51+
val size = this.size
5052
val hashCode = element?.hashCode() ?: NULL_HASH_CODE
51-
node = node.makeMutableFor(this)
52-
return node.mutableRemove(hashCode, element, 0, this)
53+
node = node.mutableRemove(hashCode, element, 0, this) ?: TrieNode.EMPTY as TrieNode<E>
54+
return size != this.size
5355
}
5456

5557
override fun clear() {

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

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,6 @@ internal class TrieNode<E>(var bitmap: Int,
3131

3232
constructor(bitmap: Int, buffer: Array<Any?>) : this(bitmap, buffer, null)
3333

34-
fun makeMutableFor(mutator: PersistentHashSetBuilder<*>): TrieNode<E> {
35-
if (marker === mutator.marker) { return this }
36-
return TrieNode(bitmap, buffer.copyOf(), mutator.marker)
37-
}
38-
39-
private fun ensureMutableBy(mutator: PersistentHashSetBuilder<*>) {
40-
if (marker !== mutator.marker) {
41-
throw IllegalStateException("Markers expected to be same")
42-
}
43-
}
44-
4534
private fun isNullCellAt(position: Int): Boolean {
4635
return bitmap and position == 0
4736
}
@@ -74,12 +63,17 @@ internal class TrieNode<E>(var bitmap: Int,
7463
return TrieNode(bitmap or position, newBuffer)
7564
}
7665

77-
private fun mutableAddElementAt(position: Int, element: E) {
66+
private fun mutableAddElementAt(position: Int, element: E, mutatorMarker: Marker): TrieNode<E> {
7867
// assert(isNullCellAt(position))
7968

8069
val index = indexOfCellAt(position)
81-
buffer = bufferAddElementAtIndex(index, element)
82-
bitmap = bitmap or position
70+
if (marker === mutatorMarker) {
71+
buffer = bufferAddElementAtIndex(index, element)
72+
bitmap = bitmap or position
73+
return this
74+
}
75+
val newBuffer = bufferAddElementAtIndex(index, element)
76+
return TrieNode(bitmap or position, newBuffer, mutatorMarker)
8377
}
8478

8579
private fun updateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<E>): TrieNode<E> {
@@ -90,8 +84,16 @@ internal class TrieNode<E>(var bitmap: Int,
9084
return TrieNode(bitmap, newBuffer)
9185
}
9286

93-
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<E>) {
94-
buffer[nodeIndex] = newNode
87+
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<E>, mutatorMarker: Marker): TrieNode<E> {
88+
// assert(buffer[nodeIndex] !== newNode)
89+
90+
if (marker === mutatorMarker) {
91+
buffer[nodeIndex] = newNode
92+
return this
93+
}
94+
val newBuffer = buffer.copyOf()
95+
newBuffer[nodeIndex] = newNode
96+
return TrieNode(bitmap, newBuffer, mutatorMarker)
9597
}
9698

9799
private fun makeNodeAtIndex(elementIndex: Int, newElementHash: Int, newElement: E,
@@ -112,10 +114,16 @@ internal class TrieNode<E>(var bitmap: Int,
112114
}
113115

114116
private fun mutableMoveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E,
115-
shift: Int, mutator: PersistentHashSetBuilder<*>) {
117+
shift: Int, mutatorMarker: Marker): TrieNode<E> {
116118
// assert(!isNullCellAt(position))
117119

118-
buffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, mutator.marker)
120+
if (marker === mutatorMarker) {
121+
buffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, mutatorMarker)
122+
return this
123+
}
124+
val newBuffer = buffer.copyOf()
125+
newBuffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, mutatorMarker)
126+
return TrieNode(bitmap, newBuffer, mutatorMarker)
119127
}
120128

121129
private fun makeNode(elementHash1: Int, element1: E, elementHash2: Int, element2: E,
@@ -155,10 +163,17 @@ internal class TrieNode<E>(var bitmap: Int,
155163
return TrieNode(bitmap xor position, newBuffer)
156164
}
157165

158-
private fun mutableRemoveCellAtIndex(cellIndex: Int, position: Int) {
166+
private fun mutableRemoveCellAtIndex(cellIndex: Int, position: Int, mutatorMarker: Marker): TrieNode<E>? {
159167
// assert(!isNullCellAt(position))
160-
buffer = bufferRemoveCellAtIndex(cellIndex)
161-
bitmap = bitmap xor position
168+
if (buffer.size == 1) { return null }
169+
170+
if (marker === mutatorMarker) {
171+
buffer = bufferRemoveCellAtIndex(cellIndex)
172+
bitmap = bitmap xor position
173+
return this
174+
}
175+
val newBuffer = bufferRemoveCellAtIndex(cellIndex)
176+
return TrieNode(bitmap xor position, newBuffer, mutatorMarker)
162177
}
163178

164179
private fun collisionRemoveElementAtIndex(i: Int): TrieNode<E>? {
@@ -168,8 +183,15 @@ internal class TrieNode<E>(var bitmap: Int,
168183
return TrieNode(0, newBuffer)
169184
}
170185

171-
private fun mutableCollisionRemoveElementAtIndex(i: Int) {
172-
buffer = bufferRemoveCellAtIndex(i)
186+
private fun mutableCollisionRemoveElementAtIndex(i: Int, mutatorMarker: Marker): TrieNode<E>? {
187+
if (buffer.size == 1) { return null }
188+
189+
if (marker === mutatorMarker) {
190+
buffer = bufferRemoveCellAtIndex(i)
191+
return this
192+
}
193+
val newBuffer = bufferRemoveCellAtIndex(i)
194+
return TrieNode(0, newBuffer, mutatorMarker)
173195
}
174196

175197
private fun collisionContainsElement(element: E): Boolean {
@@ -182,11 +204,15 @@ internal class TrieNode<E>(var bitmap: Int,
182204
return TrieNode(0, newBuffer)
183205
}
184206

185-
private fun mutableCollisionAdd(element: E, mutator: PersistentHashSetBuilder<*>): Boolean {
186-
if (collisionContainsElement(element)) { return false }
207+
private fun mutableCollisionAdd(element: E, mutator: PersistentHashSetBuilder<*>): TrieNode<E> {
208+
if (collisionContainsElement(element)) { return this }
187209
mutator.size++
188-
buffer = bufferAddElementAtIndex(0, element)
189-
return true
210+
if (marker === mutator.marker) {
211+
buffer = bufferAddElementAtIndex(0, element)
212+
return this
213+
}
214+
val newBuffer = bufferAddElementAtIndex(0, element)
215+
return TrieNode(0, newBuffer, mutator.marker)
190216
}
191217

192218
private fun collisionRemove(element: E): TrieNode<E>? {
@@ -197,14 +223,13 @@ internal class TrieNode<E>(var bitmap: Int,
197223
return this
198224
}
199225

200-
private fun mutableCollisionRemove(element: E, mutator: PersistentHashSetBuilder<*>): Boolean {
226+
private fun mutableCollisionRemove(element: E, mutator: PersistentHashSetBuilder<*>): TrieNode<E>? {
201227
val index = buffer.indexOf(element)
202228
if (index != -1) {
203229
mutator.size--
204-
mutableCollisionRemoveElementAtIndex(index)
205-
return true
230+
return mutableCollisionRemoveElementAtIndex(index, mutator.marker)
206231
}
207-
return false
232+
return this
208233
}
209234

210235
fun contains(elementHash: Int, element: E, shift: Int): Boolean {
@@ -249,31 +274,29 @@ internal class TrieNode<E>(var bitmap: Int,
249274
return moveElementToNode(cellIndex, elementHash, element, shift)
250275
}
251276

252-
fun mutableAdd(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): Boolean {
253-
ensureMutableBy(mutator)
277+
fun mutableAdd(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): TrieNode<E> {
254278
val cellPosition = 1 shl ((elementHash shr shift) and MAX_BRANCHING_FACTOR_MINUS_ONE)
255279

256280
if (isNullCellAt(cellPosition)) { // element is absent
257281
mutator.size++
258-
mutableAddElementAt(cellPosition, element)
259-
return true
282+
return mutableAddElementAt(cellPosition, element, mutator.marker)
260283
}
261284

262285
val cellIndex = indexOfCellAt(cellPosition)
263286
if (buffer[cellIndex] is TrieNode<*>) { // element may be in node
264-
val targetNode = nodeAtIndex(cellIndex).makeMutableFor(mutator)
265-
mutableUpdateNodeAtIndex(cellIndex, targetNode)
266-
return if (shift == MAX_SHIFT) {
287+
val targetNode = nodeAtIndex(cellIndex)
288+
val newNode = if (shift == MAX_SHIFT) {
267289
targetNode.mutableCollisionAdd(element, mutator)
268290
} else {
269291
targetNode.mutableAdd(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
270292
}
293+
if (targetNode === newNode) { return this }
294+
return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.marker)
271295
}
272296
// element is directly in buffer
273-
if (element == buffer[cellIndex]) { return false }
297+
if (element == buffer[cellIndex]) { return this }
274298
mutator.size++
275-
mutableMoveElementToNode(cellIndex, elementHash, element, shift, mutator)
276-
return true
299+
return mutableMoveElementToNode(cellIndex, elementHash, element, shift, mutator.marker)
277300
}
278301

279302
fun remove(elementHash: Int, element: E, shift: Int): TrieNode<E>? {
@@ -302,33 +325,31 @@ internal class TrieNode<E>(var bitmap: Int,
302325
return this
303326
}
304327

305-
fun mutableRemove(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): Boolean {
306-
ensureMutableBy(mutator)
328+
fun mutableRemove(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): TrieNode<E>? {
307329
val cellPosition = 1 shl ((elementHash shr shift) and MAX_BRANCHING_FACTOR_MINUS_ONE)
308330

309331
if (isNullCellAt(cellPosition)) { // element is absent
310-
return false
332+
return this
311333
}
312334

313335
val cellIndex = indexOfCellAt(cellPosition)
314336
if (buffer[cellIndex] is TrieNode<*>) { // element may be in node
315-
val targetNode = nodeAtIndex(cellIndex).makeMutableFor(mutator)
316-
mutableUpdateNodeAtIndex(cellIndex, targetNode)
317-
val result = if (shift == MAX_SHIFT) {
337+
val targetNode = nodeAtIndex(cellIndex)
338+
val newNode = if (shift == MAX_SHIFT) {
318339
targetNode.mutableCollisionRemove(element, mutator)
319340
} else {
320341
targetNode.mutableRemove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
321342
}
322-
if (targetNode.buffer.isEmpty()) { mutableRemoveCellAtIndex(cellIndex, cellPosition) }
323-
return result
343+
if (targetNode === newNode) { return this }
344+
if (newNode == null) { return mutableRemoveCellAtIndex(cellIndex, cellPosition, mutator.marker) }
345+
return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.marker)
324346
}
325347
// element is directly in buffer
326348
if (element == buffer[cellIndex]) {
327349
mutator.size--
328-
mutableRemoveCellAtIndex(cellIndex, cellPosition) // check is empty
329-
return true
350+
return mutableRemoveCellAtIndex(cellIndex, cellPosition, mutator.marker) // check is empty
330351
}
331-
return false
352+
return this
332353
}
333354

334355
internal companion object {

0 commit comments

Comments
 (0)