Skip to content

Commit c5f063e

Browse files
author
Abduqodiri Qurbonzoda
committed
Implement map TrieNode canonicalization
1 parent 0738906 commit c5f063e

File tree

1 file changed

+52
-51
lines changed
  • core/commonMain/src/implementations/immutableMap

1 file changed

+52
-51
lines changed

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

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,22 @@ private fun Array<Any?>.replaceEntryWithNode(keyIndex: Int, nodeIndex: Int, newN
4141
return newBuffer
4242
}
4343

44+
private fun <K, V> Array<Any?>.replaceNodeWithEntry(nodeIndex: Int, keyIndex: Int, key: K, value: V): Array<Any?> {
45+
val newBuffer = this.copyOf(this.size + 1)
46+
newBuffer.copyInto(newBuffer, nodeIndex + 2, nodeIndex + 1, this.size)
47+
newBuffer.copyInto(newBuffer, keyIndex + 2, keyIndex , nodeIndex)
48+
newBuffer[keyIndex] = key
49+
newBuffer[keyIndex + 1] = value
50+
return newBuffer
51+
}
52+
4453
private fun Array<Any?>.removeEntryAtIndex(keyIndex: Int): Array<Any?> {
4554
val newBuffer = arrayOfNulls<Any?>(this.size - ENTRY_SIZE)
4655
this.copyInto(newBuffer, endIndex = keyIndex)
4756
this.copyInto(newBuffer, keyIndex, startIndex = keyIndex + ENTRY_SIZE, endIndex = this.size)
4857
return newBuffer
4958
}
5059

51-
private fun Array<Any?>.removeNodeAtIndex(nodeIndex: Int): Array<Any?> {
52-
val newBuffer = arrayOfNulls<Any?>(this.size - 1)
53-
this.copyInto(newBuffer, endIndex = nodeIndex)
54-
this.copyInto(newBuffer, nodeIndex, startIndex = nodeIndex + 1, endIndex = this.size)
55-
return newBuffer
56-
}
57-
5860

5961

6062
internal class TrieNode<K, V>(
@@ -173,17 +175,40 @@ internal class TrieNode<K, V>(
173175
return TrieNode(dataMap, nodeMap, newBuffer, mutator.ownership)
174176
}
175177

176-
private fun updateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>): TrieNode<K, V> {
178+
private fun updateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode<K, V>): TrieNode<K, V> {
177179
// assert(buffer[nodeIndex] !== newNode)
180+
// TODO: check how this changes affect `put` and non-collision `remove` operations performance.
178181

179-
val newBuffer = buffer.copyOf()
182+
val newNodeBuffer = newNode.buffer
183+
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) {
184+
val keyIndex = entryKeyIndex(positionMask)
185+
val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1])
186+
return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer)
187+
}
188+
189+
val newBuffer = buffer.copyOf(buffer.size)
180190
newBuffer[nodeIndex] = newNode
181191
return TrieNode(dataMap, nodeMap, newBuffer)
182192
}
183193

184-
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
194+
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
185195
// assert(buffer[nodeIndex] !== newNode)
186196

197+
val newNodeBuffer = newNode.buffer
198+
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) {
199+
val keyIndex = entryKeyIndex(positionMask)
200+
val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1])
201+
202+
if (ownedBy === owner) {
203+
buffer = newBuffer
204+
dataMap = dataMap xor positionMask
205+
nodeMap = nodeMap xor positionMask
206+
return this
207+
}
208+
209+
return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer)
210+
}
211+
187212
if (ownedBy === owner) {
188213
buffer[nodeIndex] = newNode
189214
return this
@@ -274,6 +299,7 @@ internal class TrieNode<K, V>(
274299

275300
private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode<K, V>? {
276301
// assert(hasEntryAt(positionMask))
302+
// It is possible only when this node is the root node
277303
if (buffer.size == ENTRY_SIZE) return null
278304

279305
val newBuffer = buffer.removeEntryAtIndex(keyIndex)
@@ -315,27 +341,6 @@ internal class TrieNode<K, V>(
315341
return makeCollisionNode(newBuffer, mutator.ownership)
316342
}
317343

318-
private fun removeNodeAtIndex(nodeIndex: Int, positionMask: Int): TrieNode<K, V>? {
319-
// assert(hasNodeAt(positionMask))
320-
if (buffer.size == 1) return null
321-
322-
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
323-
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer)
324-
}
325-
326-
private fun mutableRemoveNodeAtIndex(nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode<K, V>? {
327-
// assert(hasNodeAt(positionMask))
328-
if (buffer.size == 1) return null
329-
330-
if (ownedBy === owner) {
331-
buffer = buffer.removeNodeAtIndex(nodeIndex)
332-
nodeMap = nodeMap xor positionMask
333-
return this
334-
}
335-
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
336-
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer, owner)
337-
}
338-
339344
private fun collisionContainsKey(key: K): Boolean {
340345
val collisionHash = keyAtIndex(0).hashCode()
341346
val keyHash = key.hashCode()
@@ -551,7 +556,7 @@ internal class TrieNode<K, V>(
551556

552557
val targetNode = nodeAtIndex(nodeIndex)
553558
val putResult = targetNode.put(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) ?: return null
554-
return putResult.replaceNode { node -> updateNodeAtIndex(nodeIndex, node) }
559+
return putResult.replaceNode { node -> updateNodeAtIndex(nodeIndex, keyPositionMask, node) }
555560
}
556561

557562
// no entry at this key hash segment
@@ -587,7 +592,7 @@ internal class TrieNode<K, V>(
587592
if (targetNode === newNode) {
588593
return this
589594
}
590-
return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
595+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
591596
}
592597

593598
// key is absent
@@ -615,11 +620,9 @@ internal class TrieNode<K, V>(
615620

616621
val targetNode = nodeAtIndex(nodeIndex)
617622
val newNode = targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR)
618-
return when {
619-
targetNode === newNode -> this
620-
newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask)
621-
else -> updateNodeAtIndex(nodeIndex, newNode)
622-
}
623+
checkNotNull(newNode)
624+
if (targetNode === newNode) return this
625+
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
623626
}
624627

625628
// key is absent
@@ -646,11 +649,11 @@ internal class TrieNode<K, V>(
646649

647650
val targetNode = nodeAtIndex(nodeIndex)
648651
val newNode = targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
649-
return when {
650-
targetNode === newNode -> this
651-
newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership)
652-
else -> mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
652+
checkNotNull(newNode)
653+
if (ownedBy === mutator.ownership || targetNode !== newNode) {
654+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
653655
}
656+
return this
654657
}
655658

656659
// key is absent
@@ -677,11 +680,9 @@ internal class TrieNode<K, V>(
677680

678681
val targetNode = nodeAtIndex(nodeIndex)
679682
val newNode = targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR)
680-
return when {
681-
targetNode === newNode -> this
682-
newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask)
683-
else -> updateNodeAtIndex(nodeIndex, newNode)
684-
}
683+
checkNotNull(newNode)
684+
if (targetNode === newNode) return this
685+
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
685686
}
686687

687688
// key is absent
@@ -708,11 +709,11 @@ internal class TrieNode<K, V>(
708709

709710
val targetNode = nodeAtIndex(nodeIndex)
710711
val newNode = targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
711-
return when {
712-
targetNode === newNode -> this
713-
newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership)
714-
else -> mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
712+
checkNotNull(newNode)
713+
if (ownedBy === mutator.ownership || targetNode !== newNode) {
714+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
715715
}
716+
return this
716717
}
717718

718719
// key is absent

0 commit comments

Comments
 (0)