From 7b49a052f9b1ab11e827c0ef870715a905ab68ce Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Sat, 23 May 2020 03:52:57 +0300 Subject: [PATCH] Decommonize collection builders (#4157) Decommonize collection builder implementations (cherry picked from commit c960dc0af1f8940e519bf6c6c0886192d2ed88bb) --- .../tests/runtime/collections/hash_map0.kt | 7 +- .../kotlin/kotlin/collections/ArrayList.kt | 149 +++++++------ .../kotlin/kotlin/collections/ArrayUtil.kt | 1 + .../kotlin/kotlin/collections/Collections.kt | 17 ++ .../main/kotlin/kotlin/collections/HashMap.kt | 197 +++++++----------- .../main/kotlin/kotlin/collections/HashSet.kt | 52 ++--- .../main/kotlin/kotlin/collections/Maps.kt | 28 ++- .../src/main/kotlin/kotlin/collections/Set.kt | 6 - .../main/kotlin/kotlin/collections/Sets.kt | 28 +++ 9 files changed, 243 insertions(+), 242 deletions(-) create mode 100644 runtime/src/main/kotlin/kotlin/collections/Sets.kt diff --git a/backend.native/tests/runtime/collections/hash_map0.kt b/backend.native/tests/runtime/collections/hash_map0.kt index 2185786092c..d147ecd9485 100644 --- a/backend.native/tests/runtime/collections/hash_map0.kt +++ b/backend.native/tests/runtime/collections/hash_map0.kt @@ -155,7 +155,7 @@ fun testEquals() { assertFalse(m == mapOf("a" to "1", "b" to "2", "c" to "5")) assertFalse(m == mapOf("a" to "1", "b" to "2")) assertEquals(m.keys, expected.keys) - assertEquals(m.values, expected.values) + assertEquals(m.values.toList(), expected.values.toList()) assertEquals(m.entries, expected.entries) } @@ -165,7 +165,6 @@ fun testHashCode() { assertEquals(expected.hashCode(), m.hashCode()) assertEquals(expected.entries.hashCode(), m.entries.hashCode()) assertEquals(expected.keys.hashCode(), m.keys.hashCode()) - assertEquals(listOf("1", "2", "3").hashCode(), m.values.hashCode()) } fun testToString() { @@ -184,9 +183,9 @@ fun testPutEntry() { assertTrue(m.entries.contains(e)) assertTrue(m.entries.remove(e)) assertTrue(mapOf("b" to "2", "c" to "3") == m) - assertTrue(m.entries.add(e)) + assertEquals(null, m.put(e.key, e.value)) assertTrue(expected == m) - assertFalse(m.entries.add(e)) + assertEquals(e.value, m.put(e.key, e.value)) assertTrue(expected == m) } diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt index 90f3a4cc8f8..69cf4bb0fbc 100644 --- a/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt @@ -9,52 +9,46 @@ actual class ArrayList private constructor( private var array: Array, private var offset: Int, private var length: Int, - private val backing: ArrayList? -) : MutableList, RandomAccess, AbstractMutableCollection() { + private var isReadOnly: Boolean, + private val backing: ArrayList?, + private val root: ArrayList? +) : MutableList, RandomAccess, AbstractMutableList() { actual constructor() : this(10) actual constructor(initialCapacity: Int) : this( - arrayOfUninitializedElements(initialCapacity), 0, 0, null) + arrayOfUninitializedElements(initialCapacity), 0, 0, false, null, null) actual constructor(elements: Collection) : this(elements.size) { addAll(elements) } - override actual val size : Int + @PublishedApi + internal fun build(): List { + if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList + checkIsMutable() + isReadOnly = true + return this + } + + override actual val size: Int get() = length override actual fun isEmpty(): Boolean = length == 0 override actual fun get(index: Int): E { - checkIndex(index) + checkElementIndex(index) return array[offset + index] } override actual operator fun set(index: Int, element: E): E { - checkIndex(index) + checkIsMutable() + checkElementIndex(index) val old = array[offset + index] array[offset + index] = element return old } - override actual fun contains(element: E): Boolean { - var i = 0 - while (i < length) { - if (array[offset + i] == element) return true - i++ - } - return false - } - - override actual fun containsAll(elements: Collection): Boolean { - val it = elements.iterator() - while (it.hasNext()) { - if (!contains(it.next()))return false - } - return true - } - override actual fun indexOf(element: E): Int { var i = 0 while (i < length) { @@ -77,60 +71,68 @@ actual class ArrayList private constructor( override actual fun listIterator(): MutableListIterator = Itr(this, 0) override actual fun listIterator(index: Int): MutableListIterator { - checkInsertIndex(index) + checkPositionIndex(index) return Itr(this, index) } override actual fun add(element: E): Boolean { + checkIsMutable() addAtInternal(offset + length, element) return true } override actual fun add(index: Int, element: E) { - checkInsertIndex(index) + checkIsMutable() + checkPositionIndex(index) addAtInternal(offset + index, element) } override actual fun addAll(elements: Collection): Boolean { + checkIsMutable() val n = elements.size addAllInternal(offset + length, elements, n) return n > 0 } override actual fun addAll(index: Int, elements: Collection): Boolean { - checkInsertIndex(index) + checkIsMutable() + checkPositionIndex(index) val n = elements.size addAllInternal(offset + index, elements, n) return n > 0 } override actual fun clear() { + checkIsMutable() removeRangeInternal(offset, length) } override actual fun removeAt(index: Int): E { - checkIndex(index) + checkIsMutable() + checkElementIndex(index) return removeAtInternal(offset + index) } override actual fun remove(element: E): Boolean { + checkIsMutable() val i = indexOf(element) if (i >= 0) removeAt(i) return i >= 0 } override actual fun removeAll(elements: Collection): Boolean { + checkIsMutable() return retainOrRemoveAllInternal(offset, length, elements, false) > 0 } override actual fun retainAll(elements: Collection): Boolean { + checkIsMutable() return retainOrRemoveAllInternal(offset, length, elements, true) > 0 } override actual fun subList(fromIndex: Int, toIndex: Int): MutableList { - checkInsertIndex(fromIndex) - checkInsertIndexFrom(toIndex, fromIndex) - return ArrayList(array, offset + fromIndex, toIndex - fromIndex, this) + checkRangeIndexes(fromIndex, toIndex) + return ArrayList(array, offset + fromIndex, toIndex - fromIndex, isReadOnly, this, root ?: this) } actual fun trimToSize() { @@ -139,12 +141,11 @@ actual class ArrayList private constructor( array = array.copyOfUninitializedElements(length) } + @OptIn(ExperimentalStdlibApi::class) final actual fun ensureCapacity(minCapacity: Int) { if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList if (minCapacity > array.size) { - var newSize = array.size * 3 / 2 - if (minCapacity > newSize) - newSize = minCapacity + val newSize = ArrayDeque.newCapacity(array.size, minCapacity) array = array.copyOfUninitializedElements(newSize) } } @@ -155,47 +156,46 @@ actual class ArrayList private constructor( } override fun hashCode(): Int { - var result = 1 - var i = 0 - while (i < length) { - val nextElement = array[offset + i] - val nextHash = if (nextElement != null) nextElement.hashCode() else 0 - result = result * 31 + nextHash - i++ - } - return result + return array.subarrayContentHashCode(offset, length) } override fun toString(): String { - return this.array.subarrayContentToString(offset, length) + return array.subarrayContentToString(offset, length) } // ---------------------------- private ---------------------------- + private fun checkElementIndex(index: Int) { + if (index < 0 || index >= length) { + throw IndexOutOfBoundsException("index: $index, size: $length") + } + } + + private fun checkPositionIndex(index: Int) { + if (index < 0 || index > length) { + throw IndexOutOfBoundsException("index: $index, size: $length") + } + } + + private fun checkRangeIndexes(fromIndex: Int, toIndex: Int) { + if (fromIndex < 0 || toIndex > length) { + throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex, size: $length") + } + if (fromIndex > toIndex) { + throw IllegalArgumentException("fromIndex: $fromIndex > toIndex: $toIndex") + } + } + + private fun checkIsMutable() { + if (isReadOnly || root != null && root.isReadOnly) throw UnsupportedOperationException() + } + private fun ensureExtraCapacity(n: Int) { ensureCapacity(length + n) } - private fun checkIndex(index: Int) { - if (index < 0 || index >= length) throw IndexOutOfBoundsException() - } - - private fun checkInsertIndex(index: Int) { - if (index < 0 || index > length) throw IndexOutOfBoundsException() - } - - private fun checkInsertIndexFrom(index: Int, fromIndex: Int) { - if (index < fromIndex || index > length) throw IndexOutOfBoundsException() - } - private fun contentEquals(other: List<*>): Boolean { - if (length != other.size) return false - var i = 0 - while (i < length) { - if (array[offset + i] != other[i]) return false - i++ - } - return true + return array.subarrayContentEquals(offset, length, other) } private fun insertAtInternal(i: Int, n: Int) { @@ -309,8 +309,8 @@ actual class ArrayList private constructor( } override fun set(element: E) { - list.checkIndex(lastIndex) - list.array[list.offset + lastIndex] = element + check(lastIndex != -1) { "Call next() or previous() before replacing element from the iterator." } + list.set(lastIndex, element) } override fun add(element: E) { @@ -326,3 +326,24 @@ actual class ArrayList private constructor( } } } + +private fun Array.subarrayContentHashCode(offset: Int, length: Int): Int { + var result = 1 + var i = 0 + while (i < length) { + val nextElement = this[offset + i] + result = result * 31 + nextElement.hashCode() + i++ + } + return result +} + +private fun Array.subarrayContentEquals(offset: Int, length: Int, other: List<*>): Boolean { + if (length != other.size) return false + var i = 0 + while (i < length) { + if (this[offset + i] != other[i]) return false + i++ + } + return true +} diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt index 92e6434586e..5e1fdb2c2f0 100644 --- a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt @@ -16,6 +16,7 @@ import kotlin.native.internal.ExportForCppRuntime @PublishedApi internal fun arrayOfUninitializedElements(size: Int): Array { // TODO: special case for size == 0? + require(size >= 0) { "capacity must be non-negative." } @Suppress("TYPE_PARAMETER_AS_REIFIED") return Array(size) } diff --git a/runtime/src/main/kotlin/kotlin/collections/Collections.kt b/runtime/src/main/kotlin/kotlin/collections/Collections.kt index f4904a31af1..90097a88400 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Collections.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Collections.kt @@ -41,6 +41,23 @@ public interface MutableIterable : Iterable { } +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildListInternal(builderAction: MutableList.() -> Unit): List { + return ArrayList().apply(builderAction).build() +} + +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildListInternal(capacity: Int, builderAction: MutableList.() -> Unit): List { + return ArrayList(capacity).apply(builderAction).build() +} + + /** * Replaces each element in the list with a result of a transformation specified. */ diff --git a/runtime/src/main/kotlin/kotlin/collections/HashMap.kt b/runtime/src/main/kotlin/kotlin/collections/HashMap.kt index fdb357cb10f..22384f69b74 100644 --- a/runtime/src/main/kotlin/kotlin/collections/HashMap.kt +++ b/runtime/src/main/kotlin/kotlin/collections/HashMap.kt @@ -21,10 +21,12 @@ actual class HashMap private constructor( override actual val size: Int get() = _size - private var keysView: HashSet? = null + private var keysView: HashMapKeys? = null private var valuesView: HashMapValues? = null private var entriesView: HashMapEntrySet? = null + private var isReadOnly: Boolean = false + // ---------------------------- functions ---------------------------- actual constructor() : this(INITIAL_CAPACITY) @@ -44,15 +46,17 @@ actual class HashMap private constructor( // This implementation doesn't use a loadFactor, this constructor is used for compatibility with common stdlib actual constructor(initialCapacity: Int, loadFactor: Float) : this(initialCapacity) + @PublishedApi + internal fun build(): Map { + checkIsMutable() + isReadOnly = true + return this + } + 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 - - operator fun set(key: K, value: V): Unit { - put(key, value) - } - override actual operator fun get(key: K): V? { val index = findKey(key) if (index < 0) return null @@ -60,6 +64,7 @@ actual class HashMap private constructor( } override actual fun put(key: K, value: V): V? { + checkIsMutable() val index = addKey(key) val valuesArray = allocateValuesArray() if (index < 0) { @@ -73,11 +78,12 @@ actual class HashMap private constructor( } override actual fun putAll(from: Map) { + checkIsMutable() putAllEntries(from.entries) } override actual fun remove(key: K): V? { - val index = removeKey(key) + val index = removeKey(key) // mutability gets checked here if (index < 0) return null val valuesArray = valuesArray!! val oldValue = valuesArray[index] @@ -86,6 +92,7 @@ actual class HashMap private constructor( } override actual fun clear() { + checkIsMutable() // O(length) implementation for hashArray cleanup for (i in 0..length - 1) { val hash = presenceArray[i] @@ -103,7 +110,7 @@ actual class HashMap private constructor( override actual val keys: MutableSet get() { val cur = keysView return if (cur == null) { - val new = HashSet(this) + val new = HashMapKeys(this) if (!isFrozen) keysView = new new @@ -133,7 +140,7 @@ actual class HashMap private constructor( override fun equals(other: Any?): Boolean { return other === this || (other is Map<*, *>) && - contentEquals(other) + contentEquals(other) } override fun hashCode(): Int { @@ -164,6 +171,10 @@ actual class HashMap private constructor( private val capacity: Int get() = keysArray.size private val hashSize: Int get() = hashArray.size + internal fun checkIsMutable() { + if (isReadOnly) throw UnsupportedOperationException() + } + private fun ensureExtraCapacity(n: Int) { ensureCapacity(length + n) } @@ -174,7 +185,7 @@ actual class HashMap private constructor( if (capacity > newSize) newSize = capacity keysArray = keysArray.copyOfUninitializedElements(newSize) valuesArray = valuesArray?.copyOfUninitializedElements(newSize) - presenceArray = presenceArray.copyOfUninitializedElements(newSize) + presenceArray = presenceArray.copyOf(newSize) val newHashSize = computeHashSize(newSize) if (newHashSize > hashSize) rehash(newHashSize) } else if (length + capacity - _size > this.capacity) { @@ -265,6 +276,7 @@ actual class HashMap private constructor( } internal fun addKey(key: K): Int { + checkIsMutable() retry@ while (true) { var hash = hash(key) // put is allowed to grow maxProbeDistance with some limits (resize hash on reaching limits) @@ -298,6 +310,7 @@ actual class HashMap private constructor( } internal fun removeKey(key: K): Int { + checkIsMutable() val index = findKey(key) if (index < 0) return TOMBSTONE removeKeyAt(index) @@ -402,7 +415,7 @@ actual class HashMap private constructor( return true } - internal fun putEntry(entry: Map.Entry): Boolean { + private fun putEntry(entry: Map.Entry): Boolean { val index = addKey(entry.key) val valuesArray = allocateValuesArray() if (index >= 0) { @@ -417,7 +430,7 @@ actual class HashMap private constructor( return false } - internal fun putAllEntries(from: Collection>): Boolean { + private fun putAllEntries(from: Collection>): Boolean { if (from.isEmpty()) return false ensureExtraCapacity(from.size) val it = from.iterator() @@ -430,6 +443,7 @@ actual class HashMap private constructor( } internal fun removeEntry(entry: Map.Entry): Boolean { + checkIsMutable() val index = findKey(entry.key) if (index < 0) return false if (valuesArray!![index] != entry.value) return false @@ -437,87 +451,30 @@ actual class HashMap private constructor( return true } - internal fun removeAllEntries(elements: Collection>): Boolean { - if (elements.isEmpty()) return false - val it = entriesIterator() - var updated = false - while (it.hasNext()) { - if (elements.contains(it.next())) { - it.remove() - updated = true - } - } - return updated - } - - internal fun retainAllEntries(elements: Collection>): Boolean { - val it = entriesIterator() - var updated = false - while (it.hasNext()) { - if (!elements.contains(it.next())) { - it.remove() - updated = true - } - } - return updated - } - - internal fun containsAllValues(elements: Collection): Boolean { - val it = elements.iterator() - while (it.hasNext()) { - if (!containsValue(it.next())) - return false - } - return true - } - internal fun removeValue(element: V): Boolean { + checkIsMutable() val index = findValue(element) if (index < 0) return false removeKeyAt(index) return true } - internal fun removeAllValues(elements: Collection): Boolean { - val it = valuesIterator() - var updated = false - while (it.hasNext()) { - if (elements.contains(it.next())) { - it.remove() - updated = true - } - } - return updated - } - - internal fun retainAllValues(elements: Collection): Boolean { - val it = valuesIterator() - var updated = false - while (it.hasNext()) { - if (!elements.contains(it.next())) { - it.remove() - updated = true - } - } - return updated - } - internal fun keysIterator() = KeysItr(this) internal fun valuesIterator() = ValuesItr(this) internal fun entriesIterator() = EntriesItr(this) @kotlin.native.internal.CanBePrecreated private companion object { - const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio - const val INITIAL_CAPACITY = 8 - const val INITIAL_MAX_PROBE_DISTANCE = 2 - const val TOMBSTONE = -1 + private const val MAGIC = -1640531527 // 2654435769L.toInt(), golden ratio + private const val INITIAL_CAPACITY = 8 + private const val INITIAL_MAX_PROBE_DISTANCE = 2 + private const val TOMBSTONE = -1 @OptIn(ExperimentalStdlibApi::class) - fun computeHashSize(capacity: Int): Int = (capacity.coerceAtLeast(1) * 3).takeHighestOneBit() + private fun computeHashSize(capacity: Int): Int = (capacity.coerceAtLeast(1) * 3).takeHighestOneBit() @OptIn(ExperimentalStdlibApi::class) - fun computeShift(hashSize: Int): Int = hashSize.countLeadingZeroBits() + 1 + private fun computeShift(hashSize: Int): Int = hashSize.countLeadingZeroBits() + 1 } internal open class Itr( @@ -538,6 +495,7 @@ actual class HashMap private constructor( fun hasNext(): Boolean = index < map.length fun remove() { + map.checkIsMutable() map.removeKeyAt(lastIndex) lastIndex = -1 } @@ -605,6 +563,7 @@ actual class HashMap private constructor( get() = map.valuesArray!![index] override fun setValue(newValue: V): V { + map.checkIsMutable() val valuesArray = map.allocateValuesArray() val oldValue = valuesArray[index] valuesArray[index] = newValue @@ -622,82 +581,78 @@ actual class HashMap private constructor( } } +internal class HashMapKeys internal constructor( + private val backing: HashMap +) : MutableSet, kotlin.native.internal.KonanSet, AbstractMutableSet() { + + override val size: Int get() = backing.size + override fun isEmpty(): Boolean = backing.isEmpty() + 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 add(element: E): Boolean = throw UnsupportedOperationException() + override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() + override fun remove(element: E): Boolean = backing.removeKey(element) >= 0 + override fun iterator(): MutableIterator = backing.keysIterator() + + override fun removeAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.removeAll(elements) + } + + override fun retainAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.retainAll(elements) + } +} + internal class HashMapValues internal constructor( val backing: HashMap<*, V> -) : MutableCollection { +) : MutableCollection, AbstractMutableCollection() { override val size: Int get() = backing.size override fun isEmpty(): Boolean = backing.isEmpty() override fun contains(element: V): Boolean = backing.containsValue(element) - override fun containsAll(elements: Collection): Boolean = backing.containsAllValues(elements) override fun add(element: V): Boolean = throw UnsupportedOperationException() override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() override fun clear() = backing.clear() override fun iterator(): MutableIterator = backing.valuesIterator() override fun remove(element: V): Boolean = backing.removeValue(element) - override fun removeAll(elements: Collection): Boolean = backing.removeAllValues(elements) - override fun retainAll(elements: Collection): Boolean = backing.retainAllValues(elements) - override fun equals(other: Any?): Boolean = - other === this || - other is Collection<*> && - contentEquals(other) - - override fun hashCode(): Int { - var result = 1 - val it = iterator() - while (it.hasNext()) { - result = result * 31 + it.next().hashCode() - } - return result + override fun removeAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.removeAll(elements) } - override fun toString(): String = collectionToString() - - // ---------------------------- private ---------------------------- - - private fun contentEquals(other: Collection<*>): Boolean { - @Suppress("UNCHECKED_CAST") // todo: figure out something better - return size == other.size && backing.containsAllValues(other as Collection) + override fun retainAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.retainAll(elements) } } internal class HashMapEntrySet internal constructor( val backing: HashMap -) : MutableSet>, kotlin.native.internal.KonanSet> { +) : MutableSet>, kotlin.native.internal.KonanSet>, AbstractMutableSet>() { override val size: Int get() = backing.size override fun isEmpty(): Boolean = backing.isEmpty() override fun contains(element: MutableMap.MutableEntry): Boolean = backing.containsEntry(element) override fun getElement(element: MutableMap.MutableEntry): MutableMap.MutableEntry? = backing.getEntry(element) override fun clear() = backing.clear() - override fun add(element: MutableMap.MutableEntry): Boolean = backing.putEntry(element) + override fun add(element: MutableMap.MutableEntry): Boolean = throw UnsupportedOperationException() + override fun addAll(elements: Collection>): Boolean = throw UnsupportedOperationException() override fun remove(element: MutableMap.MutableEntry): Boolean = backing.removeEntry(element) override fun iterator(): MutableIterator> = backing.entriesIterator() override fun containsAll(elements: Collection>): Boolean = backing.containsAllEntries(elements) - override fun addAll(elements: Collection>): Boolean = backing.putAllEntries(elements) - override fun removeAll(elements: Collection>): Boolean = backing.removeAllEntries(elements) - override fun retainAll(elements: Collection>): Boolean = backing.retainAllEntries(elements) - override fun equals(other: Any?): Boolean = - other === this || other is Set<*> && contentEquals(other) - - override fun hashCode(): Int { - var result = 0 - val it = iterator() - while (it.hasNext()) { - result += it.next().hashCode() - } - return result + override fun removeAll(elements: Collection>): Boolean { + backing.checkIsMutable() + return super.removeAll(elements) } - override fun toString(): String = collectionToString() - - // ---------------------------- private ---------------------------- - - private fun contentEquals(other: Set<*>): Boolean { - @Suppress("UNCHECKED_CAST") // todo: get rid of unchecked cast here somehow - return size == other.size && backing.containsAllEntries(other as Collection>) + override fun retainAll(elements: Collection>): Boolean { + backing.checkIsMutable() + return super.retainAll(elements) } } diff --git a/runtime/src/main/kotlin/kotlin/collections/HashSet.kt b/runtime/src/main/kotlin/kotlin/collections/HashSet.kt index 388d1af1bbe..004bf7675fe 100644 --- a/runtime/src/main/kotlin/kotlin/collections/HashSet.kt +++ b/runtime/src/main/kotlin/kotlin/collections/HashSet.kt @@ -6,8 +6,8 @@ package kotlin.collections actual class HashSet internal constructor( - val backing: HashMap -) : MutableSet, AbstractMutableCollection(), kotlin.native.internal.KonanSet { + private val backing: HashMap +) : MutableSet, kotlin.native.internal.KonanSet, AbstractMutableSet() { actual constructor() : this(HashMap()) @@ -20,6 +20,12 @@ actual class HashSet internal constructor( // This implementation doesn't use a loadFactor actual constructor(initialCapacity: Int, loadFactor: Float) : this(initialCapacity) + @PublishedApi + internal fun build(): Set { + backing.build() + return this + } + override actual val size: Int get() = backing.size override actual fun isEmpty(): Boolean = backing.isEmpty() override actual fun contains(element: E): Boolean = backing.containsKey(element) @@ -29,46 +35,20 @@ actual class HashSet internal constructor( override actual fun remove(element: E): Boolean = backing.removeKey(element) >= 0 override actual fun iterator(): MutableIterator = backing.keysIterator() - override actual fun containsAll(elements: Collection): Boolean { - val it = elements.iterator() - while (it.hasNext()) { - if (!contains(it.next())) - return false - } - return true - } - override actual fun addAll(elements: Collection): Boolean { - val it = elements.iterator() - var updated = false - while (it.hasNext()) { - if (add(it.next())) - updated = true - } - return updated + backing.checkIsMutable() + return super.addAll(elements) } - override fun equals(other: Any?): Boolean { - return other === this || - (other is Set<*>) && - contentEquals( - @Suppress("UNCHECKED_CAST") (other as Set)) + override actual fun removeAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.removeAll(elements) } - override fun hashCode(): Int { - var result = 0 - val it = iterator() - while (it.hasNext()) { - result += it.next().hashCode() - } - return result + override actual fun retainAll(elements: Collection): Boolean { + backing.checkIsMutable() + return super.retainAll(elements) } - - override fun toString(): String = collectionToString() - - // ---------------------------- private ---------------------------- - - private fun contentEquals(other: Set): Boolean = size == other.size && containsAll(other) } // This hash set keeps insertion order. diff --git a/runtime/src/main/kotlin/kotlin/collections/Maps.kt b/runtime/src/main/kotlin/kotlin/collections/Maps.kt index e511d99317c..c4255e69724 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Maps.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Maps.kt @@ -5,6 +5,23 @@ package kotlin.collections +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildMapInternal(builderAction: MutableMap.() -> Unit): Map { + return HashMap().apply(builderAction).build() +} + +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildMapInternal(capacity: Int, builderAction: MutableMap.() -> Unit): Map { + return HashMap(capacity).apply(builderAction).build() +} + + // creates a singleton copy of map, if there is specialization available in target platform, otherwise returns itself @Suppress("NOTHING_TO_INLINE") internal inline actual fun Map.toSingletonMapOrSelf(): Map = toSingletonMap() @@ -19,14 +36,3 @@ internal actual fun Map.toSingletonMap(): Map */ @PublishedApi internal actual fun mapCapacity(expectedSize: Int) = expectedSize - -/** - * Checks a collection builder function capacity argument. - * Does nothing, capacity is validated in List/Set/Map constructor - */ -@SinceKotlin("1.3") -@ExperimentalStdlibApi -@PublishedApi -internal actual fun checkBuilderCapacity(capacity: Int) { - require(capacity >= 0) { "capacity must be non-negative." } -} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/Set.kt b/runtime/src/main/kotlin/kotlin/collections/Set.kt index 68ca7ba850c..9a9a0e7d18b 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Set.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Set.kt @@ -50,9 +50,3 @@ public interface MutableSet : Set, MutableCollection { override fun retainAll(elements: Collection): Boolean override fun clear(): Unit } - -// TODO: Add SingletonSet class -/** - * Returns an immutable set containing only the specified object [element]. - */ -public fun setOf(element: T): Set = hashSetOf(element) diff --git a/runtime/src/main/kotlin/kotlin/collections/Sets.kt b/runtime/src/main/kotlin/kotlin/collections/Sets.kt new file mode 100644 index 00000000000..0757b4574f0 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/Sets.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package kotlin.collections + +// TODO: Add SingletonSet class +/** + * Returns an immutable set containing only the specified object [element]. + */ +public fun setOf(element: T): Set = hashSetOf(element) + +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildSetInternal(builderAction: MutableSet.() -> Unit): Set { + return HashSet().apply(builderAction).build() +} + +@PublishedApi +@SinceKotlin("1.3") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +internal actual inline fun buildSetInternal(capacity: Int, builderAction: MutableSet.() -> Unit): Set { + return HashSet(capacity).apply(builderAction).build() +}