Skip to content

Commit b2c46f7

Browse files
Abduqodiri Qurbonzodaqurbonzoda
authored andcommitted
Do not canonicalize MapBuilder's underlying trie
Because it changes iteration order of elements and leads to inconsistency if an iteration is already in progress
1 parent 34652b1 commit b2c46f7

File tree

3 files changed

+78
-51
lines changed

3 files changed

+78
-51
lines changed

core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ internal class PersistentHashMap<K, V>(internal val node: TrieNode<K, V>,
5151
override fun remove(key: K): PersistentHashMap<K, V> {
5252
val newNode = node.remove(key.hashCode(), key, 0)
5353
if (node === newNode) { return this }
54+
if (newNode == null) { return emptyOf() }
5455
return PersistentHashMap(newNode, size - 1)
5556
}
5657

5758
override fun remove(key: K, value: @UnsafeVariance V): PersistentHashMap<K, V> {
5859
val newNode = node.remove(key.hashCode(), key, value, 0)
5960
if (node === newNode) { return this }
61+
if (newNode == null) { return emptyOf() }
6062
return PersistentHashMap(newNode, size - 1)
6163
}
6264

core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ internal class PersistentHashMapBuilder<K, V>(private var map: PersistentHashMap
6464
override fun remove(key: K): V? {
6565
operationResult = null
6666
@Suppress("UNCHECKED_CAST")
67-
node = node.mutableRemove(key.hashCode(), key, 0, this)
67+
node = node.mutableRemove(key.hashCode(), key, 0, this) ?: TrieNode.EMPTY as TrieNode<K, V>
6868
return operationResult
6969
}
7070

7171
fun remove(key: K, value: V): Boolean {
7272
val oldSize = size
7373
@Suppress("UNCHECKED_CAST")
74-
node = node.mutableRemove(key.hashCode(), key, value, 0, this)
74+
node = node.mutableRemove(key.hashCode(), key, value, 0, this) ?: TrieNode.EMPTY as TrieNode<K, V>
7575
return oldSize != size
7676
}
7777

core/commonMain/src/implementations/immutableMap/TrieNode.kt

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ private fun Array<Any?>.removeEntryAtIndex(keyIndex: Int): Array<Any?> {
5858
return newBuffer
5959
}
6060

61+
private fun Array<Any?>.removeNodeAtIndex(nodeIndex: Int): Array<Any?> {
62+
val newBuffer = arrayOfNulls<Any?>(this.size - 1)
63+
this.copyInto(newBuffer, endIndex = nodeIndex)
64+
this.copyInto(newBuffer, nodeIndex, startIndex = nodeIndex + 1, endIndex = this.size)
65+
return newBuffer
66+
}
67+
6168

6269

6370
internal class TrieNode<K, V>(
@@ -194,28 +201,14 @@ internal class TrieNode<K, V>(
194201
}
195202

196203
/** The given [newNode] must not be a part of any persistent map instance. */
197-
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
204+
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
198205
// assert(buffer[nodeIndex] !== newNode)
199206

200-
val newNodeBuffer = newNode.buffer
201-
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) {
202-
if (buffer.size == 1) {
203-
// assert(dataMap == 0 && nodeMap xor positionMask == 0)
204-
newNode.dataMap = nodeMap
205-
return newNode
206-
}
207-
208-
val keyIndex = entryKeyIndex(positionMask)
209-
val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1])
210-
211-
if (ownedBy === owner) {
212-
buffer = newBuffer
213-
dataMap = dataMap xor positionMask
214-
nodeMap = nodeMap xor positionMask
215-
return this
216-
}
217-
218-
return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer)
207+
// nodes (including collision nodes) that have only one entry are upped if they have no siblings
208+
if (buffer.size == 1 && newNode.buffer.size == ENTRY_SIZE && newNode.nodeMap == 0) {
209+
// assert(dataMap == 0 && nodeMap xor positionMask == 0)
210+
newNode.dataMap = nodeMap
211+
return newNode
219212
}
220213

