Skip to content

Commit 3a10997

Browse files
Abdukodiri KurbonzodaAbduqodiri Qurbonzoda
authored andcommitted
Get rid of premature node copying in map
1 parent 48d91c4 commit 3a10997

File tree

2 files changed

+106
-69
lines changed

2 files changed

+106
-69
lines changed

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableMap/PersistentHashMapBuilder.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ internal class Marker
2323
internal class PersistentHashMapBuilder<K, V>(private var map: PersistentHashMap<K, V>) : PersistentMap.Builder<K, V>, AbstractMutableMap<K, V>() {
2424
internal var marker = Marker()
2525
internal var node = map.node
26+
internal var operationResult: V? = null
2627
override var size = map.size
2728

2829
override fun build(): PersistentMap<K, V> {
@@ -61,15 +62,17 @@ internal class PersistentHashMapBuilder<K, V>(private var map: PersistentHashMap
6162
}
6263

6364
override fun put(key: K, value: @UnsafeVariance V): V? {
65+
operationResult = null
6466
val keyHash = key?.hashCode() ?: NULL_HASH_CODE
65-
node = node.makeMutableFor(this)
66-
return node.mutablePut(keyHash, key, value, 0, this)
67+
node = node.mutablePut(keyHash, key, value, 0, this)
68+
return operationResult
6769
}
6870

6971
override fun remove(key: K): V? {
72+
operationResult = null
7073
val keyHash = key?.hashCode() ?: NULL_HASH_CODE
71-
node = node.makeMutableFor(this)
72-
return node.mutableRemove(keyHash, key, 0, this)
74+
node = node.mutableRemove(keyHash, key, 0, this) ?: TrieNode.EMPTY as TrieNode<K, V>
75+
return operationResult
7376
}
7477

7578
override fun clear() {

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

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,6 @@ internal class TrieNode<K, V>(var dataMap: Int,
1616

1717
constructor(dataMap: Int, nodeMap: Int, buffer: Array<Any?>) : this(dataMap, nodeMap, buffer, null)
1818

19-
fun makeMutableFor(mutator: PersistentHashMapBuilder<*, *>): TrieNode<K, V> {
20-
if (marker === mutator.marker) { return this }
21-
return TrieNode(dataMap, nodeMap, buffer.copyOf(), mutator.marker)
22-
}
23-
24-
private fun ensureMutableBy(mutator: PersistentHashMapBuilder<*, *>) {
25-
if (marker !== mutator.marker) {
26-
throw IllegalStateException("Markers expected to be same")
27-
}
28-
}
29-
3019
internal fun hasDataAt(position: Int): Boolean {
3120
return dataMap and position != 0
3221
}
@@ -72,12 +61,17 @@ internal class TrieNode<K, V>(var dataMap: Int,
7261
return TrieNode(dataMap or position, nodeMap, newBuffer)
7362
}
7463

75-
private fun mutablePutDataAt(position: Int, key: K, value: V) {
64+
private fun mutablePutDataAt(position: Int, key: K, value: V, mutatorMarker: Marker): TrieNode<K, V> {
7665
// assert(!hasDataAt(position))
7766

7867
val keyIndex = keyDataIndex(position)
79-
buffer = bufferPutDataAtIndex(keyIndex, key, value)
80-
dataMap = dataMap or position
68+
if (marker === mutatorMarker) {
69+
buffer = bufferPutDataAtIndex(keyIndex, key, value)
70+
dataMap = dataMap or position
71+
return this
72+
}
73+
val newBuffer = bufferPutDataAtIndex(keyIndex, key, value)
74+
return TrieNode(dataMap or position, nodeMap, newBuffer, mutatorMarker)
8175
}
8276

8377
private fun updateValueAtIndex(keyIndex: Int, value: V): TrieNode<K, V> {
@@ -88,10 +82,17 @@ internal class TrieNode<K, V>(var dataMap: Int,
8882
return TrieNode(dataMap, nodeMap, newBuffer)
8983
}
9084

91-
private fun mutableUpdateValueAtIndex(keyIndex: Int, value: V): V {
92-
val previousValue = buffer[keyIndex + 1]
93-
buffer[keyIndex + 1] = value
94-
return previousValue as V
85+
private fun mutableUpdateValueAtIndex(keyIndex: Int, value: V, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
86+
// assert(buffer[keyIndex + 1] !== value)
87+
88+
mutator.operationResult = buffer[keyIndex + 1] as V
89+
if (marker === mutator.marker) {
90+
buffer[keyIndex + 1] = value
91+
return this
92+
}
93+
val newBuffer = buffer.copyOf()
94+
newBuffer[keyIndex + 1] = value
95+
return TrieNode(dataMap, nodeMap, newBuffer, mutator.marker)
9596
}
9697

9798
private fun updateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>): TrieNode<K, V> {
@@ -102,8 +103,14 @@ internal class TrieNode<K, V>(var dataMap: Int,
102103
return TrieNode(dataMap, nodeMap, newBuffer)
103104
}
104105

105-
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>) {
106-
buffer[nodeIndex] = newNode
106+
private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode<K, V>, mutatorMarker: Marker): TrieNode<K, V> {
107+
if (marker === mutatorMarker) {
108+
buffer[nodeIndex] = newNode
109+
return this
110+
}
111+
val newBuffer = buffer.copyOf()
112+
newBuffer[nodeIndex] = newNode
113+
return TrieNode(dataMap, nodeMap, newBuffer, mutatorMarker)
107114
}
108115

109116
private fun bufferMoveDataToNode(keyIndex: Int, position: Int, newKeyHash: Int,
@@ -134,13 +141,18 @@ internal class TrieNode<K, V>(var dataMap: Int,
134141
}
135142

136143
private fun mutableMoveDataToNode(keyIndex: Int, position: Int, newKeyHash: Int,
137-
newKey: K, newValue: V, shift: Int, mutator: PersistentHashMapBuilder<*, *>) {
144+
newKey: K, newValue: V, shift: Int, mutatorMarker: Marker): TrieNode<K, V> {
138145
// assert(hasDataAt(position))
139146
// assert(!hasNodeAt(position))
140147

141-
buffer = bufferMoveDataToNode(keyIndex, position, newKeyHash, newKey, newValue, shift, mutator.marker)
142-
dataMap = dataMap xor position
143-
nodeMap = nodeMap or position
148+
if (marker === mutatorMarker) {
149+
buffer = bufferMoveDataToNode(keyIndex, position, newKeyHash, newKey, newValue, shift, mutatorMarker)
150+
dataMap = dataMap xor position
151+
nodeMap = nodeMap or position
152+
return this
153+
}
154+
val newBuffer = bufferMoveDataToNode(keyIndex, position, newKeyHash, newKey, newValue, shift, mutatorMarker)
155+
return TrieNode(dataMap xor position, nodeMap or position, newBuffer, mutatorMarker)
144156
}
145157

146158
private fun makeNode(keyHash1: Int, key1: K, value1: V,
@@ -180,12 +192,18 @@ internal class TrieNode<K, V>(var dataMap: Int,
180192
return TrieNode(dataMap xor position, nodeMap, newBuffer)
181193
}
182194

183-
private fun mutableRemoveDataAtIndex(keyIndex: Int, position: Int): V {
195+
private fun mutableRemoveDataAtIndex(keyIndex: Int, position: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
184196
// assert(hasDataAt(position))
185-
val previousValue = buffer[keyIndex + 1]
186-
buffer = bufferRemoveDataAtIndex(keyIndex)
187-
dataMap = dataMap xor position
188-
return previousValue as V
197+
mutator.operationResult = buffer[keyIndex + 1] as V
198+
if (buffer.size == 2) { return null }
199+
200+
if (marker === mutator.marker) {
201+
buffer = bufferRemoveDataAtIndex(keyIndex)
202+
dataMap = dataMap xor position
203+
return this
204+
}
205+
val newBuffer = bufferRemoveDataAtIndex(keyIndex)
206+
return TrieNode(dataMap xor position, nodeMap, newBuffer, mutator.marker)
189207
}
190208

191209
private fun collisionRemoveDataAtIndex(i: Int): TrieNode<K, V>? {
@@ -195,10 +213,16 @@ internal class TrieNode<K, V>(var dataMap: Int,
195213
return TrieNode(0, 0, newBuffer)
196214
}
197215

198-
private fun mutableCollisionRemoveDataAtIndex(i: Int): V? {
199-
val previousValue = buffer[i + 1]
200-
buffer = bufferRemoveDataAtIndex(i)
201-
return previousValue as V
216+
private fun mutableCollisionRemoveDataAtIndex(i: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
217+
mutator.operationResult = buffer[i + 1] as V
218+
if (buffer.size == 2) { return null }
219+
220+
if (marker === mutator.marker) {
221+
buffer = bufferRemoveDataAtIndex(i)
222+
return this
223+
}
224+
val newBuffer = bufferRemoveDataAtIndex(i)
225+
return TrieNode(0, 0, newBuffer, mutator.marker)
202226
}
203227

204228
private fun bufferRemoveNodeAtIndex(nodeIndex: Int): Array<Any?> {
@@ -216,10 +240,17 @@ internal class TrieNode<K, V>(var dataMap: Int,
216240
return TrieNode(dataMap, nodeMap xor position, newBuffer)
217241
}
218242

219-
private fun mutableRemoveNodeAtIndex(nodeIndex: Int, position: Int) {
243+
private fun mutableRemoveNodeAtIndex(nodeIndex: Int, position: Int, mutatorMarker: Marker): TrieNode<K, V>? {
220244
// assert(hasNodeAt(position))
221-
buffer = bufferRemoveNodeAtIndex(nodeIndex)
222-
nodeMap = nodeMap xor position
245+
if (buffer.size == 1) { return null }
246+
247+
if (marker === mutatorMarker) {
248+
buffer = bufferRemoveNodeAtIndex(nodeIndex)
249+
nodeMap = nodeMap xor position
250+
return this
251+
}
252+
val newBuffer = bufferRemoveNodeAtIndex(nodeIndex)
253+
return TrieNode(dataMap, nodeMap xor position, newBuffer, mutatorMarker)
223254
}
224255

225256
private fun collisionContainsKey(key: K): Boolean {
@@ -253,17 +284,23 @@ internal class TrieNode<K, V>(var dataMap: Int,
253284
return TrieNode(0, 0, newBuffer)
254285
}
255286

256-
private fun mutableCollisionPut(key: K, value: V, mutator: PersistentHashMapBuilder<*, *>): V? {
287+
private fun mutableCollisionPut(key: K, value: V, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
257288
for (i in 0 until buffer.size step ENTRY_SIZE) {
258289
if (key == buffer[i]) {
259-
val previousValue = buffer[i + 1]
260-
buffer[i + 1] = value
261-
return previousValue as V
290+
mutator.operationResult = buffer[i + 1] as V
291+
292+
if (marker === mutator.marker) {
293+
buffer[i + 1] = value
294+
return this
295+
}
296+
val newBuffer = buffer.copyOf()
297+
newBuffer[i + 1] = value
298+
return TrieNode(0, 0, newBuffer, mutator.marker)
262299
}
263300
}
264301
mutator.size++
265-
buffer = bufferPutDataAtIndex(0, key, value)
266-
return null
302+
val newBuffer = bufferPutDataAtIndex(0, key, value)
303+
return TrieNode(0, 0, newBuffer, mutator.marker)
267304
}
268305

269306
private fun collisionRemove(key: K): TrieNode<K, V>? {
@@ -275,14 +312,14 @@ internal class TrieNode<K, V>(var dataMap: Int,
275312
return this
276313
}
277314

278-
private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder<*, *>): V? {
315+
private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
279316
for (i in 0 until buffer.size step ENTRY_SIZE) {
280317
if (key == buffer[i]) {
281318
mutator.size--
282-
return mutableCollisionRemoveDataAtIndex(i)
319+
return mutableCollisionRemoveDataAtIndex(i, mutator)
283320
}
284321
}
285-
return null
322+
return this
286323
}
287324

288325
private fun collisionRemove(key: K, value: V): TrieNode<K, V>? {
@@ -368,36 +405,34 @@ internal class TrieNode<K, V>(var dataMap: Int,
368405
return putDataAt(keyPosition, key, value)
369406
}
370407

371-
fun mutablePut(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder<*, *>): V? {
372-
ensureMutableBy(mutator)
408+
fun mutablePut(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V> {
373409
val keyPosition = 1 shl ((keyHash shr shift) and MAX_BRANCHING_FACTOR_MINUS_ONE)
374410

375411
if (hasDataAt(keyPosition)) { // key is directly in buffer
376412
val keyIndex = keyDataIndex(keyPosition)
377413

378414
if (key == keyAtIndex(keyIndex)) {
379-
return mutableUpdateValueAtIndex(keyIndex, value)
415+
return mutableUpdateValueAtIndex(keyIndex, value, mutator)
380416
}
381417
mutator.size++
382-
mutableMoveDataToNode(keyIndex, keyPosition, keyHash, key, value, shift, mutator)
383-
return null
418+
return mutableMoveDataToNode(keyIndex, keyPosition, keyHash, key, value, shift, mutator.marker)
384419
}
385420
if (hasNodeAt(keyPosition)) { // key is in node
386421
val nodeIndex = keyNodeIndex(keyPosition)
387422

388-
val targetNode = nodeAtIndex(nodeIndex).makeMutableFor(mutator)
389-
mutableUpdateNodeAtIndex(nodeIndex, targetNode)
390-
return if (shift == MAX_SHIFT) {
423+
val targetNode = nodeAtIndex(nodeIndex)
424+
val newNode = if (shift == MAX_SHIFT) {
391425
targetNode.mutableCollisionPut(key, value, mutator)
392426
} else {
393427
targetNode.mutablePut(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
394428
}
429+
if (targetNode === newNode) { return this }
430+
return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.marker)
395431
}
396432

397433
// key is absent
398434
mutator.size++
399-
mutablePutDataAt(keyPosition, key, value)
400-
return null
435+
return mutablePutDataAt(keyPosition, key, value, mutator.marker)
401436
}
402437

403438
fun remove(keyHash: Int, key: K, shift: Int): TrieNode<K, V>? {
@@ -429,35 +464,34 @@ internal class TrieNode<K, V>(var dataMap: Int,
429464
return this
430465
}
431466

432-
fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder<*, *>): V? {
433-
ensureMutableBy(mutator)
467+
fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder<K, V>): TrieNode<K, V>? {
434468
val keyPosition = 1 shl ((keyHash shr shift) and MAX_BRANCHING_FACTOR_MINUS_ONE)
435469

436470
if (hasDataAt(keyPosition)) { // key is directly in buffer
437471
val keyIndex = keyDataIndex(keyPosition)
438472

439473
if (key == keyAtIndex(keyIndex)) {
440474
mutator.size--
441-
return mutableRemoveDataAtIndex(keyIndex, keyPosition)
475+
return mutableRemoveDataAtIndex(keyIndex, keyPosition, mutator)
442476
}
443-
return null
477+
return this
444478
}
445479
if (hasNodeAt(keyPosition)) { // key is in node
446480
val nodeIndex = keyNodeIndex(keyPosition)
447481

448-
val targetNode = nodeAtIndex(nodeIndex).makeMutableFor(mutator)
449-
mutableUpdateNodeAtIndex(nodeIndex, targetNode)
450-
val previousValue = if (shift == MAX_SHIFT) {
482+
val targetNode = nodeAtIndex(nodeIndex)
483+
val newNode = if (shift == MAX_SHIFT) {
451484
targetNode.mutableCollisionRemove(key, mutator)
452485
} else {
453486
targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator)
454487
}
455-
if (targetNode.buffer.isEmpty()) { mutableRemoveNodeAtIndex(nodeIndex, keyPosition) }
456-
return previousValue
488+
if (targetNode === newNode) { return this }
489+
if (newNode == null) { return mutableRemoveNodeAtIndex(nodeIndex, keyPosition, mutator.marker) }
490+
return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.marker)
457491
}
458492

459493
// key is absent
460-
return null
494+
return this
461495
}
462496

463497
fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode<K, V>? {

0 commit comments

Comments
 (0)