[JS IR] Improve InternalStringMap iterators & Extend InternalMap interface
^KT-59001
This commit is contained in:
committed by
Space Team
parent
6f0628cb8a
commit
f202865080
@@ -30,7 +30,7 @@ public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K,
|
||||
|
||||
override fun containsEntry(element: Map.Entry<K, V>): Boolean = this@HashMap.containsEntry(element)
|
||||
|
||||
override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = internalMap.iterator()
|
||||
override operator fun iterator(): MutableIterator<MutableEntry<K, V>> = internalMap.entriesIterator()
|
||||
|
||||
override fun removeEntry(element: Map.Entry<K, V>): Boolean {
|
||||
if (contains(element)) {
|
||||
@@ -107,7 +107,7 @@ public actual open class HashMap<K, V> : AbstractMutableMap<K, V>, MutableMap<K,
|
||||
|
||||
actual override fun containsKey(key: K): Boolean = internalMap.contains(key)
|
||||
|
||||
actual override fun containsValue(value: V): Boolean = internalMap.any { it.value == value }
|
||||
actual override fun containsValue(value: V): Boolean = internalMap.containsValue(value)
|
||||
|
||||
private var _entries: MutableSet<MutableMap.MutableEntry<K, V>>? = null
|
||||
actual override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
|
||||
|
||||
@@ -87,7 +87,7 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
|
||||
fun isEmpty(): Boolean = _size == 0
|
||||
fun containsKey(key: K): Boolean = findKey(key) >= 0
|
||||
fun containsValue(value: V): Boolean = findValue(value) >= 0
|
||||
override fun containsValue(value: V): Boolean = findValue(value) >= 0
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
val index = findKey(key)
|
||||
@@ -113,7 +113,7 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun putAll(from: Map<out K, V>) {
|
||||
override fun putAll(from: Map<out K, V>) {
|
||||
checkIsMutable()
|
||||
putAllEntries(from.entries)
|
||||
}
|
||||
@@ -143,10 +143,6 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
length = 0
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> {
|
||||
return entriesIterator()
|
||||
}
|
||||
|
||||
val keys: MutableSet<K>
|
||||
get() {
|
||||
val cur = keysView
|
||||
@@ -211,7 +207,7 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
private val capacity: Int get() = keysArray.size
|
||||
private val hashSize: Int get() = hashArray.size
|
||||
|
||||
internal fun checkIsMutable() {
|
||||
override fun checkIsMutable() {
|
||||
if (isReadOnly) throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
@@ -425,13 +421,18 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun containsEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
override fun containsEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
val index = findKey(entry.key)
|
||||
if (index < 0) return false
|
||||
return valuesArray!![index] == entry.value
|
||||
}
|
||||
|
||||
internal fun getEntry(entry: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? {
|
||||
override fun containsOtherEntry(entry: Map.Entry<*, *>): Boolean {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return containsEntry(entry as Map.Entry<K, V>)
|
||||
}
|
||||
|
||||
override fun getEntry(entry: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? {
|
||||
val index = findKey(entry.key)
|
||||
return if (index < 0 || valuesArray!![index] != entry.value) {
|
||||
null
|
||||
@@ -451,21 +452,6 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
|
||||
private fun contentEquals(other: Map<*, *>): Boolean = _size == other.size && containsAllEntries(other.entries)
|
||||
|
||||
internal fun containsAllEntries(m: Collection<*>): Boolean {
|
||||
val it = m.iterator()
|
||||
while (it.hasNext()) {
|
||||
val entry = it.next()
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST") // todo: get rid of unchecked cast here somehow
|
||||
if (entry == null || !containsEntry(entry as Map.Entry<K, V>))
|
||||
return false
|
||||
} catch (e: ClassCastException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun putEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
val index = addKey(entry.key)
|
||||
val valuesArray = allocateValuesArray()
|
||||
@@ -493,7 +479,7 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
return updated
|
||||
}
|
||||
|
||||
internal fun removeEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
override fun removeEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
checkIsMutable()
|
||||
val index = findKey(entry.key)
|
||||
if (index < 0) return false
|
||||
@@ -502,7 +488,7 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun removeValue(element: V): Boolean {
|
||||
override fun removeValue(element: V): Boolean {
|
||||
checkIsMutable()
|
||||
val index = findValue(element)
|
||||
if (index < 0) return false
|
||||
@@ -510,9 +496,9 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun keysIterator() = KeysItr(this)
|
||||
internal fun valuesIterator() = ValuesItr(this)
|
||||
internal fun entriesIterator() = EntriesItr(this)
|
||||
override fun keysIterator() = KeysItr(this)
|
||||
override fun valuesIterator() = ValuesItr(this)
|
||||
override fun entriesIterator() = EntriesItr(this)
|
||||
|
||||
private companion object {
|
||||
private const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio
|
||||
@@ -638,16 +624,16 @@ internal class InternalHashMap<K, V> private constructor(
|
||||
}
|
||||
|
||||
internal class HashMapKeys<E> internal constructor(
|
||||
private val backing: InternalHashMap<E, *>,
|
||||
private val backing: InternalMap<E, *>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun contains(element: E): Boolean = backing.containsKey(element)
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.contains(element)
|
||||
override fun clear() = backing.clear()
|
||||
override fun add(element: E): Boolean = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<E>): Boolean = throw UnsupportedOperationException()
|
||||
override fun remove(element: E): Boolean = backing.removeKey(element) >= 0
|
||||
override fun remove(element: E): Boolean = backing.remove(element) != null
|
||||
override fun iterator(): MutableIterator<E> = backing.keysIterator()
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
@@ -662,11 +648,11 @@ internal class HashMapKeys<E> internal constructor(
|
||||
}
|
||||
|
||||
internal class HashMapValues<V> internal constructor(
|
||||
val backing: InternalHashMap<*, V>,
|
||||
private val backing: InternalMap<*, V>,
|
||||
) : MutableCollection<V>, AbstractMutableCollection<V>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: V): Boolean = backing.containsValue(element)
|
||||
override fun add(element: V): Boolean = throw UnsupportedOperationException()
|
||||
override fun addAll(elements: Collection<V>): Boolean = throw UnsupportedOperationException()
|
||||
@@ -700,11 +686,11 @@ internal class HashMapValues<V> internal constructor(
|
||||
* See also [KT-42248](https://youtrack.jetbrains.com/issue/KT-42428).
|
||||
*/
|
||||
internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal constructor(
|
||||
val backing: InternalHashMap<K, V>,
|
||||
val backing: InternalMap<K, V>,
|
||||
) : MutableSet<E>, AbstractMutableSet<E>() {
|
||||
|
||||
override val size: Int get() = backing.size
|
||||
override fun isEmpty(): Boolean = backing.isEmpty()
|
||||
override fun isEmpty(): Boolean = backing.size == 0
|
||||
override fun contains(element: E): Boolean = backing.containsEntry(element)
|
||||
protected abstract fun getEntry(element: Map.Entry<K, V>): E?
|
||||
override fun clear() = backing.clear()
|
||||
@@ -725,7 +711,7 @@ internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal
|
||||
}
|
||||
|
||||
internal class HashMapEntrySet<K, V> internal constructor(
|
||||
backing: InternalHashMap<K, V>,
|
||||
backing: InternalMap<K, V>,
|
||||
) : HashMapEntrySetBase<K, V, MutableMap.MutableEntry<K, V>>(backing) {
|
||||
|
||||
override fun getEntry(element: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element)
|
||||
|
||||
@@ -6,22 +6,35 @@
|
||||
package kotlin.collections
|
||||
|
||||
/**
|
||||
* The common interface of [InternalStringMap] and [InternalHashCodeMap].
|
||||
* The common interface of [InternalStringMap] and [InternalHashMap].
|
||||
*/
|
||||
internal interface InternalMap<K, V> : MutableIterable<MutableMap.MutableEntry<K, V>> {
|
||||
internal interface InternalMap<K, V> {
|
||||
val size: Int
|
||||
operator fun contains(key: K): Boolean
|
||||
operator fun get(key: K): V?
|
||||
|
||||
fun put(key: K, value: V): V?
|
||||
fun remove(key: K): V?
|
||||
fun clear(): Unit
|
||||
fun putAll(from: Map<out K, V>)
|
||||
|
||||
fun createJsMap(): dynamic {
|
||||
val result = js("Object.create(null)")
|
||||
// force to switch object representation to dictionary mode
|
||||
result["foo"] = 1
|
||||
jsDeleteProperty(result, "foo")
|
||||
return result
|
||||
operator fun get(key: K): V?
|
||||
fun getEntry(entry: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>?
|
||||
|
||||
operator fun contains(key: K): Boolean
|
||||
fun containsValue(value: V): Boolean
|
||||
fun containsEntry(entry: Map.Entry<K, V>): Boolean
|
||||
fun containsOtherEntry(entry: Map.Entry<*, *>): Boolean
|
||||
|
||||
fun remove(key: K): V?
|
||||
fun removeValue(value: V): Boolean
|
||||
fun removeEntry(entry: Map.Entry<K, V>): Boolean
|
||||
|
||||
fun clear()
|
||||
|
||||
fun keysIterator(): MutableIterator<K>
|
||||
fun valuesIterator(): MutableIterator<V>
|
||||
fun entriesIterator(): MutableIterator<MutableMap.MutableEntry<K, V>>
|
||||
|
||||
fun checkIsMutable() {}
|
||||
|
||||
fun containsAllEntries(m: Collection<Map.Entry<*, *>>): Boolean {
|
||||
return m.all(this::containsOtherEntry)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,26 @@ package kotlin.collections
|
||||
|
||||
import kotlin.collections.MutableMap.MutableEntry
|
||||
|
||||
internal external interface JsRawArray<E> {
|
||||
fun push(element: E)
|
||||
fun pop(): E
|
||||
|
||||
var length: Int
|
||||
}
|
||||
|
||||
private inline fun <E> JsRawArray<E>.getElement(index: Int): E {
|
||||
return (this.asDynamic()[index]).unsafeCast<E>()
|
||||
}
|
||||
|
||||
private inline fun <E> JsRawArray<E>.setElement(index: Int, element: E) {
|
||||
this.asDynamic()[index] = element
|
||||
}
|
||||
|
||||
private inline fun <E> JsRawArray<E>.replaceElementAtWithLast(index: Int) {
|
||||
setElement(index, getElement(length - 1))
|
||||
pop()
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper around JavaScript Map for key type is string.
|
||||
*
|
||||
@@ -17,19 +37,25 @@ import kotlin.collections.MutableMap.MutableEntry
|
||||
* because we want to have it erased to Any? in order not to generate type-safe override bridges for
|
||||
* [get], [contains], [remove] etc, if they ever are generated.
|
||||
*/
|
||||
internal class InternalStringMap<K, V> : InternalMap<K, V> {
|
||||
internal open class InternalStringMap<K, V> : InternalMap<K, V> {
|
||||
private fun createJsMap(): dynamic {
|
||||
val result = js("Object.create(null)")
|
||||
// force to switch object representation to dictionary mode
|
||||
result["foo"] = 1
|
||||
jsDeleteProperty(result.unsafeCast<Any>(), "foo")
|
||||
return result
|
||||
}
|
||||
|
||||
private fun <E> createJsArray(): JsRawArray<E> {
|
||||
return js("[]").unsafeCast<JsRawArray<E>>()
|
||||
}
|
||||
|
||||
private var backingMap: dynamic = createJsMap()
|
||||
override var size: Int = 0
|
||||
private set
|
||||
private var values = createJsArray<V>()
|
||||
private var keys = createJsArray<K>()
|
||||
|
||||
// /**
|
||||
// * A mod count to track 'value' replacements in map to ensure that the 'value' that we have in the
|
||||
// * iterator entry is guaranteed to be still correct.
|
||||
// * This is to optimize for the common scenario where the values are not modified during
|
||||
// * iterations where the entries are never stale.
|
||||
// */
|
||||
// private var valueMod: Int = 0
|
||||
override val size: Int
|
||||
get() = keys.length
|
||||
|
||||
override operator fun contains(key: K): Boolean {
|
||||
if (key !is String) return false
|
||||
@@ -38,77 +64,169 @@ internal class InternalStringMap<K, V> : InternalMap<K, V> {
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
if (key !is String) return null
|
||||
val value = backingMap[key]
|
||||
return if (value !== undefined) value.unsafeCast<V>() else null
|
||||
val index = backingMap[key]
|
||||
return if (index !== undefined) values.getElement(index.unsafeCast<Int>()) else null
|
||||
}
|
||||
|
||||
override fun getEntry(entry: Map.Entry<K, V>): MutableEntry<K, V>? {
|
||||
val key = entry.key as? String ?: return null
|
||||
val index = backingMap[key]
|
||||
if (index === undefined) {
|
||||
return null
|
||||
}
|
||||
val value = values.getElement(index.unsafeCast<Int>())
|
||||
if (value == entry.value) {
|
||||
return EntryRef(entry.key, value, this)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun containsValue(value: V): Boolean {
|
||||
return values.unsafeCast<Array<V>>().contains(value)
|
||||
}
|
||||
|
||||
override fun containsEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
return getEntry(entry) != null
|
||||
}
|
||||
|
||||
override fun containsOtherEntry(entry: Map.Entry<*, *>): Boolean {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return containsEntry(entry as Map.Entry<K, V>)
|
||||
}
|
||||
|
||||
override fun removeEntry(entry: Map.Entry<K, V>): Boolean {
|
||||
val key = getEntry(entry)?.key ?: return false
|
||||
return remove(key) != null
|
||||
}
|
||||
|
||||
override fun removeValue(value: V): Boolean {
|
||||
val index = values.unsafeCast<Array<V>>().indexOf(value)
|
||||
if (index < 0) {
|
||||
return false
|
||||
}
|
||||
removeKeyIndex(keys.getElement(index), index)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun put(key: K, value: V): V? {
|
||||
require(key is String)
|
||||
val oldValue = backingMap[key]
|
||||
backingMap[key] = value
|
||||
val index = backingMap[key]
|
||||
if (index !== undefined) {
|
||||
val i = index.unsafeCast<Int>()
|
||||
val oldValue = values.getElement(i)
|
||||
values.setElement(i, value)
|
||||
return oldValue
|
||||
}
|
||||
|
||||
if (oldValue === undefined) {
|
||||
size++
|
||||
// structureChanged(host)
|
||||
return null
|
||||
} else {
|
||||
// valueMod++
|
||||
return oldValue.unsafeCast<V>()
|
||||
backingMap[key] = size
|
||||
keys.push(key)
|
||||
values.push(value)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun putAll(from: Map<out K, V>) {
|
||||
for ((key, value) in from) {
|
||||
put(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(key: K): V? {
|
||||
if (key !is String) return null
|
||||
val value = backingMap[key]
|
||||
if (value !== undefined) {
|
||||
jsDeleteProperty(backingMap, key)
|
||||
size--
|
||||
// structureChanged(host)
|
||||
return value.unsafeCast<V>()
|
||||
} else {
|
||||
// valueMod++
|
||||
val index = backingMap[key]
|
||||
if (index === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
val i = index.unsafeCast<Int>()
|
||||
val removedValue = values.getElement(i)
|
||||
|
||||
removeKeyIndex(key, i)
|
||||
return removedValue
|
||||
}
|
||||
|
||||
internal fun removeKeyIndex(key: K, index: Int) {
|
||||
jsDeleteProperty(backingMap.unsafeCast<Any>(), key as Any)
|
||||
|
||||
if (index + 1 == size) {
|
||||
keys.pop()
|
||||
values.pop()
|
||||
} else {
|
||||
keys.replaceElementAtWithLast(index)
|
||||
values.replaceElementAtWithLast(index)
|
||||
backingMap[keys.getElement(index)] = index
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
backingMap = createJsMap()
|
||||
size = 0
|
||||
keys = createJsArray()
|
||||
values = createJsArray()
|
||||
}
|
||||
|
||||
override fun keysIterator(): MutableIterator<K> = KeysItr(this)
|
||||
override fun valuesIterator(): MutableIterator<V> = ValuesItr(this)
|
||||
override fun entriesIterator(): MutableIterator<MutableEntry<K, V>> = EntriesItr(this)
|
||||
|
||||
override fun iterator(): MutableIterator<MutableEntry<K, V>> {
|
||||
return object : MutableIterator<MutableEntry<K, V>> {
|
||||
private val keys: Array<String> = js("Object").keys(backingMap)
|
||||
private val iterator = keys.iterator()
|
||||
private var lastKey: String? = null
|
||||
private abstract class BaseItr<K, V>(protected val map: InternalStringMap<K, V>) {
|
||||
protected var lastIndex = -1
|
||||
protected var index = 0
|
||||
|
||||
override fun hasNext(): Boolean = iterator.hasNext()
|
||||
|
||||
override fun next(): MutableEntry<K, V> {
|
||||
val key = iterator.next()
|
||||
lastKey = key
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return newMapEntry(key as K)
|
||||
protected fun goNext() {
|
||||
if (index >= map.size) {
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
lastIndex = index++
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this@InternalStringMap.remove(checkNotNull(lastKey) as K)
|
||||
}
|
||||
fun hasNext(): Boolean = index < map.size
|
||||
|
||||
fun remove() {
|
||||
map.removeKeyIndex(map.keys.getElement(lastIndex), lastIndex)
|
||||
lastIndex = -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun newMapEntry(key: K): MutableEntry<K, V> = object : MutableEntry<K, V> {
|
||||
override val key: K get() = key
|
||||
override val value: V get() = this@InternalStringMap[key].unsafeCast<V>()
|
||||
private abstract class Itr<I, K, V>(
|
||||
private val iterableArray: JsRawArray<I>,
|
||||
map: InternalStringMap<K, V>,
|
||||
) : MutableIterator<I>, BaseItr<K, V>(map) {
|
||||
override fun next(): I {
|
||||
goNext()
|
||||
return iterableArray.getElement(lastIndex)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setValue(newValue: V): V = this@InternalStringMap.put(key, newValue).unsafeCast<V>()
|
||||
private class KeysItr<K, V>(map: InternalStringMap<K, V>) : Itr<K, K, V>(map.keys, map)
|
||||
private class ValuesItr<K, V>(map: InternalStringMap<K, V>) : Itr<V, K, V>(map.values, map)
|
||||
|
||||
override fun hashCode(): Int = AbstractMap.entryHashCode(this)
|
||||
override fun toString(): String = AbstractMap.entryToString(this)
|
||||
override fun equals(other: Any?): Boolean = AbstractMap.entryEquals(this, other)
|
||||
private class EntriesItr<K, V>(map: InternalStringMap<K, V>) : MutableIterator<MutableEntry<K, V>>, BaseItr<K, V>(map) {
|
||||
override fun next(): MutableEntry<K, V> {
|
||||
goNext()
|
||||
val key = map.keys.getElement(lastIndex)
|
||||
val value = map.values.getElement(lastIndex)
|
||||
return EntryRef(key, value, map)
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryRef<K, V>(
|
||||
override val key: K,
|
||||
override var value: V,
|
||||
private val map: InternalStringMap<K, V>,
|
||||
) : MutableEntry<K, V> {
|
||||
override fun setValue(newValue: V): V {
|
||||
val prevValue = value
|
||||
map.put(key, newValue)
|
||||
value = newValue
|
||||
return prevValue
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
override fun toString(): String = "$key=$value"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user