221214
if (ownedBy === owner) {
@@ -227,6 +220,28 @@ internal class TrieNode<K, V>(
227220
return TrieNode(dataMap, nodeMap, newBuffer, owner)
228221
}
229222

223+
private fun removeNodeAtIndex(nodeIndex: Int, positionMask: Int): TrieNode<K, V>? {
224+
// assert(hasNodeAt(positionMask))
225+
if (buffer.size == 1) return null
226+
227+
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
228+
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer)
229+
}
230+
231+
private fun mutableRemoveNodeAtIndex(nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode<K, V>? {
232+
// assert(hasNodeAt(positionMask))
233+
if (buffer.size == 1) return null
234+
235+
if (ownedBy === owner) {
236+
buffer = buffer.removeNodeAtIndex(nodeIndex)
237+
nodeMap = nodeMap xor positionMask
238+
return this
239+
}
240+
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
241+
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer, owner)
242+
}
243+
244+
230245
private fun bufferMoveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int,
231246
newKey: K, newValue: V, shift: Int, owner: MutabilityOwnership?): Array<Any?> {
232247
val storedKey = keyAtIndex(keyIndex)
@@ -290,18 +305,18 @@ internal class TrieNode<K, V>(
290305
return TrieNode(0, 1 shl setBit1, arrayOf<Any?>(node), owner)
291306
}
292307

293-
private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode<K, V> {
308+
private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode<K, V>? {
294309
// assert(hasEntryAt(positionMask))
295-
// assert(buffer.size > ENTRY_SIZE) // can be false only for the root node
310+
if (buffer.size == ENTRY_SIZE) return null
296311
val newBuffer = buffer.removeEntryAtIndex(keyIndex)
297312
return TrieNode(dataMap xor positionMask, nodeMap, newBuffer)
298313
}
299314

300-
private fun mutableRemoveEntryAtIndex(keyIndex: Int, positionMask: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
315+
private fun mutableRemoveEntryAtIndex(keyIndex: Int, positionMask: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
301316
// assert(hasEntryAt(positionMask))
302-
// assert(buffer.size > ENTRY_SIZE)
303317
mutator.size--
304318
mutator.operationResult = valueAtKeyIndex(keyIndex)
319+
if (buffer.size == ENTRY_SIZE) return null
305320

306321
if (ownedBy === mutator.ownership) {
307322
buffer = buffer.removeEntryAtIndex(keyIndex)
@@ -312,16 +327,16 @@ internal class TrieNode<K, V>(
312327
return TrieNode(dataMap xor positionMask, nodeMap, newBuffer, mutator.ownership)
313328
}
314329

315-
private fun collisionRemoveEntryAtIndex(i: Int): TrieNode<K, V> {
316-
// assert(buffer.size > ENTRY_SIZE)
330+
private fun collisionRemoveEntryAtIndex(i: Int): TrieNode<K, V>? {
331+
if (buffer.size == ENTRY_SIZE) return null
317332
val newBuffer = buffer.removeEntryAtIndex(i)
318333
return TrieNode(0, 0, newBuffer)
319334
}
320335

321-
private fun mutableCollisionRemoveEntryAtIndex(i: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
322-
// assert(buffer.size > ENTRY_SIZE)
336+
private fun mutableCollisionRemoveEntryAtIndex(i: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
323337
mutator.size--
324338
mutator.operationResult = valueAtKeyIndex(i)
339+
if (buffer.size == ENTRY_SIZE) return null
325340

326341
if (ownedBy === mutator.ownership) {
327342
buffer = buffer.removeEntryAtIndex(i)
@@ -388,7 +403,7 @@ internal class TrieNode<K, V>(
388403
return TrieNode(0, 0, newBuffer, mutator.ownership)
389404
}
390405

391-
private fun collisionRemove(key: K): TrieNode<K, V> {
406+
private fun collisionRemove(key: K): TrieNode<K, V>? {
392407
for (i in 0 until buffer.size step ENTRY_SIZE) {
393408
if (key == keyAtIndex(i)) {
394409
return collisionRemoveEntryAtIndex(i)
@@ -397,7 +412,7 @@ internal class TrieNode<K, V>(
397412
return this
398413
}
399414

400-
private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
415+
private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
401416
for (i in 0 until buffer.size step ENTRY_SIZE) {
402417
if (key == keyAtIndex(i)) {
403418
return mutableCollisionRemoveEntryAtIndex(i, mutator)
@@ -406,7 +421,7 @@ internal class TrieNode<K, V>(
406421
return this
407422
}
408423

409-
private fun collisionRemove(key: K, value: V): TrieNode<K, V> {
424+
private fun collisionRemove(key: K, value: V): TrieNode<K, V>? {
410425
for (i in 0 until buffer.size step ENTRY_SIZE) {
411426
if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) {
412427
return collisionRemoveEntryAtIndex(i)
@@ -415,7 +430,7 @@ internal class TrieNode<K, V>(
415430
return this
416431
}
417432

418-
private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
433+
private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
419434
for (i in 0 until buffer.size step ENTRY_SIZE) {
420435
if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) {
421436
return mutableCollisionRemoveEntryAtIndex(i, mutator)
@@ -523,15 +538,15 @@ internal class TrieNode<K, V>(
523538
if (targetNode === newNode) {
524539
return this
525540
}
526-
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
541+
return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
527542
}
528543

529544
// key is absent
530545
mutator.size++
531546
return mutableInsertEntryAt(keyPositionMask, key, value, mutator.ownership)
532547
}
533548

534-
fun remove(keyHash: Int, key: K, shift: Int): TrieNode<K, V> {
549+
fun remove(keyHash: Int, key: K, shift: Int): TrieNode<K, V>? {
535550
val keyPositionMask = 1 shl indexSegment(keyHash, shift)
536551

537552
if (hasEntryAt(keyPositionMask)) { // key is directly in buffer
@@ -551,15 +566,23 @@ internal class TrieNode<K, V>(
551566
} else {
552567
targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR)
553568
}
554-
if (targetNode === newNode) return this
555-
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
569+
return replaceNode(targetNode, newNode, nodeIndex, keyPositionMask)
556570
}
557571

558572
// key is absent
559573
return this
560574
}
561575

562-
fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
576+
private fun replaceNode(targetNode: TrieNode<K, V>, newNode: TrieNode<K, V>?, nodeIndex: Int, positionMask: Int) = when {
577+
newNode == null ->
578+
removeNodeAtIndex(nodeIndex, positionMask)
579+
targetNode !== newNode ->
580+
updateNodeAtIndex(nodeIndex, positionMask, newNode)
581+
else ->
582+
this
583+
}
584+
585+
fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
563586
val keyPositionMask = 1 shl indexSegment(keyHash, shift)
564587

565588
if (hasEntryAt(keyPositionMask)) { // key is directly in buffer
@@ -579,17 +602,23 @@ internal class TrieNode<K, V>(
579602
} else {
580603
targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
581604
}
582-
if (ownedBy === mutator.ownership || targetNode !== newNode) {
583-
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
584-
}
585-
return this
605+
return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership)
586606
}
587607

588608
// key is absent
589609
return this
590610
}
591611

592-
fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode<K, V> {
612+
private fun mutableReplaceNode(targetNode: TrieNode<K, V>, newNode: TrieNode<K, V>?, nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership) = when {
613+
newNode == null ->
614+
mutableRemoveNodeAtIndex(nodeIndex, positionMask, owner)
615+
ownedBy === owner || targetNode !== newNode ->
616+
mutableUpdateNodeAtIndex(nodeIndex, newNode, owner)
617+
else ->
618+
this
619+
}
620+
621+
fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode<K, V>? {
593622
val keyPositionMask = 1 shl indexSegment(keyHash, shift)
594623

595624
if (hasEntryAt(keyPositionMask)) { // key is directly in buffer
@@ -609,15 +638,14 @@ internal class TrieNode<K, V>(
609638
} else {
610639
targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR)
611640
}
612-
if (targetNode === newNode) return this
613-
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
641+
return replaceNode(targetNode, newNode, nodeIndex, keyPositionMask)
614642
}
615643

616644
// key is absent
617645
return this
618646
}
619647

620-
fun mutableRemove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
648+
fun mutableRemove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
621649
val keyPositionMask = 1 shl indexSegment(keyHash, shift)
622650

623651
if (hasEntryAt(keyPositionMask)) { // key is directly in buffer
@@ -637,10 +665,7 @@ internal class TrieNode<K, V>(
637665
} else {
638666
targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
639667
}
640-
if (ownedBy === mutator.ownership || targetNode !== newNode) {
641-
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
642-
}
643-
return this
668+
return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership)
644669
}
645670

646671
// key is absent

0 commit comments

Comments
 (0)