[JS IR] Cleanup InternalHashMap

^KT-59001
This commit is contained in:
Alexander Korepanov
2023-06-19 13:52:16 +02:00
committed by Space Team
parent e26c06f0e1
commit 402d5e63fd
2 changed files with 98 additions and 84 deletions
@@ -0,0 +1,23 @@
/*
* 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 fun <T> Array<T>.resetAt(index: Int) {
this.unsafeCast<Array<Any?>>()[index] = null
}
internal fun <T> Array<T>.resetRange(fromIndex: Int, toIndex: Int) {
this.nativeFill(null, fromIndex, toIndex)
}
internal fun <T> Array<T>.copyOfUninitializedElements(newSize: Int): Array<T> {
return this.copyOf(newSize).unsafeCast<Array<T>>()
}
internal fun <T> arrayOfUninitializedElements(capacity: Int): Array<T> {
require(capacity >= 0) { "capacity must be non-negative." }
return arrayOfNulls<Any>(capacity).unsafeCast<Array<T>>()
}
@@ -5,22 +5,18 @@
package kotlin.collections package kotlin.collections
import kotlin.native.concurrent.isFrozen internal class InternalHashMap<K, V> private constructor(
import kotlin.native.FreezingIsDeprecated private var keysArray: Array<K>,
private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet
@OptIn(FreezingIsDeprecated::class) private var presenceArray: IntArray,
actual class HashMap<K, V> private constructor( private var hashArray: IntArray,
private var keysArray: Array<K>, private var maxProbeDistance: Int,
private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet private var length: Int,
private var presenceArray: IntArray, ) {
private var hashArray: IntArray,
private var maxProbeDistance: Int,
private var length: Int
) : MutableMap<K, V> {
private var hashShift: Int = computeShift(hashSize) private var hashShift: Int = computeShift(hashSize)
private var _size: Int = 0 private var _size: Int = 0
override actual val size: Int val size: Int
get() = _size get() = _size
private var keysView: HashMapKeys<K>? = null private var keysView: HashMapKeys<K>? = null
@@ -34,7 +30,7 @@ actual class HashMap<K, V> private constructor(
/** /**
* Creates a new empty [HashMap]. * Creates a new empty [HashMap].
*/ */
actual constructor() : this(INITIAL_CAPACITY) constructor() : this(INITIAL_CAPACITY)
/** /**
* Creates a new empty [HashMap] with the specified initial capacity. * Creates a new empty [HashMap] with the specified initial capacity.
@@ -48,18 +44,19 @@ actual class HashMap<K, V> private constructor(
* *
* @throws IllegalArgumentException if [initialCapacity] is negative. * @throws IllegalArgumentException if [initialCapacity] is negative.
*/ */
actual constructor(initialCapacity: Int) : this( constructor(initialCapacity: Int) : this(
arrayOfUninitializedElements(initialCapacity), arrayOfUninitializedElements(initialCapacity),
null, null,
IntArray(initialCapacity), IntArray(initialCapacity),
IntArray(computeHashSize(initialCapacity)), IntArray(computeHashSize(initialCapacity)),
INITIAL_MAX_PROBE_DISTANCE, INITIAL_MAX_PROBE_DISTANCE,
0) 0
)
/** /**
* Creates a new [HashMap] filled with the contents of the specified [original] map. * Creates a new [HashMap] filled with the contents of the specified [original] map.
*/ */
actual constructor(original: Map<out K, V>) : this(original.size) { constructor(original: Map<out K, V>) : this(original.size) {
putAll(original) putAll(original)
} }
@@ -77,28 +74,28 @@ actual class HashMap<K, V> private constructor(
* *
* @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive. * @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive.
*/ */
actual constructor(initialCapacity: Int, loadFactor: Float) : this(initialCapacity) { constructor(initialCapacity: Int, loadFactor: Float) : this(initialCapacity) {
require(loadFactor > 0) { "Non-positive load factor: $loadFactor" } require(loadFactor > 0) { "Non-positive load factor: $loadFactor" }
} }
@PublishedApi @PublishedApi
internal fun build(): Map<K, V> { internal fun build(): InternalHashMap<K, V> {
checkIsMutable() checkIsMutable()
isReadOnly = true isReadOnly = true
return if (size > 0) this else EmptyHolder.value() return if (size > 0) this else EmptyHolder.value()
} }
override actual fun isEmpty(): Boolean = _size == 0 fun isEmpty(): Boolean = _size == 0
override actual fun containsKey(key: K): Boolean = findKey(key) >= 0 fun containsKey(key: K): Boolean = findKey(key) >= 0
override actual fun containsValue(value: V): Boolean = findValue(value) >= 0 fun containsValue(value: V): Boolean = findValue(value) >= 0
override actual operator fun get(key: K): V? { operator fun get(key: K): V? {
val index = findKey(key) val index = findKey(key)
if (index < 0) return null if (index < 0) return null
return valuesArray!![index] return valuesArray!![index]
} }
override actual fun put(key: K, value: V): V? { fun put(key: K, value: V): V? {
checkIsMutable() checkIsMutable()
val index = addKey(key) val index = addKey(key)
val valuesArray = allocateValuesArray() val valuesArray = allocateValuesArray()
@@ -112,12 +109,12 @@ actual class HashMap<K, V> private constructor(
} }
} }
override actual fun putAll(from: Map<out K, V>) { fun putAll(from: Map<out K, V>) {
checkIsMutable() checkIsMutable()
putAllEntries(from.entries) putAllEntries(from.entries)
} }
override actual fun remove(key: K): V? { fun remove(key: K): V? {
val index = removeKey(key) // mutability gets checked here val index = removeKey(key) // mutability gets checked here
if (index < 0) return null if (index < 0) return null
val valuesArray = valuesArray!! val valuesArray = valuesArray!!
@@ -126,7 +123,7 @@ actual class HashMap<K, V> private constructor(
return oldValue return oldValue
} }
override actual fun clear() { fun clear() {
checkIsMutable() checkIsMutable()
// O(length) implementation for hashArray cleanup // O(length) implementation for hashArray cleanup
for (i in 0..length - 1) { for (i in 0..length - 1) {
@@ -142,35 +139,35 @@ actual class HashMap<K, V> private constructor(
length = 0 length = 0
} }
override actual val keys: MutableSet<K> get() { val keys: MutableSet<K>
val cur = keysView get() {
return if (cur == null) { val cur = keysView
val new = HashMapKeys(this) return if (cur == null) {
if (!isFrozen) val new = HashMapKeys(this)
keysView = new keysView = new
new new
} else cur } else cur
} }
override actual val values: MutableCollection<V> get() { val values: MutableCollection<V>
val cur = valuesView get() {
return if (cur == null) { val cur = valuesView
val new = HashMapValues(this) return if (cur == null) {
if (!isFrozen) val new = HashMapValues(this)
valuesView = new valuesView = new
new new
} else cur } else cur
} }
override actual val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() { val entries: MutableSet<MutableMap.MutableEntry<K, V>>
val cur = entriesView get() {
return if (cur == null) { val cur = entriesView
val new = HashMapEntrySet(this) return if (cur == null) {
if (!isFrozen) val new = HashMapEntrySet(this)
entriesView = new entriesView = new
new new
} else cur } else cur
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return other === this || return other === this ||
@@ -227,7 +224,7 @@ actual class HashMap<K, V> private constructor(
} }
private fun ensureCapacity(minCapacity: Int) { private fun ensureCapacity(minCapacity: Int) {
if (minCapacity < 0) throw OutOfMemoryError() // overflow if (minCapacity < 0) throw RuntimeException("too many elements") // overflow
if (minCapacity > this.capacity) { if (minCapacity > this.capacity) {
val newSize = AbstractList.newCapacity(this.capacity, minCapacity) val newSize = AbstractList.newCapacity(this.capacity, minCapacity)
keysArray = keysArray.copyOfUninitializedElements(newSize) keysArray = keysArray.copyOfUninitializedElements(newSize)
@@ -278,8 +275,9 @@ actual class HashMap<K, V> private constructor(
var i = 0 var i = 0
while (i < length) { while (i < length) {
if (!putRehash(i++)) { if (!putRehash(i++)) {
throw IllegalStateException("This cannot happen with fixed magic multiplier and grow-only hash array. " + throw IllegalStateException(
"Have object hashCodes changed?") "This cannot happen with fixed magic multiplier and grow-only hash array. Have object hashCodes changed?"
)
} }
} }
} }
@@ -508,7 +506,6 @@ actual class HashMap<K, V> private constructor(
internal fun valuesIterator() = ValuesItr(this) internal fun valuesIterator() = ValuesItr(this)
internal fun entriesIterator() = EntriesItr(this) internal fun entriesIterator() = EntriesItr(this)
@kotlin.native.internal.CanBePrecreated
private companion object { private companion object {
private const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio private const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio
private const val INITIAL_CAPACITY = 8 private const val INITIAL_CAPACITY = 8
@@ -521,16 +518,16 @@ actual class HashMap<K, V> private constructor(
} }
internal object EmptyHolder { internal object EmptyHolder {
val value_ = HashMap<Nothing, Nothing>(0).also { it.isReadOnly = true } val value_ = InternalHashMap<Nothing, Nothing>(0).also { it.isReadOnly = true }
fun <K, V> value(): HashMap<K, V> { fun <K, V> value(): InternalHashMap<K, V> {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return value_ as HashMap<K, V> return value_ as InternalHashMap<K, V>
} }
} }
internal open class Itr<K, V>( internal open class Itr<K, V>(
internal val map: HashMap<K, V> internal val map: InternalHashMap<K, V>,
) { ) {
internal var index = 0 internal var index = 0
internal var lastIndex: Int = -1 internal var lastIndex: Int = -1
@@ -553,7 +550,7 @@ actual class HashMap<K, V> private constructor(
} }
} }
internal class KeysItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<K> { internal class KeysItr<K, V>(map: InternalHashMap<K, V>) : Itr<K, V>(map), MutableIterator<K> {
override fun next(): K { override fun next(): K {
if (index >= map.length) throw NoSuchElementException() if (index >= map.length) throw NoSuchElementException()
lastIndex = index++ lastIndex = index++
@@ -564,7 +561,7 @@ actual class HashMap<K, V> private constructor(
} }
internal class ValuesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<V> { internal class ValuesItr<K, V>(map: InternalHashMap<K, V>) : Itr<K, V>(map), MutableIterator<V> {
override fun next(): V { override fun next(): V {
if (index >= map.length) throw NoSuchElementException() if (index >= map.length) throw NoSuchElementException()
lastIndex = index++ lastIndex = index++
@@ -574,8 +571,7 @@ actual class HashMap<K, V> private constructor(
} }
} }
internal class EntriesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), internal class EntriesItr<K, V>(map: InternalHashMap<K, V>) : Itr<K, V>(map), MutableIterator<MutableMap.MutableEntry<K, V>> {
MutableIterator<MutableMap.MutableEntry<K, V>> {
override fun next(): EntryRef<K, V> { override fun next(): EntryRef<K, V> {
if (index >= map.length) throw NoSuchElementException() if (index >= map.length) throw NoSuchElementException()
lastIndex = index++ lastIndex = index++
@@ -605,8 +601,8 @@ actual class HashMap<K, V> private constructor(
} }
internal class EntryRef<K, V>( internal class EntryRef<K, V>(
private val map: HashMap<K, V>, private val map: InternalHashMap<K, V>,
private val index: Int private val index: Int,
) : MutableMap.MutableEntry<K, V> { ) : MutableMap.MutableEntry<K, V> {
override val key: K override val key: K
get() = map.keysArray[index] get() = map.keysArray[index]
@@ -623,9 +619,9 @@ actual class HashMap<K, V> private constructor(
} }
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other is Map.Entry<*, *> && other is Map.Entry<*, *> &&
other.key == key && other.key == key &&
other.value == value other.value == value
override fun hashCode(): Int = key.hashCode() xor value.hashCode() override fun hashCode(): Int = key.hashCode() xor value.hashCode()
@@ -634,13 +630,12 @@ actual class HashMap<K, V> private constructor(
} }
internal class HashMapKeys<E> internal constructor( internal class HashMapKeys<E> internal constructor(
private val backing: HashMap<E, *> private val backing: InternalHashMap<E, *>,
) : MutableSet<E>, kotlin.native.internal.KonanSet<E>, AbstractMutableSet<E>() { ) : MutableSet<E>, AbstractMutableSet<E>() {
override val size: Int get() = backing.size override val size: Int get() = backing.size
override fun isEmpty(): Boolean = backing.isEmpty() override fun isEmpty(): Boolean = backing.isEmpty()
override fun contains(element: E): Boolean = backing.containsKey(element) override fun contains(element: E): Boolean = backing.containsKey(element)
override fun getElement(element: E): E? = backing.getKey(element)
override fun clear() = backing.clear() override fun clear() = backing.clear()
override fun add(element: E): Boolean = throw UnsupportedOperationException() override fun add(element: E): Boolean = throw UnsupportedOperationException()
override fun addAll(elements: Collection<E>): Boolean = throw UnsupportedOperationException() override fun addAll(elements: Collection<E>): Boolean = throw UnsupportedOperationException()
@@ -659,7 +654,7 @@ internal class HashMapKeys<E> internal constructor(
} }
internal class HashMapValues<V> internal constructor( internal class HashMapValues<V> internal constructor(
val backing: HashMap<*, V> val backing: InternalHashMap<*, V>,
) : MutableCollection<V>, AbstractMutableCollection<V>() { ) : MutableCollection<V>, AbstractMutableCollection<V>() {
override val size: Int get() = backing.size override val size: Int get() = backing.size
@@ -697,13 +692,12 @@ internal class HashMapValues<V> internal constructor(
* See also [KT-42248](https://youtrack.jetbrains.com/issue/KT-42428). * See also [KT-42248](https://youtrack.jetbrains.com/issue/KT-42428).
*/ */
internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal constructor( internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal constructor(
val backing: HashMap<K, V> val backing: InternalHashMap<K, V>,
) : MutableSet<E>, kotlin.native.internal.KonanSet<E>, AbstractMutableSet<E>() { ) : MutableSet<E>, AbstractMutableSet<E>() {
override val size: Int get() = backing.size override val size: Int get() = backing.size
override fun isEmpty(): Boolean = backing.isEmpty() override fun isEmpty(): Boolean = backing.isEmpty()
override fun contains(element: E): Boolean = backing.containsEntry(element) override fun contains(element: E): Boolean = backing.containsEntry(element)
override fun getElement(element: E): E? = getEntry(element)
protected abstract fun getEntry(element: Map.Entry<K, V>): E? protected abstract fun getEntry(element: Map.Entry<K, V>): E?
override fun clear() = backing.clear() override fun clear() = backing.clear()
override fun add(element: E): Boolean = throw UnsupportedOperationException() override fun add(element: E): Boolean = throw UnsupportedOperationException()
@@ -723,13 +717,10 @@ internal abstract class HashMapEntrySetBase<K, V, E : Map.Entry<K, V>> internal
} }
internal class HashMapEntrySet<K, V> internal constructor( internal class HashMapEntrySet<K, V> internal constructor(
backing: HashMap<K, V> backing: InternalHashMap<K, V>,
) : HashMapEntrySetBase<K, V, MutableMap.MutableEntry<K, V>>(backing) { ) : HashMapEntrySetBase<K, V, MutableMap.MutableEntry<K, V>>(backing) {
override fun getEntry(element: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element) override fun getEntry(element: Map.Entry<K, V>): MutableMap.MutableEntry<K, V>? = backing.getEntry(element)
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator() override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator()
} }
// This hash map keeps insertion order.
actual typealias LinkedHashMap<K, V> = HashMap<K, V>