Skip to content

Commit 25d9e54

Browse files
author
Abduqodiri Qurbonzoda
committed
Implement map TrieNode canonicalization
1 parent cb23766 commit 25d9e54

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
@@ -42,20 +42,22 @@ private fun Array<Any?>.replaceEntryWithNode(keyIndex: Int, nodeIndex: Int, newN
4242
return newBuffer
4343
}
4444

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

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

6062

6163
internal class TrieNode<K, V>(
@@ -170,17 +172,40 @@ internal class TrieNode<K, V>(
170172
return TrieNode(dataMap, nodeMap, newBuffer, mutator.ownership)
171173
}
172174

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

176-
val newBuffer = buffer.copyOf()
179+
val newNodeBuffer = newNode.buffer
180+
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) {
181+
val keyIndex = entryKeyIndex(positionMask)
182+
val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1])
183+
return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer)
184+
}
185+
186+
val newBuffer = buffer.copyOf(buffer.size)
177187
newBuffer[nodeIndex] = newNode
178188
return TrieNode(dataMap, nodeMap, newBuffer)
179189
}
180190

181-
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
191+
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode<K, V>, owner: MutabilityOwnership): TrieNode<K, V> {
182192
// assert(buffer[nodeIndex] !== newNode)
183193

194+
val newNodeBuffer = newNode.buffer
195+
if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) {
196+
val keyIndex = entryKeyIndex(positionMask)
197+
val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1])
198+
199+
if (ownedBy === owner) {
200+
buffer = newBuffer
201+
dataMap = dataMap xor positionMask
202+
nodeMap = nodeMap xor positionMask
203+
return this
204+
}
205+
206+
return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer)
207+
}
208+
184209
if (ownedBy === owner) {
185210
buffer[nodeIndex] = newNode
186211
return this
@@ -255,6 +280,7 @@ internal class TrieNode<K, V>(
255280

256281
private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode<K, V>? {
257282
// assert(hasEntryAt(positionMask))
283+
// It is possible only when this node is the root node
258284
if (buffer.size == ENTRY_SIZE) return null
259285

260286
val newBuffer = buffer.removeEntryAtIndex(keyIndex)
@@ -296,27 +322,6 @@ internal class TrieNode<K, V>(
296322
return TrieNode(0, 0, newBuffer, mutator.ownership)
297323
}
298324

299-
private fun removeNodeAtIndex(nodeIndex: Int, positionMask: Int): TrieNode<K, V>? {
300-
// assert(hasNodeAt(positionMask))
301-
if (buffer.size == 1) return null
302-
303-
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
304-
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer)
305-
}
306-
307-
private fun mutableRemoveNodeAtIndex(nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode<K, V>? {
308-
// assert(hasNodeAt(positionMask))
309-
if (buffer.size == 1) return null
310-
311-
if (ownedBy === owner) {
312-
buffer = buffer.removeNodeAtIndex(nodeIndex)
313-
nodeMap = nodeMap xor positionMask
314-
return this
315-
}
316-
val newBuffer = buffer.removeNodeAtIndex(nodeIndex)
317-
return TrieNode(dataMap, nodeMap xor positionMask, newBuffer, owner)
318-
}
319-
320325
private fun collisionContainsKey(key: K): Boolean {
321326
for (i in 0 until buffer.size step ENTRY_SIZE) {
322327
if (key == buffer[i]) return true
@@ -473,7 +478,7 @@ internal class TrieNode<K, V>(
473478
} else {
474479
targetNode.put(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) ?: return null
475480
}
476-
return putResult.replaceNode { node -> updateNodeAtIndex(nodeIndex, node) }
481+
return putResult.replaceNode { node -> updateNodeAtIndex(nodeIndex, keyPositionMask, node) }
477482
}
478483

479484
// no entry at this key hash segment
@@ -509,7 +514,7 @@ internal class TrieNode<K, V>(
509514
if (targetNode === newNode) {
510515
return this
511516
}
512-
return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
517+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
513518
}
514519

515520
// key is absent
@@ -537,11 +542,9 @@ internal class TrieNode<K, V>(
537542
} else {
538543
targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR)
539544
}
540-
return when {
541-
targetNode === newNode -> this
542-
newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask)
543-
else -> updateNodeAtIndex(nodeIndex, newNode)
544-
}
545+
checkNotNull(newNode)
546+
if (targetNode === newNode) return this
547+
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
545548
}
546549

547550
// key is absent
@@ -568,11 +571,11 @@ internal class TrieNode<K, V>(
568571
} else {
569572
targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
570573
}
571-
return when {
572-
targetNode === newNode -> this
573-
newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership)
574-
else -> mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
574+
checkNotNull(newNode)
575+
if (ownedBy === mutator.ownership || targetNode !== newNode) {
576+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
575577
}
578+
return this
576579
}
577580

578581
// key is absent
@@ -599,11 +602,9 @@ internal class TrieNode<K, V>(
599602
} else {
600603
targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR)
601604
}
602-
return when {
603-
targetNode === newNode -> this
604-
newNode == null -> removeNodeAtIndex(nodeIndex, keyPositionMask)
605-
else -> updateNodeAtIndex(nodeIndex, newNode)
606-
}
605+
checkNotNull(newNode)
606+
if (targetNode === newNode) return this
607+
return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode)
607608
}
608609

609610
// key is absent
@@ -630,11 +631,11 @@ internal class TrieNode<K, V>(
630631
} else {
631632
targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
632633
}
633-
return when {
634-
targetNode === newNode -> this
635-
newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, keyPositionMask, mutator.ownership)
636-
else -> mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership)
634+
checkNotNull(newNode)
635+
if (ownedBy === mutator.ownership || targetNode !== newNode) {
636+
return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership)
637637
}
638+
return this
638639
}
639640

640641
// key is absent

0 commit comments

Comments
 (0)