From 5e2c1761fc00cfa956a126b32549e9ecf1e65244 Mon Sep 17 00:00:00 2001 From: Alexander Korepanov Date: Thu, 22 Jun 2023 15:01:13 +0200 Subject: [PATCH] [JS IR] Implement InternalStringLinkedMap ^KT-59001 --- .../collections/InternalStringLinkedMap.kt | 142 ++++++++++++++++++ .../kotlin/collections/InternalStringMap.kt | 28 ++-- .../src/kotlin/collections/LinkedHashMap.kt | 2 +- .../src/kotlin/collections/LinkedHashSet.kt | 2 +- 4 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 libraries/stdlib/js/src/kotlin/collections/InternalStringLinkedMap.kt diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalStringLinkedMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalStringLinkedMap.kt new file mode 100644 index 00000000000..d2282d5d55a --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/collections/InternalStringLinkedMap.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.collections + +internal class InternalStringLinkedMap : InternalStringMap() { + companion object { + private const val EMPTY_INDEX = -1 + private const val INITIAL_CAPACITY = 8 + } + + private var nextIndexes = IntArray(INITIAL_CAPACITY) + private var prevIndexes = IntArray(INITIAL_CAPACITY) + + private var headIndex = EMPTY_INDEX + private var tailIndex = EMPTY_INDEX + + private fun ensureCapacity() { + if (nextIndexes.size < size) { + val newCapacity = AbstractList.newCapacity(nextIndexes.size, size) + nextIndexes = nextIndexes.copyOf(newCapacity) + prevIndexes = prevIndexes.copyOf(newCapacity) + } + } + + override fun put(key: K, value: V): V? { + val prevValue = super.put(key, value) + // new element inserted + if (prevValue == null) { + ensureCapacity() + if (size == 1) { + headIndex = 0 + tailIndex = 0 + nextIndexes[0] = EMPTY_INDEX + prevIndexes[0] = EMPTY_INDEX + } else { + val oldTail = tailIndex + tailIndex = size - 1 + + nextIndexes[oldTail] = size - 1 + prevIndexes[tailIndex] = oldTail + nextIndexes[tailIndex] = EMPTY_INDEX + } + } + return prevValue + } + + override fun removeKeyIndex(key: K, removingIndex: Int) { + super.removeKeyIndex(key, removingIndex) + + // remove removedIndex from the list + val nextIndex = nextIndexes[removingIndex] + val prevIndex = prevIndexes[removingIndex] + + nextIndexes[prevIndex] = nextIndex + prevIndexes[nextIndex] = prevIndex + + if (headIndex == removingIndex) { + headIndex = nextIndex + } + if (tailIndex == removingIndex) { + tailIndex = prevIndex + } + + // if the removed index from the middle, we have to move the last index on its place + val lastIndex = size + if (removingIndex != lastIndex) { + nextIndexes[removingIndex] = nextIndexes[lastIndex] + prevIndexes[removingIndex] = prevIndexes[lastIndex] + nextIndexes[prevIndexes[removingIndex]] = removingIndex + prevIndexes[nextIndexes[removingIndex]] = removingIndex + if (headIndex == lastIndex) { + headIndex = removingIndex + } + if (tailIndex == lastIndex) { + tailIndex = removingIndex + } + } + } + + override fun clear() { + super.clear() + headIndex = EMPTY_INDEX + tailIndex = EMPTY_INDEX + + nextIndexes = IntArray(INITIAL_CAPACITY) + prevIndexes = IntArray(INITIAL_CAPACITY) + } + + override fun keysIterator(): MutableIterator = KeysLinkedItr(this) + override fun valuesIterator(): MutableIterator = ValuesLinkedItr(this) + override fun entriesIterator(): MutableIterator> = EntriesLinkedItr(this) + + private abstract class BaseLinkedItr(protected val map: InternalStringLinkedMap) { + protected var lastIndex = EMPTY_INDEX + protected var index = map.headIndex + + protected fun goNext() { + if (index == EMPTY_INDEX) { + throw NoSuchElementException() + } + lastIndex = index + index = map.nextIndexes[index] + } + + fun hasNext(): Boolean = index != EMPTY_INDEX + + fun remove() { + map.removeKeyIndex(map.keys.getElement(lastIndex), lastIndex) + if (index == map.size) { + index = lastIndex + } + lastIndex = EMPTY_INDEX + } + } + + private abstract class LinkedItr( + private val iterableArray: JsRawArray, + map: InternalStringLinkedMap, + ) : MutableIterator, BaseLinkedItr(map) { + override fun next(): I { + goNext() + return iterableArray.getElement(lastIndex) + } + } + + private class KeysLinkedItr(map: InternalStringLinkedMap) : LinkedItr(map.keys, map) + private class ValuesLinkedItr(map: InternalStringLinkedMap) : LinkedItr(map.values, map) + + private class EntriesLinkedItr(map: InternalStringLinkedMap) : + MutableIterator>, + BaseLinkedItr(map) { + override fun next(): MutableMap.MutableEntry { + goNext() + val key = map.keys.getElement(lastIndex) + val value = map.values.getElement(lastIndex) + return EntryRef(key, value, map) + } + } +} diff --git a/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt b/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt index 85e84782fe5..7cc56a7f14b 100644 --- a/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/InternalStringMap.kt @@ -17,7 +17,7 @@ internal external interface JsRawArray { var length: Int } -private inline fun JsRawArray.getElement(index: Int): E { +internal inline fun JsRawArray.getElement(index: Int): E { return (this.asDynamic()[index]).unsafeCast() } @@ -51,8 +51,8 @@ internal open class InternalStringMap : InternalMap { } private var backingMap: dynamic = createJsMap() - private var values = createJsArray() - private var keys = createJsArray() + internal var values = createJsArray() + internal var keys = createJsArray() override val size: Int get() = keys.length @@ -144,16 +144,16 @@ internal open class InternalStringMap : InternalMap { return removedValue } - internal fun removeKeyIndex(key: K, index: Int) { + internal open fun removeKeyIndex(key: K, removingIndex: Int) { jsDeleteProperty(backingMap.unsafeCast(), key as Any) - if (index + 1 == size) { + if (removingIndex + 1 == size) { keys.pop() values.pop() } else { - keys.replaceElementAtWithLast(index) - values.replaceElementAtWithLast(index) - backingMap[keys.getElement(index)] = index + keys.replaceElementAtWithLast(removingIndex) + values.replaceElementAtWithLast(removingIndex) + backingMap[keys.getElement(removingIndex)] = removingIndex } } @@ -164,11 +164,11 @@ internal open class InternalStringMap : InternalMap { } override fun build() { + // Feel free to implement later if it is required throw UnsupportedOperationException("build method is not implemented") } - override fun checkIsMutable() { - } + override fun checkIsMutable() {} override fun keysIterator(): MutableIterator = KeysItr(this) override fun valuesIterator(): MutableIterator = ValuesItr(this) @@ -189,6 +189,7 @@ internal open class InternalStringMap : InternalMap { fun remove() { map.removeKeyIndex(map.keys.getElement(lastIndex), lastIndex) + index = lastIndex lastIndex = -1 } } @@ -215,7 +216,7 @@ internal open class InternalStringMap : InternalMap { } } - private class EntryRef( + protected class EntryRef( override val key: K, override var value: V, private val map: InternalStringMap, @@ -227,10 +228,7 @@ internal open class InternalStringMap : InternalMap { return prevValue } - override fun equals(other: Any?): Boolean = - other is Map.Entry<*, *> && - other.key == key && - other.value == value + override fun equals(other: Any?): Boolean = other is Map.Entry<*, *> && other.key == key && other.value == value override fun hashCode(): Int = key.hashCode() xor value.hashCode() diff --git a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt index 92b2d2de426..5a9abacf604 100644 --- a/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt +++ b/libraries/stdlib/js/src/kotlin/collections/LinkedHashMap.kt @@ -78,5 +78,5 @@ public actual open class LinkedHashMap : HashMap, MutableMap { * JS object without hashing them. */ public fun linkedStringMapOf(vararg pairs: Pair): LinkedHashMap { - return LinkedHashMap(InternalStringMap()).apply { putAll(pairs) } + return LinkedHashMap(InternalStringLinkedMap()).apply { putAll(pairs) } } diff --git a/libraries/stdlib/js/src/kotlin/collections/LinkedHashSet.kt b/libraries/stdlib/js/src/kotlin/collections/LinkedHashSet.kt index 2e7c3c36c76..fdd7edbdb40 100644 --- a/libraries/stdlib/js/src/kotlin/collections/LinkedHashSet.kt +++ b/libraries/stdlib/js/src/kotlin/collections/LinkedHashSet.kt @@ -77,5 +77,5 @@ public actual open class LinkedHashSet : HashSet, MutableSet { * which elements the keys as properties of JS object without hashing them. */ public fun linkedStringSetOf(vararg elements: String): LinkedHashSet { - return LinkedHashSet(InternalStringMap()).apply { addAll(elements) } + return LinkedHashSet(InternalStringLinkedMap()).apply { addAll(elements) } }