[libraries] Split HashMap and HashMapInternal
HashMapInternal will be reused in JS (KT-59001).
diff --git a/libraries/stdlib/native-wasm/src/kotlin/collections/HashMap.kt b/libraries/stdlib/native-wasm/src/kotlin/collections/HashMap.kt
new file mode 100644
index 0000000..4df6a2e
--- /dev/null
+++ b/libraries/stdlib/native-wasm/src/kotlin/collections/HashMap.kt
@@ -0,0 +1,104 @@
+/*
+ * 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
+
+import kotlin.native.concurrent.isFrozen
+import kotlin.native.FreezingIsDeprecated
+
+@OptIn(FreezingIsDeprecated::class)
+actual class HashMap<K, V> private constructor(private val backing: HashMapInternal<K, V>) : MutableMap<K, V> {
+ private companion object {
+ private val Empty = HashMap(HashMapInternal.EmptyHolder.value)
+ }
+
+ actual override val size: Int get() = backing.size
+
+ actual override val keys: MutableSet<K> get() = backing.getKeys { !it.isFrozen }
+
+ actual override val values: MutableCollection<V> get() = backing.getValues { !it.isFrozen }
+
+ actual override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() = backing.getEntries { !it.isFrozen }
+
+ /**
+ * Creates a new empty [HashMap] with the specified initial capacity.
+ *
+ * Capacity is the maximum number of entries the map is able to store in current internal data structure.
+ * When the map gets full by a certain default load factor, its capacity is expanded,
+ * which usually leads to rebuild of the internal data structure.
+ *
+ * @param initialCapacity the initial capacity of the created map.
+ * Note that the argument is just a hint for the implementation and can be ignored.
+ *
+ * @throws IllegalArgumentException if [initialCapacity] is negative.
+ */
+ actual constructor(initialCapacity: Int) : this(HashMapInternal<K, V>(initialCapacity))
+
+ /**
+ * Creates a new empty [HashMap] with the specified initial capacity and load factor.
+ *
+ * Capacity is the maximum number of entries the map is able to store in current internal data structure.
+ * Load factor is the measure of how full the map is allowed to get in relation to
+ * its capacity before the capacity is expanded, which usually leads to rebuild of the internal data structure.
+ *
+ * @param initialCapacity the initial capacity of the created map.
+ * Note that the argument is just a hint for the implementation and can be ignored.
+ * @param loadFactor the load factor of the created map.
+ * Note that the argument is just a hint for the implementation and can be ignored.
+ *
+ * @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive.
+ */
+ actual constructor(initialCapacity: Int, loadFactor: Float) : this(HashMapInternal<K, V>(initialCapacity, loadFactor))
+
+ /**
+ * Creates a new [HashMap] filled with the contents of the specified [original] map.
+ */
+ actual constructor(original: Map<out K, V>) : this(HashMapInternal<K, V>(original))
+
+ /**
+ * Creates a new empty [HashMap].
+ */
+ actual constructor() : this(HashMapInternal<K, V>())
+
+ actual override fun isEmpty(): Boolean = backing.isEmpty()
+
+ actual override fun containsKey(key: K): Boolean = backing.containsKey(key)
+
+ actual override fun containsValue(value: @UnsafeVariance V): Boolean = backing.containsValue(value)
+
+ actual override fun get(key: K): V? = backing.get(key)
+
+ actual override fun put(key: K, value: V): V? = backing.put(key, value)
+
+ actual override fun remove(key: K): V? = backing.remove(key)
+
+ actual override fun putAll(from: Map<out K, V>) {
+ backing.putAll(from)
+ }
+
+ actual override fun clear() {
+ backing.clear()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other === this ||
+ (other is Map<*, *>) &&
+ backing.contentEquals(other)
+ }
+
+ override fun hashCode(): Int = backing.hashCode()
+
+ override fun toString(): String = backing.toString()
+
+ @PublishedApi
+ internal fun build(): Map<K, V> {
+ backing.build()
+ @Suppress("UNCHECKED_CAST")
+ return if (size > 0) this else Empty as Map<K, V>
+ }
+}
+
+// This hash map keeps insertion order.
+actual typealias LinkedHashMap<K, V> = HashMap<K, V>
diff --git a/libraries/stdlib/native-wasm/src/kotlin/collections/HashMapInternal.kt b/libraries/stdlib/native-wasm/src/kotlin/collections/HashMapInternal.kt
index a1956b2..34fdb8f 100644
--- a/libraries/stdlib/native-wasm/src/kotlin/collections/HashMapInternal.kt
+++ b/libraries/stdlib/native-wasm/src/kotlin/collections/HashMapInternal.kt
@@ -5,39 +5,35 @@
package kotlin.collections
-import kotlin.native.concurrent.isFrozen
-import kotlin.native.FreezingIsDeprecated
-
-@OptIn(FreezingIsDeprecated::class)
-actual class HashMap<K, V> private constructor(
- private var keysArray: Array<K>,
- private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet
- private var presenceArray: IntArray,
- private var hashArray: IntArray,
- private var maxProbeDistance: Int,
- private var length: Int
-) : MutableMap<K, V> {
+internal class HashMapInternal<K, V> private constructor(
+ private var keysArray: Array<K>,
+ private var valuesArray: Array<V>?, // allocated only when actually used, always null in pure HashSet
+ private var presenceArray: IntArray,
+ private var hashArray: IntArray,
+ private var maxProbeDistance: Int,
+ private var length: Int
+) {
private var hashShift: Int = computeShift(hashSize)
private var _size: Int = 0
- override actual val size: Int
+ val size: Int
get() = _size
- private var keysView: HashMapKeys<K>? = null
- private var valuesView: HashMapValues<V>? = null
- private var entriesView: HashMapEntrySet<K, V>? = null
+ internal var keysView: HashMapKeys<K>? = null
+ internal var valuesView: HashMapValues<V>? = null
+ internal var entriesView: HashMapEntrySet<K, V>? = null
private var isReadOnly: Boolean = false
// ---------------------------- functions ----------------------------
/**
- * Creates a new empty [HashMap].
+ * Creates a new empty [HashMapInternal].
*/
- actual constructor() : this(INITIAL_CAPACITY)
+ constructor() : this(INITIAL_CAPACITY)
/**
- * Creates a new empty [HashMap] with the specified initial capacity.
+ * Creates a new empty [HashMapInternal] with the specified initial capacity.
*
* Capacity is the maximum number of entries the map is able to store in current internal data structure.
* When the map gets full by a certain default load factor, its capacity is expanded,
@@ -48,23 +44,24 @@
*
* @throws IllegalArgumentException if [initialCapacity] is negative.
*/
- actual constructor(initialCapacity: Int) : this(
- arrayOfUninitializedElements(initialCapacity),
- null,
- IntArray(initialCapacity),
- IntArray(computeHashSize(initialCapacity)),
- INITIAL_MAX_PROBE_DISTANCE,
- 0)
+ constructor(initialCapacity: Int) : this(
+ arrayOfUninitializedElements(initialCapacity),
+ null,
+ IntArray(initialCapacity),
+ IntArray(computeHashSize(initialCapacity)),
+ INITIAL_MAX_PROBE_DISTANCE,
+ 0
+ )
/**
- * Creates a new [HashMap] filled with the contents of the specified [original] map.
+ * Creates a new [HashMapInternal] 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)
}
/**
- * Creates a new empty [HashMap] with the specified initial capacity and load factor.
+ * Creates a new empty [HashMapInternal] with the specified initial capacity and load factor.
*
* Capacity is the maximum number of entries the map is able to store in current internal data structure.
* Load factor is the measure of how full the map is allowed to get in relation to
@@ -77,28 +74,26 @@
*
* @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" }
}
- @PublishedApi
- internal fun build(): Map<K, V> {
+ internal fun build() {
checkIsMutable()
isReadOnly = true
- return if (size > 0) this else EmptyHolder.value()
}
- override actual fun isEmpty(): Boolean = _size == 0
- override actual fun containsKey(key: K): Boolean = findKey(key) >= 0
- override actual fun containsValue(value: V): Boolean = findValue(value) >= 0
+ fun isEmpty(): Boolean = _size == 0
+ fun containsKey(key: K): Boolean = findKey(key) >= 0
+ fun containsValue(value: V): Boolean = findValue(value) >= 0
- override actual operator fun get(key: K): V? {
+ fun get(key: K): V? {
val index = findKey(key)
if (index < 0) return null
return valuesArray!![index]
}
- override actual fun put(key: K, value: V): V? {
+ fun put(key: K, value: V): V? {
checkIsMutable()
val index = addKey(key)
val valuesArray = allocateValuesArray()
@@ -112,12 +107,12 @@
}
}
- override actual fun putAll(from: Map<out K, V>) {
+ fun putAll(from: Map<out K, V>) {
checkIsMutable()
putAllEntries(from.entries)
}
- override actual fun remove(key: K): V? {
+ fun remove(key: K): V? {
val index = removeKey(key) // mutability gets checked here
if (index < 0) return null
val valuesArray = valuesArray!!
@@ -126,7 +121,7 @@
return oldValue
}
- override actual fun clear() {
+ fun clear() {
checkIsMutable()
// O(length) implementation for hashArray cleanup
for (i in 0..length - 1) {
@@ -142,31 +137,31 @@
length = 0
}
- override actual val keys: MutableSet<K> get() {
+ internal inline fun getKeys(canBeSet: (HashMapInternal<K, V>) -> Boolean = { true }): MutableSet<K> {
val cur = keysView
return if (cur == null) {
val new = HashMapKeys(this)
- if (!isFrozen)
+ if (canBeSet(this))
keysView = new
new
} else cur
}
- override actual val values: MutableCollection<V> get() {
+ internal inline fun getValues(canBeSet: (HashMapInternal<K, V>) -> Boolean = { true }): MutableCollection<V> {
val cur = valuesView
return if (cur == null) {
val new = HashMapValues(this)
- if (!isFrozen)
+ if (canBeSet(this))
valuesView = new
new
} else cur
}
- override actual val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() {
+ internal inline fun getEntries(canBeSet: (HashMapInternal<K, V>) -> Boolean = { true }): MutableSet<MutableMap.MutableEntry<K, V>> {
val cur = entriesView
return if (cur == null) {
val new = HashMapEntrySet(this)
- if (!isFrozen)
+ if (canBeSet(this))
entriesView = new
new
} else cur
@@ -443,7 +438,7 @@
}
}
- private fun contentEquals(other: Map<*, *>): Boolean = _size == other.size && containsAllEntries(other.entries)
+ internal fun contentEquals(other: Map<*, *>): Boolean = _size == other.size && containsAllEntries(other.entries)
internal fun containsAllEntries(m: Collection<*>): Boolean {
val it = m.iterator()
@@ -521,16 +516,11 @@
}
internal object EmptyHolder {
- val value_ = HashMap<Nothing, Nothing>(0).also { it.isReadOnly = true }
-
- fun <K, V> value(): HashMap<K, V> {
- @Suppress("UNCHECKED_CAST")
- return value_ as HashMap<K, V>
- }
+ val value = HashMapInternal<Nothing, Nothing>(0).also { it.isReadOnly = true }
}
internal open class Itr<K, V>(
- internal val map: HashMap<K, V>
+ internal val map: HashMapInternal<K, V>
) {
internal var index = 0
internal var lastIndex: Int = -1
@@ -553,7 +543,7 @@
}
}
- internal class KeysItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<K> {
+ internal class KeysItr<K, V>(map: HashMapInternal<K, V>) : Itr<K, V>(map), MutableIterator<K> {
override fun next(): K {
if (index >= map.length) throw NoSuchElementException()
lastIndex = index++
@@ -564,7 +554,7 @@
}
- internal class ValuesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map), MutableIterator<V> {
+ internal class ValuesItr<K, V>(map: HashMapInternal<K, V>) : Itr<K, V>(map), MutableIterator<V> {
override fun next(): V {
if (index >= map.length) throw NoSuchElementException()
lastIndex = index++
@@ -574,7 +564,7 @@
}
}
- internal class EntriesItr<K, V>(map: HashMap<K, V>) : Itr<K, V>(map),
+ internal class EntriesItr<K, V>(map: HashMapInternal<K, V>) : Itr<K, V>(map),
MutableIterator<MutableMap.MutableEntry<K, V>> {
override fun next(): EntryRef<K, V> {
if (index >= map.length) throw NoSuchElementException()
@@ -605,7 +595,7 @@
}
internal class EntryRef<K, V>(
- private val map: HashMap<K, V>,
+ private val map: HashMapInternal<K, V>,
private val index: Int
) : MutableMap.MutableEntry<K, V> {
override val key: K
@@ -634,7 +624,7 @@
}
internal class HashMapKeys<E> internal constructor(
- private val backing: HashMap<E, *>
+ private val backing: HashMapInternal<E, *>
) : MutableSet<E>, kotlin.native.internal.KonanSet<E>, AbstractMutableSet<E>() {
override val size: Int get() = backing.size
@@ -659,7 +649,7 @@
}
internal class HashMapValues<V> internal constructor(
- val backing: HashMap<*, V>
+ val backing: HashMapInternal<*, V>
) : MutableCollection<V>, AbstractMutableCollection<V>() {
override val size: Int get() = backing.size
@@ -697,7 +687,7 @@
* 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: HashMap<K, V>
+ val backing: HashMapInternal<K, V>
) : MutableSet<E>, kotlin.native.internal.KonanSet<E>, AbstractMutableSet<E>() {
override val size: Int get() = backing.size
@@ -723,7 +713,7 @@
}
internal class HashMapEntrySet<K, V> internal constructor(
- backing: HashMap<K, V>
+ backing: HashMapInternal<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)
@@ -731,5 +721,3 @@
override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> = backing.entriesIterator()
}
-// This hash map keeps insertion order.
-actual typealias LinkedHashMap<K, V> = HashMap<K, V>
\ No newline at end of file
diff --git a/libraries/stdlib/native-wasm/src/kotlin/collections/HashSet.kt b/libraries/stdlib/native-wasm/src/kotlin/collections/HashSet.kt
index fe6aff5..bc83440 100644
--- a/libraries/stdlib/native-wasm/src/kotlin/collections/HashSet.kt
+++ b/libraries/stdlib/native-wasm/src/kotlin/collections/HashSet.kt
@@ -6,16 +6,16 @@
package kotlin.collections
actual class HashSet<E> internal constructor(
- private val backing: HashMap<E, *>
+ private val backing: HashMapInternal<E, *>
) : MutableSet<E>, kotlin.native.internal.KonanSet<E>, AbstractMutableSet<E>() {
private companion object {
- private val Empty = HashSet(HashMap.EmptyHolder.value<Nothing, Nothing>())
+ private val Empty = HashSet(HashMapInternal.EmptyHolder.value)
}
/**
* Creates a new empty [HashSet].
*/
- actual constructor() : this(HashMap<E, Nothing>())
+ actual constructor() : this(HashMapInternal<E, Nothing>())
/**
* Creates a new empty [HashSet] with the specified initial capacity.
@@ -29,7 +29,7 @@
*
* @throws IllegalArgumentException if [initialCapacity] is negative.
*/
- actual constructor(initialCapacity: Int) : this(HashMap<E, Nothing>(initialCapacity))
+ actual constructor(initialCapacity: Int) : this(HashMapInternal<E, Nothing>(initialCapacity))
/**
* Creates a new [HashSet] filled with the elements of the specified collection.
@@ -52,7 +52,7 @@
*
* @throws IllegalArgumentException if [initialCapacity] is negative or [loadFactor] is non-positive.
*/
- actual constructor(initialCapacity: Int, loadFactor: Float) : this(HashMap<E, Nothing>(initialCapacity, loadFactor))
+ actual constructor(initialCapacity: Int, loadFactor: Float) : this(HashMapInternal<E, Nothing>(initialCapacity, loadFactor))
@PublishedApi
internal fun build(): Set<E> {
@@ -86,4 +86,4 @@
}
// This hash set keeps insertion order.
-actual typealias LinkedHashSet<V> = HashSet<V>
\ No newline at end of file
+actual typealias LinkedHashSet<V> = HashSet<V>