From b54903301c01fbe0fce828e44edd955643457587 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 23 Oct 2019 20:43:00 +0300 Subject: [PATCH 001/162] Advance development version to 0.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 69d69497..637c9e80 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group=org.jetbrains.kotlinx -version=0.3 +version=0.4 versionSuffix=SNAPSHOT \ No newline at end of file From bcf2d91affb95b428ab3a74c52923055bd6c18dd Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 11 Sep 2019 17:15:37 +0300 Subject: [PATCH 002/162] Convert benchmarks to multi-platform project --- benchmarks-mpp/build.gradle | 53 ----------- benchmarks/build.gradle.kts | 90 +++++++++++++++++++ .../src}/benchmarks/ObjectWrapper.kt | 7 +- .../src}/benchmarks/benchmarkSize.kt | 0 .../src}/benchmarks/hashCodeTypes.kt | 4 +- .../src}/benchmarks/immutableList/Add.kt | 5 +- .../src}/benchmarks/immutableList/AddAll.kt | 17 +--- .../src}/benchmarks/immutableList/Get.kt | 5 +- .../src}/benchmarks/immutableList/Iterate.kt | 5 +- .../src}/benchmarks/immutableList/Remove.kt | 6 +- .../benchmarks/immutableList/RemoveAll.kt | 23 ++--- .../src}/benchmarks/immutableList/Set.kt | 4 +- .../benchmarks/immutableList/builder/Add.kt | 5 +- .../immutableList/builder/AddAll.kt | 17 +--- .../benchmarks/immutableList/builder/Get.kt | 5 +- .../immutableList/builder/Iterate.kt | 5 +- .../immutableList/builder/Remove.kt | 4 +- .../immutableList/builder/RemoveAll.kt | 21 ++--- .../benchmarks/immutableList/builder/Set.kt | 4 +- .../benchmarks/immutableList/builder/utils.kt | 0 .../src}/benchmarks/immutableList/utils.kt | 0 .../immutableMap/Canonicalization.kt | 7 +- .../src}/benchmarks/immutableMap/Get.kt | 7 +- .../src}/benchmarks/immutableMap/Iterate.kt | 7 +- .../src}/benchmarks/immutableMap/Put.kt | 7 +- .../src}/benchmarks/immutableMap/Remove.kt | 6 +- .../benchmarks/immutableMap/builder/Get.kt | 7 +- .../immutableMap/builder/Iterate.kt | 7 +- .../benchmarks/immutableMap/builder/Put.kt | 7 +- .../benchmarks/immutableMap/builder/Remove.kt | 6 +- .../benchmarks/immutableMap/builder/utils.kt | 0 .../src}/benchmarks/immutableMap/utils.kt | 0 .../src}/benchmarks/immutablePercentage.kt | 0 .../src}/benchmarks/immutableSet/Add.kt | 7 +- .../immutableSet/Canonicalization.kt | 7 +- .../src}/benchmarks/immutableSet/Contains.kt | 7 +- .../src}/benchmarks/immutableSet/Iterate.kt | 7 +- .../src}/benchmarks/immutableSet/Remove.kt | 6 +- .../benchmarks/immutableSet/builder/Add.kt | 7 +- .../immutableSet/builder/Contains.kt | 7 +- .../immutableSet/builder/Iterate.kt | 7 +- .../benchmarks/immutableSet/builder/Remove.kt | 6 +- .../benchmarks/immutableSet/builder/utils.kt | 0 .../src}/benchmarks/immutableSet/utils.kt | 0 .../runner}/build.gradle | 2 +- settings.gradle | 2 +- 46 files changed, 190 insertions(+), 216 deletions(-) delete mode 100644 benchmarks-mpp/build.gradle create mode 100644 benchmarks/build.gradle.kts rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/ObjectWrapper.kt (72%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/benchmarkSize.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/hashCodeTypes.kt (96%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/Add.kt (94%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/AddAll.kt (85%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/Get.kt (88%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/Iterate.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/Remove.kt (95%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/RemoveAll.kt (77%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/Set.kt (95%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/Add.kt (95%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/AddAll.kt (87%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/Get.kt (89%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/Iterate.kt (91%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/Remove.kt (97%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/RemoveAll.kt (83%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/Set.kt (95%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/builder/utils.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableList/utils.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/Canonicalization.kt (97%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/Get.kt (89%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/Iterate.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/Put.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/Remove.kt (93%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/builder/Get.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/builder/Iterate.kt (91%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/builder/Put.kt (92%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/builder/Remove.kt (95%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/builder/utils.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableMap/utils.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutablePercentage.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/Add.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/Canonicalization.kt (97%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/Contains.kt (90%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/Iterate.kt (87%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/Remove.kt (93%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/builder/Add.kt (92%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/builder/Contains.kt (91%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/builder/Iterate.kt (89%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/builder/Remove.kt (94%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/builder/utils.kt (100%) rename {benchmarks-mpp/src/jvmMain/kotlin => benchmarks/commonMain/src}/benchmarks/immutableSet/utils.kt (100%) rename {benchmarks-runner => benchmarks/runner}/build.gradle (97%) diff --git a/benchmarks-mpp/build.gradle b/benchmarks-mpp/build.gradle deleted file mode 100644 index 5ee05289..00000000 --- a/benchmarks-mpp/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.multiplatform' - id 'kotlinx.benchmark' version '0.2.0-dev-2' -} - -evaluationDependsOn(":kotlinx-collections-immutable") - -repositories { - mavenCentral() - maven { url 'https://dl.bintray.com/orangy/maven' } - maven { url 'https://dl.bintray.com/kotlin/kotlinx' } - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } -} - -apply plugin: 'maven-publish' - -kotlin { - jvm() - - sourceSets { - commonMain { - } - commonTest { - } - jvmMain { - dependencies { - implementation kotlin('stdlib-jdk8') - implementation project(path: ':kotlinx-collections-immutable') - implementation 'org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-2' - } - } - jvmTest { - } - } -} - -benchmark { - targets { - register("jvm") { - jmhVersion = "1.21" - } - } -} - -configurations { - benchmarksJar -} - -afterEvaluate { - artifacts { - benchmarksJar jvmBenchmarkJar - } -} diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts new file mode 100644 index 00000000..34b0672a --- /dev/null +++ b/benchmarks/build.gradle.kts @@ -0,0 +1,90 @@ +import kotlinx.benchmark.gradle.JvmBenchmarkTarget +import org.gradle.jvm.tasks.Jar + +plugins { + id("kotlin-multiplatform") + id("kotlinx.benchmark") version "0.2.0-dev-6" +} + + +evaluationDependsOn(":kotlinx-collections-immutable") + +val JDK_6: String by project + +repositories { + maven(url = "https://dl.bintray.com/kotlin/kotlinx") +} + + +kotlin { + infra { + target("macosX64") + target("linuxX64") + target("mingwX64") + } + + jvm { + compilations.all { + kotlinOptions { + jvmTarget = "1.6" + jdkHome = JDK_6 + } + } + } + + js { + nodejs { + + } + } + + sourceSets.all { + kotlin.setSrcDirs(listOf("$name/src")) + resources.setSrcDirs(listOf("$name/resources")) + } + + sourceSets { + commonMain { + dependencies { + api("org.jetbrains.kotlin:kotlin-stdlib-common") + api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-6") + api(project(":kotlinx-collections-immutable")) + } + } + } +} + + +// Configure benchmark +benchmark { + configurations { + named("main") { + warmups = 7 + iterations = 7 + iterationTime = 500 + iterationTimeUnit = "ms" + param("size", "1", "10", "100", "1000", "10000") + param("immutablePercentage", /*"95", "30", */"0") + param("hashCodeType", "random", "collision") + } + } + + targets { + register("jvm") { + this as JvmBenchmarkTarget + jmhVersion = "1.21" + } + register("js") + register("native") + register("macosX64") + register("linuxX64") + register("mingwX64") + } +} + +val benchmarksJar: Configuration by configurations.creating + +afterEvaluate { + val jvmBenchmarkJar by tasks.getting(Jar::class) + artifacts.add("benchmarksJar", jvmBenchmarkJar) +} \ No newline at end of file diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/ObjectWrapper.kt b/benchmarks/commonMain/src/benchmarks/ObjectWrapper.kt similarity index 72% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/ObjectWrapper.kt rename to benchmarks/commonMain/src/benchmarks/ObjectWrapper.kt index 5e755ee5..0991846c 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/ObjectWrapper.kt +++ b/benchmarks/commonMain/src/benchmarks/ObjectWrapper.kt @@ -4,8 +4,12 @@ */ package benchmarks +import kotlin.js.JsName -class ObjectWrapper>(val obj: K, val hashCode: Int) : Comparable> { +class ObjectWrapper>( + val obj: K, + @JsName("_hashCode") val hashCode: Int +) : Comparable> { override fun hashCode(): Int { return hashCode } @@ -14,7 +18,6 @@ class ObjectWrapper>(val obj: K, val hashCode: Int) : Comparabl if (other !is ObjectWrapper<*>) { return false } - assert(obj != other.obj || hashCode == other.hashCode) // if elements are equal hashCodes must be equal return obj == other.obj } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/benchmarkSize.kt b/benchmarks/commonMain/src/benchmarks/benchmarkSize.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/benchmarkSize.kt rename to benchmarks/commonMain/src/benchmarks/benchmarkSize.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/hashCodeTypes.kt b/benchmarks/commonMain/src/benchmarks/hashCodeTypes.kt similarity index 96% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/hashCodeTypes.kt rename to benchmarks/commonMain/src/benchmarks/hashCodeTypes.kt index 43bec0b9..1cf4d98a 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/hashCodeTypes.kt +++ b/benchmarks/commonMain/src/benchmarks/hashCodeTypes.kt @@ -5,8 +5,6 @@ package benchmarks -import java.util.* - const val ASCENDING_HASH_CODE = "ascending" const val RANDOM_HASH_CODE = "random" const val COLLISION_HASH_CODE = "collision" @@ -21,7 +19,7 @@ private inline fun intWrappers(size: Int, hashCodeGenerator: (index: Int) -> Int } private fun generateIntWrappers(hashCodeType: String, size: Int): List { - val random = Random(40) + val random = kotlin.random.Random(40) return when(hashCodeType) { ASCENDING_HASH_CODE -> intWrappers(size) { it } RANDOM_HASH_CODE, diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Add.kt b/benchmarks/commonMain/src/benchmarks/immutableList/Add.kt similarity index 94% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Add.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/Add.kt index 03e0591f..d2f1ef32 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Add.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/Add.kt @@ -7,10 +7,9 @@ package benchmarks.immutableList import benchmarks.* import kotlinx.collections.immutable.ImmutableList -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Add { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/AddAll.kt b/benchmarks/commonMain/src/benchmarks/immutableList/AddAll.kt similarity index 85% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/AddAll.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/AddAll.kt index cea30a74..b3688706 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/AddAll.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/AddAll.kt @@ -1,17 +1,6 @@ /* * Copyright 2016-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package benchmarks.immutableList @@ -19,9 +8,9 @@ package benchmarks.immutableList import benchmarks.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class AddAll { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Get.kt b/benchmarks/commonMain/src/benchmarks/immutableList/Get.kt similarity index 88% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Get.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/Get.kt index 7f0afe4a..3f0264b3 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Get.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/Get.kt @@ -8,8 +8,7 @@ package benchmarks.immutableList import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Get { @@ -18,7 +17,7 @@ open class Get { private var persistentList: PersistentList = persistentListOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentList = persistentListAdd(size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableList/Iterate.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/Iterate.kt index 00cea5ee..cb00a701 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/Iterate.kt @@ -8,8 +8,7 @@ package benchmarks.immutableList import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Iterate { @@ -18,7 +17,7 @@ open class Iterate { private var persistentList: PersistentList = persistentListOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentList = persistentListAdd(size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableList/Remove.kt similarity index 95% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/Remove.kt index 2a2fcb7f..6e1e3c07 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/Remove.kt @@ -9,16 +9,16 @@ import benchmarks.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 private var persistentList: PersistentList = persistentListOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentList = persistentListAdd(size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/RemoveAll.kt b/benchmarks/commonMain/src/benchmarks/immutableList/RemoveAll.kt similarity index 77% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/RemoveAll.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/RemoveAll.kt index b4260da9..9edff485 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/RemoveAll.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/RemoveAll.kt @@ -1,17 +1,6 @@ /* * Copyright 2016-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package benchmarks.immutableList @@ -19,16 +8,17 @@ package benchmarks.immutableList import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* +import kotlin.random.Random -@State(Scope.Thread) +@State(Scope.Benchmark) open class RemoveAll { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 private var persistentList: PersistentList = persistentListOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentList = persistentListOf().addAll(List(size) { it }) } @@ -92,8 +82,7 @@ open class RemoveAll { } private fun randomIndexes(count: Int): List { - val random = java.util.Random() - return List(count) { random.nextInt(size) } + return List(count) { Random.nextInt(size) } } private fun tailSize(): Int { diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Set.kt b/benchmarks/commonMain/src/benchmarks/immutableList/Set.kt similarity index 95% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Set.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/Set.kt index 849c3819..e7282aff 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/Set.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/Set.kt @@ -9,7 +9,7 @@ import benchmarks.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Set { @@ -19,7 +19,7 @@ open class Set { private var persistentList: PersistentList = persistentListOf() private var randomIndices = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentList = persistentListAdd(size) randomIndices = List(size) { it }.shuffled() diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Add.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Add.kt similarity index 95% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Add.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/Add.kt index cdca3f2f..e3c67b0e 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Add.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Add.kt @@ -7,10 +7,9 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.PersistentList -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Add { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/AddAll.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/AddAll.kt similarity index 87% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/AddAll.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/AddAll.kt index 34ab412a..f2b10e48 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/AddAll.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/AddAll.kt @@ -1,17 +1,6 @@ /* * Copyright 2016-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package benchmarks.immutableList.builder @@ -19,9 +8,9 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class AddAll { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Get.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Get.kt similarity index 89% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Get.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/Get.kt index 43147d1b..91eb251b 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Get.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Get.kt @@ -7,8 +7,7 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Get { @@ -20,7 +19,7 @@ open class Get { private var builder = persistentListOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { builder = persistentListBuilderAdd(size, immutablePercentage) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Iterate.kt similarity index 91% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/Iterate.kt index 5b015bea..a641e35e 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Iterate.kt @@ -7,8 +7,7 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Iterate { @@ -20,7 +19,7 @@ open class Iterate { private var builder = persistentListOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { builder = persistentListBuilderAdd(size, immutablePercentage) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Remove.kt similarity index 97% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/Remove.kt index 6d7b5fea..25635584 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Remove.kt @@ -7,9 +7,9 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.PersistentList -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/RemoveAll.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/RemoveAll.kt similarity index 83% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/RemoveAll.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/RemoveAll.kt index 36fbe042..f2c994f3 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/RemoveAll.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/RemoveAll.kt @@ -1,17 +1,6 @@ /* * Copyright 2016-2019 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ package benchmarks.immutableList.builder @@ -19,9 +8,10 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* +import kotlin.random.Random -@State(Scope.Thread) +@State(Scope.Benchmark) open class RemoveAll { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) var size: Int = 0 @@ -106,8 +96,7 @@ open class RemoveAll { } private fun randomIndexes(count: Int): List { - val random = java.util.Random() - return List(count) { random.nextInt(size) } + return List(count) { Random.nextInt(size) } } private fun tailSize(): Int { diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Set.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Set.kt similarity index 95% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Set.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/Set.kt index c35c61ce..ff353267 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/Set.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableList/builder/Set.kt @@ -8,7 +8,7 @@ package benchmarks.immutableList.builder import benchmarks.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* @State(Scope.Benchmark) open class Set { @@ -21,7 +21,7 @@ open class Set { private var builder = persistentListOf().builder() private var randomIndices = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { builder = persistentListBuilderAdd(size, immutablePercentage) randomIndices = List(size) { it }.shuffled() diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableList/builder/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/builder/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/builder/utils.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableList/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableList/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableList/utils.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Canonicalization.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Canonicalization.kt similarity index 97% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Canonicalization.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/Canonicalization.kt index f9463e11..ad7c2338 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Canonicalization.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Canonicalization.kt @@ -8,8 +8,7 @@ package benchmarks.immutableMap import benchmarks.* import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* /** @@ -23,7 +22,7 @@ import org.openjdk.jmh.infra.Blackhole * after removing some entries its actual average height will become bigger then its expected height. * Thus, many operations on the resulting map will be slower. */ -@State(Scope.Thread) +@State(Scope.Benchmark) open class Canonicalization { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -44,7 +43,7 @@ open class Canonicalization { */ private var halfHeightPersistentMap = persistentMapOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) persistentMap = persistentMapPut(implementation, keys) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Get.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Get.kt similarity index 89% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Get.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/Get.kt index cf86d97e..f63867d2 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Get.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Get.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap import benchmarks.* import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Get { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -24,7 +23,7 @@ open class Get { private var keys = listOf() private var persistentMap = persistentMapOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) persistentMap = persistentMapPut(implementation, keys) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Iterate.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/Iterate.kt index c1c9850e..80c62a79 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Iterate.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap import benchmarks.* import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Iterate { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -23,7 +22,7 @@ open class Iterate { private var persistentMap = persistentMapOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentMap = persistentMapPut(implementation, generateKeys(hashCodeType, size)) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Put.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Put.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Put.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/Put.kt index 89201c90..2fbc85d5 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Put.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Put.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap import benchmarks.* import kotlinx.collections.immutable.PersistentMap -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Put { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -23,7 +22,7 @@ open class Put { private var keys = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Remove.kt similarity index 93% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/Remove.kt index 2539fb50..345c0d2b 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Remove.kt @@ -8,9 +8,9 @@ package benchmarks.immutableMap import benchmarks.* import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -24,7 +24,7 @@ open class Remove { private var keys = listOf() private var persistentMap = persistentMapOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) persistentMap = persistentMapPut(implementation, keys) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Get.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Get.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Get.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/builder/Get.kt index fb03f4fc..d34b6da5 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Get.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Get.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap.builder import benchmarks.* import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Get { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -27,7 +26,7 @@ open class Get { private var keys = listOf() private var builder = persistentMapOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) builder = persistentMapBuilderPut(implementation, keys, immutablePercentage) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Iterate.kt similarity index 91% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/builder/Iterate.kt index 44af8f37..9de1c51b 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Iterate.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap.builder import benchmarks.* import kotlinx.collections.immutable.persistentMapOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Iterate { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +25,7 @@ open class Iterate { private var builder = persistentMapOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { val keys = generateKeys(hashCodeType, size) builder = persistentMapBuilderPut(implementation, keys, immutablePercentage) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Put.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Put.kt similarity index 92% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Put.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/builder/Put.kt index 8b871c37..b3e8b270 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Put.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Put.kt @@ -7,10 +7,9 @@ package benchmarks.immutableMap.builder import benchmarks.* import kotlinx.collections.immutable.PersistentMap -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Put { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +25,7 @@ open class Put { private var keys = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Remove.kt similarity index 95% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/builder/Remove.kt index d4eb9c62..d7218def 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Remove.kt @@ -7,9 +7,9 @@ package benchmarks.immutableMap.builder import benchmarks.* import kotlinx.collections.immutable.PersistentMap -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +26,7 @@ open class Remove { private var keys = listOf() private var keysToRemove = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { keys = generateKeys(hashCodeType, size) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/builder/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/builder/utils.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableMap/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableMap/utils.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutablePercentage.kt b/benchmarks/commonMain/src/benchmarks/immutablePercentage.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutablePercentage.kt rename to benchmarks/commonMain/src/benchmarks/immutablePercentage.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Add.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Add.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Add.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/Add.kt index 71fc4e52..08cec63a 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Add.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Add.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet import benchmarks.* import kotlinx.collections.immutable.ImmutableSet -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Add { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -23,7 +22,7 @@ open class Add { private var elements = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Canonicalization.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Canonicalization.kt similarity index 97% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Canonicalization.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/Canonicalization.kt index 520f6626..e2099c68 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Canonicalization.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Canonicalization.kt @@ -8,8 +8,7 @@ package benchmarks.immutableSet import benchmarks.* import kotlinx.collections.immutable.PersistentSet import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* /** @@ -23,7 +22,7 @@ import org.openjdk.jmh.infra.Blackhole * after removing some elements its actual average height will become bigger then its expected height. * Thus, many operations on the resulting set will be slower. */ -@State(Scope.Thread) +@State(Scope.Benchmark) open class Canonicalization { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -44,7 +43,7 @@ open class Canonicalization { */ private var halfHeightPersistentSet = persistentSetOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) persistentSet = persistentSetAdd(implementation, elements) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Contains.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Contains.kt similarity index 90% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Contains.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/Contains.kt index 579859e4..1a7dbdff 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Contains.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Contains.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet import benchmarks.* import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Contains { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -24,7 +23,7 @@ open class Contains { private var elements = listOf() private var persistentSet = persistentSetOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) persistentSet = persistentSetAdd(implementation, elements) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Iterate.kt similarity index 87% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/Iterate.kt index efa0a74b..5c05262a 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Iterate.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet import benchmarks.* import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Iterate { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -23,7 +22,7 @@ open class Iterate { private var persistentSet = persistentSetOf() - @Setup(Level.Trial) + @Setup fun prepare() { persistentSet = persistentSetAdd(implementation, generateElements(hashCodeType, size)) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Remove.kt similarity index 93% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/Remove.kt index 33414929..c2263f7a 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Remove.kt @@ -8,9 +8,9 @@ package benchmarks.immutableSet import benchmarks.* import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -24,7 +24,7 @@ open class Remove { private var elements = listOf() private var persistentSet = persistentSetOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) persistentSet = persistentSetAdd(implementation, elements) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Add.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Add.kt similarity index 92% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Add.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/builder/Add.kt index 5bc1053e..069a67d1 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Add.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Add.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet.builder import benchmarks.* import kotlinx.collections.immutable.PersistentSet -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Add { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +25,7 @@ open class Add { private var elements = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) } diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Contains.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Contains.kt similarity index 91% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Contains.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/builder/Contains.kt index 5b5b2382..1f23fa24 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Contains.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Contains.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet.builder import benchmarks.* import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Contains { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -27,7 +26,7 @@ open class Contains { private var elements = listOf() private var builder = persistentSetOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) builder = persistentSetBuilderAdd(implementation, elements, immutablePercentage) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Iterate.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Iterate.kt similarity index 89% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Iterate.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/builder/Iterate.kt index a11db721..56d055ee 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Iterate.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Iterate.kt @@ -7,10 +7,9 @@ package benchmarks.immutableSet.builder import benchmarks.* import kotlinx.collections.immutable.persistentSetOf -import org.openjdk.jmh.annotations.* -import org.openjdk.jmh.infra.Blackhole +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Iterate { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +25,7 @@ open class Iterate { private var builder = persistentSetOf().builder() - @Setup(Level.Trial) + @Setup fun prepare() { val elements = generateElements(hashCodeType, size) builder = persistentSetBuilderAdd(implementation, elements, immutablePercentage) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Remove.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Remove.kt similarity index 94% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Remove.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/builder/Remove.kt index 05e0f9b0..bf3131b1 100644 --- a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/Remove.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Remove.kt @@ -7,9 +7,9 @@ package benchmarks.immutableSet.builder import benchmarks.* import kotlinx.collections.immutable.PersistentSet -import org.openjdk.jmh.annotations.* +import kotlinx.benchmark.* -@State(Scope.Thread) +@State(Scope.Benchmark) open class Remove { @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) var size: Int = 0 @@ -26,7 +26,7 @@ open class Remove { private var elements = listOf() private var elementsToRemove = listOf() - @Setup(Level.Trial) + @Setup fun prepare() { elements = generateElements(hashCodeType, size) diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/builder/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/builder/utils.kt diff --git a/benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/utils.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/utils.kt similarity index 100% rename from benchmarks-mpp/src/jvmMain/kotlin/benchmarks/immutableSet/utils.kt rename to benchmarks/commonMain/src/benchmarks/immutableSet/utils.kt diff --git a/benchmarks-runner/build.gradle b/benchmarks/runner/build.gradle similarity index 97% rename from benchmarks-runner/build.gradle rename to benchmarks/runner/build.gradle index f1d380ae..516bc097 100644 --- a/benchmarks-runner/build.gradle +++ b/benchmarks/runner/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib" implementation 'org.openjdk.jmh:jmh-core:1.21' - runtimeOnly project(path: ':benchmarks-mpp', configuration: 'benchmarksJar') + runtimeOnly project(path: ':benchmarks', configuration: 'benchmarksJar') runtimeOnly project(path: ':kotlinx-collections-immutable') } diff --git a/settings.gradle b/settings.gradle index a0183fb4..b3a104e8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,5 +14,5 @@ rootProject.name = 'Kotlin-Immutable-Collections' // TODO: Make readable name wh include ':core' project(":core").name='kotlinx-collections-immutable' -include ':benchmarks-mpp' +include ':benchmarks' include ':benchmarks-runner' \ No newline at end of file From 99a55564d683250a9d9a3d9995ad9a592da647b3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 31 Oct 2019 17:53:22 +0300 Subject: [PATCH 003/162] Place runner inside benchmarks project --- benchmarks/runner/build.gradle | 2 ++ .../src/main/kotlin => benchmarks/runner/src}/contants.kt | 0 .../src/main/kotlin => benchmarks/runner/src}/csvPrinter.kt | 0 .../main/kotlin => benchmarks/runner/src}/customRunResult.kt | 0 .../kotlin => benchmarks/runner/src}/regressionCalculator.kt | 0 .../src/main/kotlin => benchmarks/runner/src}/reportPrinter.kt | 0 .../src/main/kotlin => benchmarks/runner/src}/runUtils.kt | 0 .../runner/src}/runners/hashMapBuilderRunner.kt | 0 .../kotlin => benchmarks/runner/src}/runners/hashMapRunner.kt | 0 .../runner/src}/runners/hashSetBuilderRunner.kt | 0 .../kotlin => benchmarks/runner/src}/runners/hashSetRunner.kt | 0 .../runner/src}/runners/listBuilderRunner.kt | 0 .../main/kotlin => benchmarks/runner/src}/runners/listRunner.kt | 0 .../runner/src}/runners/orderedMapBuilderRunner.kt | 0 .../runner/src}/runners/orderedMapRunner.kt | 0 .../runner/src}/runners/orderedSetBuilderRunner.kt | 0 .../runner/src}/runners/orderedSetRunner.kt | 0 settings.gradle | 2 +- 18 files changed, 3 insertions(+), 1 deletion(-) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/contants.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/csvPrinter.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/customRunResult.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/regressionCalculator.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/reportPrinter.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runUtils.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/hashMapBuilderRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/hashMapRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/hashSetBuilderRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/hashSetRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/listBuilderRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/listRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/orderedMapBuilderRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/orderedMapRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/orderedSetBuilderRunner.kt (100%) rename {benchmarks-runner/src/main/kotlin => benchmarks/runner/src}/runners/orderedSetRunner.kt (100%) diff --git a/benchmarks/runner/build.gradle b/benchmarks/runner/build.gradle index 516bc097..3a080dbb 100644 --- a/benchmarks/runner/build.gradle +++ b/benchmarks/runner/build.gradle @@ -16,6 +16,8 @@ dependencies { runtimeOnly project(path: ':kotlinx-collections-immutable') } +sourceSets.main.kotlin.srcDirs "src" + // map task benchmarkHashMap(type: JavaExec, group: "Benchmark") { main = 'runners.HashMapRunnerKt' diff --git a/benchmarks-runner/src/main/kotlin/contants.kt b/benchmarks/runner/src/contants.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/contants.kt rename to benchmarks/runner/src/contants.kt diff --git a/benchmarks-runner/src/main/kotlin/csvPrinter.kt b/benchmarks/runner/src/csvPrinter.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/csvPrinter.kt rename to benchmarks/runner/src/csvPrinter.kt diff --git a/benchmarks-runner/src/main/kotlin/customRunResult.kt b/benchmarks/runner/src/customRunResult.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/customRunResult.kt rename to benchmarks/runner/src/customRunResult.kt diff --git a/benchmarks-runner/src/main/kotlin/regressionCalculator.kt b/benchmarks/runner/src/regressionCalculator.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/regressionCalculator.kt rename to benchmarks/runner/src/regressionCalculator.kt diff --git a/benchmarks-runner/src/main/kotlin/reportPrinter.kt b/benchmarks/runner/src/reportPrinter.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/reportPrinter.kt rename to benchmarks/runner/src/reportPrinter.kt diff --git a/benchmarks-runner/src/main/kotlin/runUtils.kt b/benchmarks/runner/src/runUtils.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runUtils.kt rename to benchmarks/runner/src/runUtils.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/hashMapBuilderRunner.kt b/benchmarks/runner/src/runners/hashMapBuilderRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/hashMapBuilderRunner.kt rename to benchmarks/runner/src/runners/hashMapBuilderRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/hashMapRunner.kt b/benchmarks/runner/src/runners/hashMapRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/hashMapRunner.kt rename to benchmarks/runner/src/runners/hashMapRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/hashSetBuilderRunner.kt b/benchmarks/runner/src/runners/hashSetBuilderRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/hashSetBuilderRunner.kt rename to benchmarks/runner/src/runners/hashSetBuilderRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/hashSetRunner.kt b/benchmarks/runner/src/runners/hashSetRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/hashSetRunner.kt rename to benchmarks/runner/src/runners/hashSetRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/listBuilderRunner.kt b/benchmarks/runner/src/runners/listBuilderRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/listBuilderRunner.kt rename to benchmarks/runner/src/runners/listBuilderRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/listRunner.kt b/benchmarks/runner/src/runners/listRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/listRunner.kt rename to benchmarks/runner/src/runners/listRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/orderedMapBuilderRunner.kt b/benchmarks/runner/src/runners/orderedMapBuilderRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/orderedMapBuilderRunner.kt rename to benchmarks/runner/src/runners/orderedMapBuilderRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/orderedMapRunner.kt b/benchmarks/runner/src/runners/orderedMapRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/orderedMapRunner.kt rename to benchmarks/runner/src/runners/orderedMapRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/orderedSetBuilderRunner.kt b/benchmarks/runner/src/runners/orderedSetBuilderRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/orderedSetBuilderRunner.kt rename to benchmarks/runner/src/runners/orderedSetBuilderRunner.kt diff --git a/benchmarks-runner/src/main/kotlin/runners/orderedSetRunner.kt b/benchmarks/runner/src/runners/orderedSetRunner.kt similarity index 100% rename from benchmarks-runner/src/main/kotlin/runners/orderedSetRunner.kt rename to benchmarks/runner/src/runners/orderedSetRunner.kt diff --git a/settings.gradle b/settings.gradle index b3a104e8..b254aa69 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,4 @@ include ':core' project(":core").name='kotlinx-collections-immutable' include ':benchmarks' -include ':benchmarks-runner' \ No newline at end of file +include ':benchmarks:runner' \ No newline at end of file From 6fe71c1e08b472041e908ff4ee722cb5b4035085 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 5 Nov 2019 20:24:00 +0300 Subject: [PATCH 004/162] Benchmark configs --- .teamcity/settings.kts | 63 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index c9da575e..fa3d2198 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -65,7 +65,13 @@ project { } } - buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray()) + val benchmarks = listOf( + benchmark("js", "Linux"), + benchmark("jvm", "Linux"), + *platforms.map { benchmark("native", it) }.toTypedArray() + ) + + buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray(), *benchmarks.toTypedArray()) } fun Project.buildVersion() = BuildType { @@ -136,6 +142,61 @@ fun Project.build(platform: String, versionBuild: BuildType) = platform(platform artifactRules = "+:build/maven=>maven\n+:build/api=>api" } +fun Project.benchmark(target: String, platform: String) = BuildType { + // ID is prepended with Project ID, so don't repeat it here + // ID should conform to identifier rules, so just letters, numbers and underscore + id("${target}Benchmark_${platform.substringBefore(" ")}") + // Display name of the build configuration + this.name = "${target}Benchmark ($platform)" + + + steps { + gradle { + name = "Benchmark" + tasks = "${target}Benchmark" + jdkHome = "%env.$jdk%" + param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") + } + } + + // What files to publish as build artifacts + artifactRules = "benchmarks/build/reports/**=> reports" + + requirements { + equals("system.ec2.instance-type", "m5d.xlarge") + contains("teamcity.agent.jvm.os.name", platform) + noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") + } + + params { + // This parameter is needed for macOS agent to be compatible + if (platform.startsWith("Mac")) param("env.JDK_17", "") + } + + // Allow to fetch build status through API for badges + allowExternalStatus = true + + // Configure VCS, by default use the same and only VCS root from which this configuration is fetched + vcs { + root(DslContext.settingsRoot) + showDependenciesChanges = true + checkoutMode = CheckoutMode.ON_AGENT + } + + failureConditions { + errorMessage = true + nonZeroExitCode = true + executionTimeoutMin = 1440 + } + + features { + feature { + id = "perfmon" + type = "perfmon" + } + } +}.also { buildType(it) } + fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = apply { dependencies.dependency(build, configure) From e6fa6a92c107658efaebf4804a175d5adc0cec13 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 11 Nov 2019 21:27:03 +0300 Subject: [PATCH 005/162] Set benchmarks build file path --- .teamcity/settings.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index fa3d2198..4637d7b8 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -156,6 +156,8 @@ fun Project.benchmark(target: String, platform: String) = BuildType { tasks = "${target}Benchmark" jdkHome = "%env.$jdk%" param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") + buildFile = "" + gradleWrapperPath = "" } } From 14eac1f647e792036a410f95c7fee22e61f4cbaa Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 13 Nov 2019 17:03:58 +0300 Subject: [PATCH 006/162] Fix native benchmarks task name --- .teamcity/settings.kts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 4637d7b8..68e97c02 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -1,6 +1,7 @@ import jetbrains.buildServer.configs.kotlin.v2018_2.* import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.* import jetbrains.buildServer.configs.kotlin.v2018_2.triggers.* +import java.lang.IllegalArgumentException /* The settings script is an entry point for defining a TeamCity @@ -149,11 +150,10 @@ fun Project.benchmark(target: String, platform: String) = BuildType { // Display name of the build configuration this.name = "${target}Benchmark ($platform)" - steps { gradle { name = "Benchmark" - tasks = "${target}Benchmark" + tasks = "${if (target == "native") nativeTarget(platform) else target}Benchmark" jdkHome = "%env.$jdk%" param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") buildFile = "" @@ -199,6 +199,13 @@ fun Project.benchmark(target: String, platform: String) = BuildType { } }.also { buildType(it) } +fun nativeTarget(platform: String): String = when(platform) { + "Mac OS X" -> "macosX64" + "Linux" -> "linuxX64" + "Windows" -> "mingwX64" + else -> throw IllegalArgumentException("Unknown platform: $platform") +} + fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = apply { dependencies.dependency(build, configure) From 829446ab9f3f7c88652826b760b32cea87f8c1bd Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 13 Nov 2019 18:10:36 +0300 Subject: [PATCH 007/162] Fix agents instance type requirement --- .teamcity/settings.kts | 56 ++++++++++++------------------------------ 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 68e97c02..fcd6ce13 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -143,17 +143,11 @@ fun Project.build(platform: String, versionBuild: BuildType) = platform(platform artifactRules = "+:build/maven=>maven\n+:build/api=>api" } -fun Project.benchmark(target: String, platform: String) = BuildType { - // ID is prepended with Project ID, so don't repeat it here - // ID should conform to identifier rules, so just letters, numbers and underscore - id("${target}Benchmark_${platform.substringBefore(" ")}") - // Display name of the build configuration - this.name = "${target}Benchmark ($platform)" - +fun Project.benchmark(target: String, platform: String) = platform(platform, "${target}Benchmark") { steps { gradle { name = "Benchmark" - tasks = "${if (target == "native") nativeTarget(platform) else target}Benchmark" + tasks = benchmarkTask(target, platform) jdkHome = "%env.$jdk%" param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") buildFile = "" @@ -161,49 +155,31 @@ fun Project.benchmark(target: String, platform: String) = BuildType { } } - // What files to publish as build artifacts artifactRules = "benchmarks/build/reports/**=> reports" requirements { - equals("system.ec2.instance-type", "m5d.xlarge") - contains("teamcity.agent.jvm.os.name", platform) - noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") - } - - params { - // This parameter is needed for macOS agent to be compatible - if (platform.startsWith("Mac")) param("env.JDK_17", "") - } - - // Allow to fetch build status through API for badges - allowExternalStatus = true - - // Configure VCS, by default use the same and only VCS root from which this configuration is fetched - vcs { - root(DslContext.settingsRoot) - showDependenciesChanges = true - checkoutMode = CheckoutMode.ON_AGENT + benchmarkAgentInstanceTypeRequirement(platform) } failureConditions { - errorMessage = true - nonZeroExitCode = true executionTimeoutMin = 1440 } +} - features { - feature { - id = "perfmon" - type = "perfmon" - } +fun benchmarkTask(target: String, platform: String): String = when(target) { + "js", "jvm" -> "${target}Benchmark" + "native" -> when(platform) { + "Mac OS X" -> "macosX64Benchmark" + "Linux" -> "linuxX64Benchmark" + "Windows" -> "mingwX64Benchmark" + else -> throw IllegalArgumentException("Unknown platform: $platform") } -}.also { buildType(it) } + else -> throw IllegalArgumentException("Unknown target: $target") +} -fun nativeTarget(platform: String): String = when(platform) { - "Mac OS X" -> "macosX64" - "Linux" -> "linuxX64" - "Windows" -> "mingwX64" - else -> throw IllegalArgumentException("Unknown platform: $platform") +fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: String) { + if (platform == "Linux") equals("system.ec2.instance-type", "m5d.xlarge") + else if (platform == "Windows") equals("system.ec2.instance-type", "m5.xlarge") } fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = From dcb7755d10d273f54ecf82fb58d1291613c8211f Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 13 Mar 2020 11:05:58 +0300 Subject: [PATCH 008/162] Update kotlin version to 1.3.70 --- benchmarks/build.gradle.kts | 4 ++-- build.gradle.kts | 4 ++-- core/build.gradle.kts | 6 +++++- gradle.properties | 4 +++- settings.gradle | 1 - 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 34b0672a..890afaf7 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("kotlinx.benchmark") version "0.2.0-dev-6" + id("kotlinx.benchmark") version "0.2.0-dev-8" } @@ -47,7 +47,7 @@ kotlin { commonMain { dependencies { api("org.jetbrains.kotlin:kotlin-stdlib-common") - api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-6") + api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8") api(project(":kotlinx-collections-immutable")) } } diff --git a/build.gradle.kts b/build.gradle.kts index 45a8e8e0..ee0c4652 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,12 +4,12 @@ buildscript { maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70") } } plugins { - id("kotlinx.team.infra") version "0.1.0-dev-49" + id("kotlinx.team.infra") version "0.1.0-dev-52" } infra { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 9a38d3f1..fcba2b31 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -30,7 +30,11 @@ kotlin { js { nodejs { -// testTask { } + testTask { + useMocha { + timeout = "30000" + } + } } compilations.all { kotlinOptions { diff --git a/gradle.properties b/gradle.properties index 637c9e80..c7828ca9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,5 @@ group=org.jetbrains.kotlinx version=0.4 -versionSuffix=SNAPSHOT \ No newline at end of file +versionSuffix=SNAPSHOT + +org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index b254aa69..8f34a2e7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ pluginManagement { repositories { - maven { url 'https://dl.bintray.com/orangy/maven' } maven { url 'https://dl.bintray.com/kotlin/kotlinx' } maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } gradlePluginPortal() From 99659181872632b7b60712d08bc93ec426b42d04 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 18 Mar 2020 18:35:59 +0300 Subject: [PATCH 009/162] Introduce separate methods for creating empty maps and sets --- core/commonMain/src/extensions.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index a33da718..2322f495 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -448,6 +448,11 @@ fun persistentSetOf(): PersistentSet = PersistentOrderedSet.emptyOf() */ fun persistentHashSetOf(vararg elements: E): PersistentSet = PersistentHashSet.emptyOf().addAll(elements.asList()) +/** + * Returns an empty persistent set. + */ +fun persistentHashSetOf(): PersistentSet = PersistentHashSet.emptyOf() + /** * Returns a new persistent map with the specified contents, given as a list of pairs @@ -459,6 +464,12 @@ fun persistentHashSetOf(vararg elements: E): PersistentSet = PersistentHa */ fun persistentMapOf(vararg pairs: Pair): PersistentMap = PersistentOrderedMap.emptyOf().mutate { it += pairs } +/** + * Returns an empty persistent map. + */ +fun persistentMapOf(): PersistentMap = PersistentOrderedMap.emptyOf() + + /** * Returns a new persistent map with the specified contents, given as a list of pairs * where the first component is the key and the second is the value. @@ -469,6 +480,11 @@ fun persistentMapOf(vararg pairs: Pair): PersistentMap = Pers */ fun persistentHashMapOf(vararg pairs: Pair): PersistentMap = PersistentHashMap.emptyOf().mutate { it += pairs } +/** + * Returns an empty persistent map. + */ +fun persistentHashMapOf(): PersistentMap = PersistentHashMap.emptyOf() + /** * Returns a new persistent list of the specified elements. From a5f78bc9efbc7a89ecfea7663c52ddae565af55b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 26 Mar 2020 19:56:57 +0300 Subject: [PATCH 010/162] Small refactoring in Set builder iterator --- .../immutableSet/PersistentHashSetMutableIterator.kt | 9 ++++----- .../src/implementations/immutableSet/TrieNode.kt | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt index dd08a99a..23975e9c 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetMutableIterator.kt @@ -26,10 +26,10 @@ internal class PersistentHashSetMutableIterator(private val builder: Persiste if (hasNext()) { val currentElement = currentElement() - assert(builder.remove(lastIteratedElement)) + builder.remove(lastIteratedElement) resetPath(currentElement.hashCode(), builder.node, currentElement, 0) } else { - assert(builder.remove(lastIteratedElement)) + builder.remove(lastIteratedElement) } lastIteratedElement = null @@ -46,9 +46,8 @@ internal class PersistentHashSetMutableIterator(private val builder: Persiste return } - val position = 1 shl ((hashCode shr (pathIndex * LOG_MAX_BRANCHING_FACTOR)) and MAX_BRANCHING_FACTOR_MINUS_ONE) - @UseExperimental(ExperimentalStdlibApi::class) - val index = (node.bitmap and (position - 1)).countOneBits() + val position = 1 shl indexSegment(hashCode, pathIndex * LOG_MAX_BRANCHING_FACTOR) + val index = node.indexOfCellAt(position) path[pathIndex].reset(node.buffer, index) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 59aa94e0..8cc97736 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -54,7 +54,7 @@ internal class TrieNode( } @UseExperimental(ExperimentalStdlibApi::class) - private fun indexOfCellAt(positionMask: Int): Int { + internal fun indexOfCellAt(positionMask: Int): Int { return (bitmap and (positionMask - 1)).countOneBits() } From 5a7318069e7ceae833002463689ac4dc0887ff14 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 00:17:41 +0300 Subject: [PATCH 011/162] Fix memory leak in map iterator --- .../immutableMap/PersistentHashMapContentIterators.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt index 6da0237c..302ac25f 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt @@ -98,8 +98,11 @@ internal open class MapEntry(override val key: K, override val val } -internal abstract class PersistentHashMapBaseIterator(node: TrieNode, - protected val path: Array>) : Iterator { +internal abstract class PersistentHashMapBaseIterator( + node: TrieNode, + protected val path: Array> +) : Iterator { + private var pathLastIndex = 0 @JsName("_hasNext") private var hasNext = true @@ -144,6 +147,7 @@ internal abstract class PersistentHashMapBaseIterator(node: TrieNode 0) { path[i - 1].moveToNextNode() } + path[i].reset(TrieNode.EMPTY.buffer, 0) } hasNext = false } From e8122de195f0522a3daf1a458f6d4f34fb9208b3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 00:20:41 +0300 Subject: [PATCH 012/162] Fix memory leak in set iterator --- .../implementations/immutableSet/PersistentHashSetIterator.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetIterator.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetIterator.kt index 7cf7558c..a560f0ae 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetIterator.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetIterator.kt @@ -54,6 +54,7 @@ internal open class PersistentHashSetIterator(node: TrieNode) : Iterator 0) { path[i - 1].moveToNextCell() } + path[i].reset(TrieNode.EMPTY.buffer, 0) } hasNext = false } From 9ea68d0fc9b99ad56a3d6c9013a1510281760b69 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 04:23:27 +0300 Subject: [PATCH 013/162] Add Map keys/values/entries iterator tests --- .../map/PersistentHashMapBuilderTest.kt | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt index 33c5277a..fcba1e5b 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt @@ -107,6 +107,124 @@ class PersistentHashMapBuilderTest : ExecutionTimeMeasuringTest() { } } + private fun testAfterRandomPut(block: (MutableMap, PersistentMap) -> Unit) { + val elementsToAdd = NForAlgorithmComplexity.O_NNlogN + + var map = persistentHashMapOf() + val expected = hashMapOf() + + repeat(times = elementsToAdd) { + val keyValue = Random.nextInt() + val keyHash = Random.nextInt(elementsToAdd) // to have collisions + val key = IntWrapper(keyValue, keyHash) + + expected[key] = keyValue + map = map.put(key, keyValue) + + val shouldTest = Random.nextDouble() < 0.1 + if (shouldTest) { + block(expected, map) + } + } + } + + @Test + fun keysIteratorTests() { + fun testKeysIterator(expected: MutableMap, actual: PersistentMap.Builder) { + var expectedSize = actual.size + while (expectedSize > 0) { + assertEquals(expectedSize, actual.size) + + val iterator = actual.keys.iterator() + repeat(expectedSize) { + assertTrue(iterator.hasNext()) + + val nextKey = iterator.next() + assertEquals(expected[nextKey], actual[nextKey]) + + val shouldRemove = Random.nextDouble() < 0.2 + if (shouldRemove) { + iterator.remove() + expectedSize-- + } + } + assertFalse(iterator.hasNext()) + } + + assertTrue(actual.isEmpty()) + } + + testAfterRandomPut { expected, map -> + testKeysIterator(expected, map.builder()) + } + } + + @Test + fun valuesIteratorTests() { + fun testValuesIterator(actual: PersistentMap.Builder) { + var expectedSize = actual.size + while (expectedSize > 0) { + assertEquals(expectedSize, actual.size) + + val iterator = actual.values.iterator() + repeat(expectedSize) { + assertTrue(iterator.hasNext()) + + iterator.next() + + val shouldRemove = Random.nextDouble() < 0.2 + if (shouldRemove) { + iterator.remove() + expectedSize-- + } + } + assertFalse(iterator.hasNext()) + } + + assertTrue(actual.isEmpty()) + } + + testAfterRandomPut { _, map -> + testValuesIterator(map.builder()) + } + } + + @Test + fun entriesIteratorTests() { + fun testEntriesIterator(expected: MutableMap, actual: PersistentMap.Builder) { + var expectedSize = actual.size + while (expectedSize > 0) { + assertEquals(expectedSize, actual.size) + + val iterator = actual.entries.iterator() + repeat(expectedSize) { + assertTrue(iterator.hasNext()) + + val nextEntry = iterator.next() + assertEquals(expected[nextEntry.key], actual[nextEntry.key]) + + val shouldUpdate = Random.nextDouble() < 0.1 + if (shouldUpdate) { + val newValue = Random.nextInt() + assertEquals(expected.put(nextEntry.key, newValue), nextEntry.setValue(newValue)) + } + val shouldRemove = Random.nextDouble() < 0.2 + if (shouldRemove) { + iterator.remove() + expectedSize-- + } + } + assertFalse(iterator.hasNext()) + } + + assertTrue(actual.isEmpty()) + } + + testAfterRandomPut { expected, map -> + testEntriesIterator(expected.toMutableMap(), map.builder()) + } + } + @Test fun removeTests() { val builder = persistentHashMapOf().builder() From 34652b1b474cdb227ea355b97e18da7a869c4970 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 04:29:04 +0300 Subject: [PATCH 014/162] Fix setValue method of MutableMapEntry --- ...ersistentHashMapBuilderContentIterators.kt | 80 ++++++++++++++----- .../PersistentHashMapContentIterators.kt | 2 +- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt index e3bd0906..986369ca 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt @@ -8,32 +8,37 @@ package kotlinx.collections.immutable.implementations.immutableMap import kotlinx.collections.immutable.internal.assert -internal class TrieNodeMutableEntriesIterator(private val builder: PersistentHashMapBuilder) - : TrieNodeBaseIterator>() { +internal class TrieNodeMutableEntriesIterator( + private val parentIterator: PersistentHashMapBuilderEntriesIterator +) : TrieNodeBaseIterator>() { override fun next(): MutableMap.MutableEntry { assert(hasNextKey()) index += 2 @Suppress("UNCHECKED_CAST") - return MutableMapEntry(builder, buffer[index - 2] as K, buffer[index - 1] as V) + return MutableMapEntry(parentIterator, buffer[index - 2] as K, buffer[index - 1] as V) } } -private class MutableMapEntry(private val builder: PersistentHashMapBuilder, - key: K, - override var value: V) : MapEntry(key, value), MutableMap.MutableEntry { +private class MutableMapEntry( + private val parentIterator: PersistentHashMapBuilderEntriesIterator, + key: K, + override var value: V +) : MapEntry(key, value), MutableMap.MutableEntry { + override fun setValue(newValue: V): V { val result = value value = newValue - builder[key] = newValue + parentIterator.setValue(key, newValue) return result } } -internal abstract class PersistentHashMapBuilderBaseIterator(private val builder: PersistentHashMapBuilder, - path: Array>) - : MutableIterator, PersistentHashMapBaseIterator(builder.node, path) { +internal open class PersistentHashMapBuilderBaseIterator( + private val builder: PersistentHashMapBuilder, + path: Array> +) : MutableIterator, PersistentHashMapBaseIterator(builder.node, path) { private var lastIteratedKey: K? = null private var nextWasInvoked = false @@ -62,8 +67,33 @@ internal abstract class PersistentHashMapBuilderBaseIterator(private va expectedModCount = builder.modCount } + fun setValue(key: K, newValue: V) { + if (!builder.containsKey(key)) return + + if (hasNext()) { + val currentKey = currentKey() + + builder[key] = newValue + resetPath(currentKey.hashCode(), builder.node, currentKey, 0) + } else { + builder[key] = newValue + } + + expectedModCount = builder.modCount + } + private fun resetPath(keyHash: Int, node: TrieNode<*, *>, key: K, pathIndex: Int) { val shift = pathIndex * LOG_MAX_BRANCHING_FACTOR + + if (shift > MAX_SHIFT) { // collision + path[pathIndex].reset(node.buffer, node.buffer.size, 0) + while (path[pathIndex].currentKey() != key) { + path[pathIndex].moveToNextKey() + } + pathLastIndex = pathIndex + return + } + val keyPositionMask = 1 shl indexSegment(keyHash, shift) if (node.hasEntryAt(keyPositionMask)) { // key is directly in buffer @@ -72,22 +102,16 @@ internal abstract class PersistentHashMapBuilderBaseIterator(private va // assert(node.keyAtIndex(keyIndex) == key) path[pathIndex].reset(node.buffer, ENTRY_SIZE * node.entryCount(), keyIndex) + pathLastIndex = pathIndex return } -// assert(node.hasNodeAt(keyPosition)) // key is in node +// assert(node.hasNodeAt(keyPositionMask)) // key is in node val nodeIndex = node.nodeIndex(keyPositionMask) val targetNode = node.nodeAtIndex(nodeIndex) - if (shift == MAX_SHIFT) { // collision - path[pathIndex].reset(node.buffer, node.buffer.size, 0) - while (path[pathIndex].currentKey() != key) { - path[pathIndex].moveToNextKey() - } - } else { - path[pathIndex].reset(node.buffer, ENTRY_SIZE * node.entryCount(), nodeIndex) - resetPath(keyHash, targetNode, key, pathIndex + 1) - } + path[pathIndex].reset(node.buffer, ENTRY_SIZE * node.entryCount(), nodeIndex) + resetPath(keyHash, targetNode, key, pathIndex + 1) } private fun checkNextWasInvoked() { @@ -101,8 +125,20 @@ internal abstract class PersistentHashMapBuilderBaseIterator(private va } } -internal class PersistentHashMapBuilderEntriesIterator(builder: PersistentHashMapBuilder) - : PersistentHashMapBuilderBaseIterator>(builder, Array(TRIE_MAX_HEIGHT + 1) { TrieNodeMutableEntriesIterator(builder) }) +internal class PersistentHashMapBuilderEntriesIterator( + builder: PersistentHashMapBuilder +) : MutableIterator> { + private val base = PersistentHashMapBuilderBaseIterator>( + builder, + Array(TRIE_MAX_HEIGHT + 1) { TrieNodeMutableEntriesIterator(this) } + ) + + override fun hasNext(): Boolean = base.hasNext() + override fun next(): MutableMap.MutableEntry = base.next() + override fun remove(): Unit = base.remove() + + fun setValue(key: K, newValue: V): Unit = base.setValue(key, newValue) +} internal class PersistentHashMapBuilderKeysIterator(builder: PersistentHashMapBuilder) : PersistentHashMapBuilderBaseIterator(builder, Array(TRIE_MAX_HEIGHT + 1) { TrieNodeKeysIterator() }) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt index 302ac25f..305dcce4 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentIterators.kt @@ -103,7 +103,7 @@ internal abstract class PersistentHashMapBaseIterator( protected val path: Array> ) : Iterator { - private var pathLastIndex = 0 + protected var pathLastIndex = 0 @JsName("_hasNext") private var hasNext = true From b2c46f7bb07b0c5d3e6ece464f8067f35d6d5045 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 04:37:10 +0300 Subject: [PATCH 015/162] Do not canonicalize MapBuilder's underlying trie Because it changes iteration order of elements and leads to inconsistency if an iteration is already in progress --- .../immutableMap/PersistentHashMap.kt | 2 + .../immutableMap/PersistentHashMapBuilder.kt | 4 +- .../implementations/immutableMap/TrieNode.kt | 123 +++++++++++------- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt index 0bafd4db..6df0dc8e 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt @@ -51,12 +51,14 @@ internal class PersistentHashMap(internal val node: TrieNode, override fun remove(key: K): PersistentHashMap { val newNode = node.remove(key.hashCode(), key, 0) if (node === newNode) { return this } + if (newNode == null) { return emptyOf() } return PersistentHashMap(newNode, size - 1) } override fun remove(key: K, value: @UnsafeVariance V): PersistentHashMap { val newNode = node.remove(key.hashCode(), key, value, 0) if (node === newNode) { return this } + if (newNode == null) { return emptyOf() } return PersistentHashMap(newNode, size - 1) } diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index d11385f2..2f1383e2 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -64,14 +64,14 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap override fun remove(key: K): V? { operationResult = null @Suppress("UNCHECKED_CAST") - node = node.mutableRemove(key.hashCode(), key, 0, this) + node = node.mutableRemove(key.hashCode(), key, 0, this) ?: TrieNode.EMPTY as TrieNode return operationResult } fun remove(key: K, value: V): Boolean { val oldSize = size @Suppress("UNCHECKED_CAST") - node = node.mutableRemove(key.hashCode(), key, value, 0, this) + node = node.mutableRemove(key.hashCode(), key, value, 0, this) ?: TrieNode.EMPTY as TrieNode return oldSize != size } diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 6b4172d5..2cff74c3 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -58,6 +58,13 @@ private fun Array.removeEntryAtIndex(keyIndex: Int): Array { return newBuffer } +private fun Array.removeNodeAtIndex(nodeIndex: Int): Array { + val newBuffer = arrayOfNulls(this.size - 1) + this.copyInto(newBuffer, endIndex = nodeIndex) + this.copyInto(newBuffer, nodeIndex, startIndex = nodeIndex + 1, endIndex = this.size) + return newBuffer +} + internal class TrieNode( @@ -194,28 +201,14 @@ internal class TrieNode( } /** The given [newNode] must not be a part of any persistent map instance. */ - private fun mutableUpdateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { + private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { // assert(buffer[nodeIndex] !== newNode) - val newNodeBuffer = newNode.buffer - if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) { - if (buffer.size == 1) { -// assert(dataMap == 0 && nodeMap xor positionMask == 0) - newNode.dataMap = nodeMap - return newNode - } - - val keyIndex = entryKeyIndex(positionMask) - val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1]) - - if (ownedBy === owner) { - buffer = newBuffer - dataMap = dataMap xor positionMask - nodeMap = nodeMap xor positionMask - return this - } - - return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer) + // nodes (including collision nodes) that have only one entry are upped if they have no siblings + if (buffer.size == 1 && newNode.buffer.size == ENTRY_SIZE && newNode.nodeMap == 0) { +// assert(dataMap == 0 && nodeMap xor positionMask == 0) + newNode.dataMap = nodeMap + return newNode } if (ownedBy === owner) { @@ -227,6 +220,28 @@ internal class TrieNode( return TrieNode(dataMap, nodeMap, newBuffer, owner) } + private fun removeNodeAtIndex(nodeIndex: Int, positionMask: Int): TrieNode? { +// assert(hasNodeAt(positionMask)) + if (buffer.size == 1) return null + + val newBuffer = buffer.removeNodeAtIndex(nodeIndex) + return TrieNode(dataMap, nodeMap xor positionMask, newBuffer) + } + + private fun mutableRemoveNodeAtIndex(nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode? { +// assert(hasNodeAt(positionMask)) + if (buffer.size == 1) return null + + if (ownedBy === owner) { + buffer = buffer.removeNodeAtIndex(nodeIndex) + nodeMap = nodeMap xor positionMask + return this + } + val newBuffer = buffer.removeNodeAtIndex(nodeIndex) + return TrieNode(dataMap, nodeMap xor positionMask, newBuffer, owner) + } + + private fun bufferMoveEntryToNode(keyIndex: Int, positionMask: Int, newKeyHash: Int, newKey: K, newValue: V, shift: Int, owner: MutabilityOwnership?): Array { val storedKey = keyAtIndex(keyIndex) @@ -290,18 +305,18 @@ internal class TrieNode( return TrieNode(0, 1 shl setBit1, arrayOf(node), owner) } - private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode { + private fun removeEntryAtIndex(keyIndex: Int, positionMask: Int): TrieNode? { // assert(hasEntryAt(positionMask)) -// assert(buffer.size > ENTRY_SIZE) // can be false only for the root node + if (buffer.size == ENTRY_SIZE) return null val newBuffer = buffer.removeEntryAtIndex(keyIndex) return TrieNode(dataMap xor positionMask, nodeMap, newBuffer) } - private fun mutableRemoveEntryAtIndex(keyIndex: Int, positionMask: Int, mutator: PersistentHashMapBuilder): TrieNode { + private fun mutableRemoveEntryAtIndex(keyIndex: Int, positionMask: Int, mutator: PersistentHashMapBuilder): TrieNode? { // assert(hasEntryAt(positionMask)) -// assert(buffer.size > ENTRY_SIZE) mutator.size-- mutator.operationResult = valueAtKeyIndex(keyIndex) + if (buffer.size == ENTRY_SIZE) return null if (ownedBy === mutator.ownership) { buffer = buffer.removeEntryAtIndex(keyIndex) @@ -312,16 +327,16 @@ internal class TrieNode( return TrieNode(dataMap xor positionMask, nodeMap, newBuffer, mutator.ownership) } - private fun collisionRemoveEntryAtIndex(i: Int): TrieNode { -// assert(buffer.size > ENTRY_SIZE) + private fun collisionRemoveEntryAtIndex(i: Int): TrieNode? { + if (buffer.size == ENTRY_SIZE) return null val newBuffer = buffer.removeEntryAtIndex(i) return TrieNode(0, 0, newBuffer) } - private fun mutableCollisionRemoveEntryAtIndex(i: Int, mutator: PersistentHashMapBuilder): TrieNode { -// assert(buffer.size > ENTRY_SIZE) + private fun mutableCollisionRemoveEntryAtIndex(i: Int, mutator: PersistentHashMapBuilder): TrieNode? { mutator.size-- mutator.operationResult = valueAtKeyIndex(i) + if (buffer.size == ENTRY_SIZE) return null if (ownedBy === mutator.ownership) { buffer = buffer.removeEntryAtIndex(i) @@ -388,7 +403,7 @@ internal class TrieNode( return TrieNode(0, 0, newBuffer, mutator.ownership) } - private fun collisionRemove(key: K): TrieNode { + private fun collisionRemove(key: K): TrieNode? { for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { return collisionRemoveEntryAtIndex(i) @@ -397,7 +412,7 @@ internal class TrieNode( return this } - private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder): TrieNode { + private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder): TrieNode? { for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i)) { return mutableCollisionRemoveEntryAtIndex(i, mutator) @@ -406,7 +421,7 @@ internal class TrieNode( return this } - private fun collisionRemove(key: K, value: V): TrieNode { + private fun collisionRemove(key: K, value: V): TrieNode? { for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { return collisionRemoveEntryAtIndex(i) @@ -415,7 +430,7 @@ internal class TrieNode( return this } - private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode { + private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode? { for (i in 0 until buffer.size step ENTRY_SIZE) { if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { return mutableCollisionRemoveEntryAtIndex(i, mutator) @@ -523,7 +538,7 @@ internal class TrieNode( if (targetNode === newNode) { return this } - return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership) + return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership) } // key is absent @@ -531,7 +546,7 @@ internal class TrieNode( return mutableInsertEntryAt(keyPositionMask, key, value, mutator.ownership) } - fun remove(keyHash: Int, key: K, shift: Int): TrieNode { + fun remove(keyHash: Int, key: K, shift: Int): TrieNode? { val keyPositionMask = 1 shl indexSegment(keyHash, shift) if (hasEntryAt(keyPositionMask)) { // key is directly in buffer @@ -551,15 +566,23 @@ internal class TrieNode( } else { targetNode.remove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR) } - if (targetNode === newNode) return this - return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode) + return replaceNode(targetNode, newNode, nodeIndex, keyPositionMask) } // key is absent return this } - fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder): TrieNode { + private fun replaceNode(targetNode: TrieNode, newNode: TrieNode?, nodeIndex: Int, positionMask: Int) = when { + newNode == null -> + removeNodeAtIndex(nodeIndex, positionMask) + targetNode !== newNode -> + updateNodeAtIndex(nodeIndex, positionMask, newNode) + else -> + this + } + + fun mutableRemove(keyHash: Int, key: K, shift: Int, mutator: PersistentHashMapBuilder): TrieNode? { val keyPositionMask = 1 shl indexSegment(keyHash, shift) if (hasEntryAt(keyPositionMask)) { // key is directly in buffer @@ -579,17 +602,23 @@ internal class TrieNode( } else { targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } - if (ownedBy === mutator.ownership || targetNode !== newNode) { - return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership) - } - return this + return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership) } // key is absent return this } - fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode { + private fun mutableReplaceNode(targetNode: TrieNode, newNode: TrieNode?, nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership) = when { + newNode == null -> + mutableRemoveNodeAtIndex(nodeIndex, positionMask, owner) + ownedBy === owner || targetNode !== newNode -> + mutableUpdateNodeAtIndex(nodeIndex, newNode, owner) + else -> + this + } + + fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode? { val keyPositionMask = 1 shl indexSegment(keyHash, shift) if (hasEntryAt(keyPositionMask)) { // key is directly in buffer @@ -609,15 +638,14 @@ internal class TrieNode( } else { targetNode.remove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR) } - if (targetNode === newNode) return this - return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode) + return replaceNode(targetNode, newNode, nodeIndex, keyPositionMask) } // key is absent return this } - fun mutableRemove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder): TrieNode { + fun mutableRemove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int, mutator: PersistentHashMapBuilder): TrieNode? { val keyPositionMask = 1 shl indexSegment(keyHash, shift) if (hasEntryAt(keyPositionMask)) { // key is directly in buffer @@ -637,10 +665,7 @@ internal class TrieNode( } else { targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } - if (ownedBy === mutator.ownership || targetNode !== newNode) { - return mutableUpdateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership) - } - return this + return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership) } // key is absent From 0a322dd3ccb7a306bcb02bac7c27ded3786dfa1b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 27 Mar 2020 04:41:06 +0300 Subject: [PATCH 016/162] Test that Map correctly handles non-canonical trie it derives from builder --- .../map/PersistentHashMapBuilderTest.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt index fcba1e5b..1fed738e 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt @@ -243,6 +243,53 @@ class PersistentHashMapBuilderTest : ExecutionTimeMeasuringTest() { } } + @Test + fun removeBuildTests() { + val builder = persistentHashMapOf().builder() + + val elementsToAddToBuilder = NForAlgorithmComplexity.O_NlogN + + val keyGen = WrapperGenerator(elementsToAddToBuilder) + val expectedKeys = hashSetOf() + + repeat(times = elementsToAddToBuilder) { + val keyValue = Random.nextInt() + val key = keyGen.wrapper(keyValue) + expectedKeys.add(key) + builder[key] = keyValue + } + + val elementsToRemoveFromBuilder = expectedKeys.size / 2 + expectedKeys.take(elementsToRemoveFromBuilder).forEach { key -> + expectedKeys.remove(key) + builder.remove(key) + } + + var map = builder.build() + + val elementsToRemoveFromMap = expectedKeys.size / 2 + expectedKeys.take(elementsToRemoveFromMap).forEach { key -> + expectedKeys.remove(key) + map = map.remove(key) + } + assertEquals>(expectedKeys, map.keys) + + val elementsToAddToMap = elementsToAddToBuilder - expectedKeys.size + repeat(elementsToAddToMap) { + val keyValue = Random.nextInt() + val key = keyGen.wrapper(keyValue) + expectedKeys.add(key) + map = map.put(key, keyValue) + } + assertEquals>(expectedKeys, map.keys) + + expectedKeys.toHashSet().forEach { key -> + expectedKeys.remove(key) + map = map.remove(key) + } + assertEquals>(expectedKeys, map.keys) + } + @Test fun removeEntryTests() { val builder = persistentHashMapOf().builder() From 553ab2487ae29a52f10ba4072f02a4d669baf1e4 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 9 Apr 2020 07:56:11 +0300 Subject: [PATCH 017/162] Update kotlin version to 1.3.71 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ee0c4652..e08b0c4e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ buildscript { maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.70") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.71") } } From a9fa662e1a4a2d10fbbd902ef2f6266922fc8693 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 10 Apr 2020 10:47:59 +0300 Subject: [PATCH 018/162] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2a5245..5d7311a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # CHANGELOG +## 0.3.2 + +- Introduce `persistentHashSetOf`, `persistentMapOf` and `persistentHashMapOf` methods that take no arguments and create an empty instance [#67](https://github.com/Kotlin/kotlinx.collections.immutable/issues/67). +- Fix map `entries`/`keys`/`values` iterators including [#68](https://github.com/Kotlin/kotlinx.collections.immutable/issues/68). + +## 0.3.1 + +- Update Kotlin version up to 1.3.70 + ## 0.3 - Turn the JVM-only project into a multiplatform library From c4df377b6f8a386166eac182351537ce1c3f5e95 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 10 Apr 2020 11:01:33 +0300 Subject: [PATCH 019/162] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 172d1283..f54823e2 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ collection.mutate { some_actions_on(it) } The library is published to [kotlinx](https://bintray.com/kotlin/kotlinx/kotlinx.collections.immutable) bintray repository and available in jcenter too. -The library depends on the Kotlin Standard Library of the version at least `1.3.50`. +The library depends on the Kotlin Standard Library of the version at least `1.3.70`. ### Gradle @@ -132,7 +132,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.2") } } } @@ -142,7 +142,7 @@ kotlin { To use the library in a JVM-only project add the platform to the artifact name, e.g.: ```groovy -implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3") +implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.2") ``` ### Maven @@ -166,7 +166,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3 + 0.3.2 ``` From 45316d4b87c4d82db80b75ef4b064f82bfaf6eba Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 5 Jun 2020 04:18:16 +0300 Subject: [PATCH 020/162] Fix creation of SmallPersistentList from mutable root buffer --- .../src/implementations/immutableList/PersistentVector.kt | 3 ++- core/commonTest/src/contract/list/ImmutableListTest.kt | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/commonMain/src/implementations/immutableList/PersistentVector.kt b/core/commonMain/src/implementations/immutableList/PersistentVector.kt index e6556d6f..d902297c 100644 --- a/core/commonMain/src/implementations/immutableList/PersistentVector.kt +++ b/core/commonMain/src/implementations/immutableList/PersistentVector.kt @@ -180,7 +180,8 @@ internal class PersistentVector(private val root: Array, */ private fun pullLastBufferFromRoot(root: Array, rootSize: Int, shift: Int): PersistentList { if (shift == 0) { - return SmallPersistentVector(root) + val buffer = if (root.size == MUTABLE_BUFFER_SIZE) root.copyOf(MAX_BUFFER_SIZE) else root + return SmallPersistentVector(buffer) } val tailCarry = ObjectRef(null) val newRoot = pullLastBuffer(root, shift, rootSize - 1, tailCarry)!! diff --git a/core/commonTest/src/contract/list/ImmutableListTest.kt b/core/commonTest/src/contract/list/ImmutableListTest.kt index ffd2df58..b6be9de2 100644 --- a/core/commonTest/src/contract/list/ImmutableListTest.kt +++ b/core/commonTest/src/contract/list/ImmutableListTest.kt @@ -96,6 +96,14 @@ class ImmutableListTest { assertEquals(emptyList(), list.clear()) } + @Test + fun smallPersistentListFromMutableBuffer() { + val list = List(33) { it } + var vector = persistentListOf().mutate { it.addAll(list) } + vector = vector.removeAt(vector.lastIndex) + assertEquals(list.dropLast(1), vector) + } + @Test fun subList() { val list = "abcxaxyz12".toImmutableList() val subList = list.subList(2, 5) // 2, 3, 4 From 7b4ad87ac14e326ee85094868e3ebdaa389a7ccf Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 6 Jul 2020 07:06:42 +0300 Subject: [PATCH 021/162] Extract benchmarks as a subproject in teamcity settings --- .teamcity/Benchmarks.kt | 113 ++++++++++++++++++++++++++++++++++++++++ .teamcity/settings.kts | 99 ++--------------------------------- 2 files changed, 116 insertions(+), 96 deletions(-) create mode 100644 .teamcity/Benchmarks.kt diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt new file mode 100644 index 00000000..5867b5db --- /dev/null +++ b/.teamcity/Benchmarks.kt @@ -0,0 +1,113 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +import jetbrains.buildServer.configs.kotlin.v2018_2.* +import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.gradle +import java.lang.IllegalArgumentException + +val platforms = listOf("Windows", "Linux", "Mac OS X") +val jdk = "JDK_18_x64" + +fun benchmarksProject() = Project { + this.id("Benchmarks") + this.name = "Benchmarks" + + val benchmarks = listOf( + benchmark("js", "Linux"), + benchmark("jvm", "Linux"), + *platforms.map { benchmark("native", it) }.toTypedArray() + ) + + buildTypesOrder = benchmarks +} + +fun Project.benchmark(target: String, platform: String) = platform(platform, "${target}Benchmark") { + steps { + gradle { + name = "Benchmark" + tasks = benchmarkTask(target, platform) + jdkHome = "%env.$jdk%" + param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") + buildFile = "" + gradleWrapperPath = "" + } + } + + artifactRules = "benchmarks/build/reports/**=> reports" + + requirements { + benchmarkAgentInstanceTypeRequirement(platform) + } + + failureConditions { + executionTimeoutMin = 1440 + } +} + +fun benchmarkTask(target: String, platform: String): String = when(target) { + "js", "jvm" -> "${target}Benchmark" + "native" -> when(platform) { + "Mac OS X" -> "macosX64Benchmark" + "Linux" -> "linuxX64Benchmark" + "Windows" -> "mingwX64Benchmark" + else -> throw IllegalArgumentException("Unknown platform: $platform") + } + else -> throw IllegalArgumentException("Unknown target: $target") +} + +fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: String) { + if (platform == "Linux") equals("system.ec2.instance-type", "m5d.xlarge") + else if (platform == "Windows") equals("system.ec2.instance-type", "m5.xlarge") +} + +fun Project.platform(platform: String, name: String, configure: BuildType.() -> Unit) = BuildType { + // ID is prepended with Project ID, so don't repeat it here + // ID should conform to identifier rules, so just letters, numbers and underscore + id("${name}_${platform.substringBefore(" ")}") + // Display name of the build configuration + this.name = "$name ($platform)" + + requirements { + contains("teamcity.agent.jvm.os.name", platform) + } + + params { + // This parameter is needed for macOS agent to be compatible + if (platform.startsWith("Mac")) param("env.JDK_17", "") + } + + commonConfigure() + configure() +}.also { buildType(it) } + + +fun BuildType.commonConfigure() { + requirements { + noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") + } + + // Allow to fetch build status through API for badges + allowExternalStatus = true + + // Configure VCS, by default use the same and only VCS root from which this configuration is fetched + vcs { + root(DslContext.settingsRoot) + showDependenciesChanges = true + checkoutMode = CheckoutMode.ON_AGENT + } + + failureConditions { + errorMessage = true + nonZeroExitCode = true + executionTimeoutMin = 120 + } + + features { + feature { + id = "perfmon" + type = "perfmon" + } + } +} diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index fcd6ce13..36ded96f 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -1,7 +1,6 @@ import jetbrains.buildServer.configs.kotlin.v2018_2.* import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.* import jetbrains.buildServer.configs.kotlin.v2018_2.triggers.* -import java.lang.IllegalArgumentException /* The settings script is an entry point for defining a TeamCity @@ -66,13 +65,10 @@ project { } } - val benchmarks = listOf( - benchmark("js", "Linux"), - benchmark("jvm", "Linux"), - *platforms.map { benchmark("native", it) }.toTypedArray() - ) + buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray()) - buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray(), *benchmarks.toTypedArray()) + val benchmarksProject = benchmarksProject() + subProject(benchmarksProject) } fun Project.buildVersion() = BuildType { @@ -143,45 +139,6 @@ fun Project.build(platform: String, versionBuild: BuildType) = platform(platform artifactRules = "+:build/maven=>maven\n+:build/api=>api" } -fun Project.benchmark(target: String, platform: String) = platform(platform, "${target}Benchmark") { - steps { - gradle { - name = "Benchmark" - tasks = benchmarkTask(target, platform) - jdkHome = "%env.$jdk%" - param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") - buildFile = "" - gradleWrapperPath = "" - } - } - - artifactRules = "benchmarks/build/reports/**=> reports" - - requirements { - benchmarkAgentInstanceTypeRequirement(platform) - } - - failureConditions { - executionTimeoutMin = 1440 - } -} - -fun benchmarkTask(target: String, platform: String): String = when(target) { - "js", "jvm" -> "${target}Benchmark" - "native" -> when(platform) { - "Mac OS X" -> "macosX64Benchmark" - "Linux" -> "linuxX64Benchmark" - "Windows" -> "mingwX64Benchmark" - else -> throw IllegalArgumentException("Unknown platform: $platform") - } - else -> throw IllegalArgumentException("Unknown target: $target") -} - -fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: String) { - if (platform == "Linux") equals("system.ec2.instance-type", "m5d.xlarge") - else if (platform == "Windows") equals("system.ec2.instance-type", "m5.xlarge") -} - fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = apply { dependencies.dependency(build, configure) @@ -268,53 +225,3 @@ fun Project.deploy(platform: String, configureBuild: BuildType) = platform(platf } } }.dependsOnSnapshot(configureBuild) - -fun Project.platform(platform: String, name: String, configure: BuildType.() -> Unit) = BuildType { - // ID is prepended with Project ID, so don't repeat it here - // ID should conform to identifier rules, so just letters, numbers and underscore - id("${name}_${platform.substringBefore(" ")}") - // Display name of the build configuration - this.name = "$name ($platform)" - - requirements { - contains("teamcity.agent.jvm.os.name", platform) - } - - params { - // This parameter is needed for macOS agent to be compatible - if (platform.startsWith("Mac")) param("env.JDK_17", "") - } - - commonConfigure() - configure() -}.also { buildType(it) } - - -fun BuildType.commonConfigure() { - requirements { - noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") - } - - // Allow to fetch build status through API for badges - allowExternalStatus = true - - // Configure VCS, by default use the same and only VCS root from which this configuration is fetched - vcs { - root(DslContext.settingsRoot) - showDependenciesChanges = true - checkoutMode = CheckoutMode.ON_AGENT - } - - failureConditions { - errorMessage = true - nonZeroExitCode = true - executionTimeoutMin = 120 - } - - features { - feature { - id = "perfmon" - type = "perfmon" - } - } -} From 48917ca22eb667cf42a8b91b69aea3a1f3749967 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 01:49:31 +0300 Subject: [PATCH 022/162] Run `fastBenchmark` task to reduce benchmark run time in teamcity --- .teamcity/Benchmarks.kt | 8 ++++---- benchmarks/build.gradle.kts | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 5867b5db..616359d2 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -47,11 +47,11 @@ fun Project.benchmark(target: String, platform: String) = platform(platform, "${ } fun benchmarkTask(target: String, platform: String): String = when(target) { - "js", "jvm" -> "${target}Benchmark" + "js", "jvm" -> "${target}FastBenchmark" "native" -> when(platform) { - "Mac OS X" -> "macosX64Benchmark" - "Linux" -> "linuxX64Benchmark" - "Windows" -> "mingwX64Benchmark" + "Mac OS X" -> "macosX64FastBenchmark" + "Linux" -> "linuxX64FastBenchmark" + "Windows" -> "mingwX64FastBenchmark" else -> throw IllegalArgumentException("Unknown platform: $platform") } else -> throw IllegalArgumentException("Unknown target: $target") diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 890afaf7..eca23c27 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -67,6 +67,32 @@ benchmark { param("immutablePercentage", /*"95", "30", */"0") param("hashCodeType", "random", "collision") } + + register("fast") { + warmups = 7 + iterations = 7 + iterationTime = 500 + iterationTimeUnit = "ms" + param("size", "1000") + param("immutablePercentage", "0") + param("hashCodeType", "random") + + include("immutableList.Add.addLast$") + include("immutableList.Get.getByIndex$") + include("immutableList.Iterate.firstToLast$") + include("immutableList.Remove.removeLast$") + include("immutableList.Set.setByIndex$") + + include("immutableMap.Get.get$") + include("immutableMap.Iterate.iterateKeys$") + include("immutableMap.Put.put$") + include("immutableMap.Remove.remove$") + + include("immutableSet.Add.add$") + include("immutableSet.Contains.contains$") + include("immutableSet.Iterate.iterate$") + include("immutableSet.Remove.remove$") + } } targets { From 478be83550420010e8ed2719da801f104ef2f81e Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 01:56:01 +0300 Subject: [PATCH 023/162] Add `benchmarkAll` build configuration --- .teamcity/Benchmarks.kt | 35 ++++++++++++++++++++++++++++++++++- .teamcity/settings.kts | 15 --------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 616359d2..45077449 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -14,15 +14,33 @@ fun benchmarksProject() = Project { this.id("Benchmarks") this.name = "Benchmarks" + val benchmarkAll = benchmarkAll() val benchmarks = listOf( benchmark("js", "Linux"), benchmark("jvm", "Linux"), *platforms.map { benchmark("native", it) }.toTypedArray() ) - buildTypesOrder = benchmarks + benchmarks.forEach { benchmark -> + benchmarkAll.dependsOnSnapshot(benchmark, onFailure = FailureAction.ADD_PROBLEM) + benchmarkAll.dependsOn(benchmark) { + artifacts { + artifactRules = "+:reports=>reports" + } + } + } + + buildTypesOrder = listOf(benchmarkAll, *benchmarks.toTypedArray()) } +fun Project.benchmarkAll() = BuildType { + id("Benchmark_All") + this.name = "Benchmark (All)" + type = BuildTypeSettings.Type.COMPOSITE + + commonConfigure() +}.also { buildType(it) } + fun Project.benchmark(target: String, platform: String) = platform(platform, "${target}Benchmark") { steps { gradle { @@ -62,6 +80,21 @@ fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: String) { else if (platform == "Windows") equals("system.ec2.instance-type", "m5.xlarge") } +fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = + apply { + dependencies.dependency(build, configure) + } + +fun BuildType.dependsOnSnapshot(build: BuildType, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { + dependencies.dependency(build) { + snapshot { + configure() + onDependencyFailure = onFailure + onDependencyCancel = FailureAction.CANCEL + } + } +} + fun Project.platform(platform: String, name: String, configure: BuildType.() -> Unit) = BuildType { // ID is prepended with Project ID, so don't repeat it here // ID should conform to identifier rules, so just letters, numbers and underscore diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 36ded96f..deaa6297 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -139,21 +139,6 @@ fun Project.build(platform: String, versionBuild: BuildType) = platform(platform artifactRules = "+:build/maven=>maven\n+:build/api=>api" } -fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = - apply { - dependencies.dependency(build, configure) - } - -fun BuildType.dependsOnSnapshot(build: BuildType, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { - dependencies.dependency(build) { - snapshot { - configure() - onDependencyFailure = onFailure - onDependencyCancel = FailureAction.CANCEL - } - } -} - fun Project.deployConfigure() = BuildType { id("Deploy_Configure") this.name = "Deploy (Configure Version)" From e093db041cd9d6ec931df20cf481c1001b29a258 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 02:01:42 +0300 Subject: [PATCH 024/162] Add `additionalConfiguration` extension point for Project --- .teamcity/Benchmarks.kt | 4 ++++ .teamcity/settings.kts | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 45077449..e0e35ff3 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -10,6 +10,10 @@ import java.lang.IllegalArgumentException val platforms = listOf("Windows", "Linux", "Mac OS X") val jdk = "JDK_18_x64" +fun Project.additionalConfiguration() { + subProject(benchmarksProject()) +} + fun benchmarksProject() = Project { this.id("Benchmarks") this.name = "Benchmarks" diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index deaa6297..658f8afd 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -67,8 +67,7 @@ project { buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray()) - val benchmarksProject = benchmarksProject() - subProject(benchmarksProject) + additionalConfiguration() } fun Project.buildVersion() = BuildType { From 13d057cef3703529f1a6c98f3d40a32b8be12984 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 05:43:14 +0300 Subject: [PATCH 025/162] Extract utils to separate file --- .teamcity/Benchmarks.kt | 93 +++++--------------------------------- .teamcity/settings.kts | 17 ++----- .teamcity/utils.kt | 98 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 94 deletions(-) create mode 100644 .teamcity/utils.kt diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index e0e35ff3..be70b248 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -7,9 +7,6 @@ import jetbrains.buildServer.configs.kotlin.v2018_2.* import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.gradle import java.lang.IllegalArgumentException -val platforms = listOf("Windows", "Linux", "Mac OS X") -val jdk = "JDK_18_x64" - fun Project.additionalConfiguration() { subProject(benchmarksProject()) } @@ -18,10 +15,14 @@ fun benchmarksProject() = Project { this.id("Benchmarks") this.name = "Benchmarks" + params { + param("teamcity.ui.settings.readOnly", "true") + } + val benchmarkAll = benchmarkAll() val benchmarks = listOf( - benchmark("js", "Linux"), - benchmark("jvm", "Linux"), + benchmark("js", Platform.Linux), + benchmark("jvm", Platform.Linux), *platforms.map { benchmark("native", it) }.toTypedArray() ) @@ -45,7 +46,7 @@ fun Project.benchmarkAll() = BuildType { commonConfigure() }.also { buildType(it) } -fun Project.benchmark(target: String, platform: String) = platform(platform, "${target}Benchmark") { +fun Project.benchmark(target: String, platform: Platform) = buildType("${target}Benchmark", platform) { steps { gradle { name = "Benchmark" @@ -68,83 +69,13 @@ fun Project.benchmark(target: String, platform: String) = platform(platform, "${ } } -fun benchmarkTask(target: String, platform: String): String = when(target) { +fun benchmarkTask(target: String, platform: Platform): String = when(target) { "js", "jvm" -> "${target}FastBenchmark" - "native" -> when(platform) { - "Mac OS X" -> "macosX64FastBenchmark" - "Linux" -> "linuxX64FastBenchmark" - "Windows" -> "mingwX64FastBenchmark" - else -> throw IllegalArgumentException("Unknown platform: $platform") - } + "native" -> "${platform.nativeTaskPrefix()}FastBenchmark" else -> throw IllegalArgumentException("Unknown target: $target") } -fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: String) { - if (platform == "Linux") equals("system.ec2.instance-type", "m5d.xlarge") - else if (platform == "Windows") equals("system.ec2.instance-type", "m5.xlarge") -} - -fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = - apply { - dependencies.dependency(build, configure) - } - -fun BuildType.dependsOnSnapshot(build: BuildType, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { - dependencies.dependency(build) { - snapshot { - configure() - onDependencyFailure = onFailure - onDependencyCancel = FailureAction.CANCEL - } - } -} - -fun Project.platform(platform: String, name: String, configure: BuildType.() -> Unit) = BuildType { - // ID is prepended with Project ID, so don't repeat it here - // ID should conform to identifier rules, so just letters, numbers and underscore - id("${name}_${platform.substringBefore(" ")}") - // Display name of the build configuration - this.name = "$name ($platform)" - - requirements { - contains("teamcity.agent.jvm.os.name", platform) - } - - params { - // This parameter is needed for macOS agent to be compatible - if (platform.startsWith("Mac")) param("env.JDK_17", "") - } - - commonConfigure() - configure() -}.also { buildType(it) } - - -fun BuildType.commonConfigure() { - requirements { - noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") - } - - // Allow to fetch build status through API for badges - allowExternalStatus = true - - // Configure VCS, by default use the same and only VCS root from which this configuration is fetched - vcs { - root(DslContext.settingsRoot) - showDependenciesChanges = true - checkoutMode = CheckoutMode.ON_AGENT - } - - failureConditions { - errorMessage = true - nonZeroExitCode = true - executionTimeoutMin = 120 - } - - features { - feature { - id = "perfmon" - type = "perfmon" - } - } +fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: Platform) { + if (platform == Platform.Linux) equals("system.ec2.instance-type", "m5d.xlarge") + else if (platform == Platform.Windows) equals("system.ec2.instance-type", "m5.xlarge") } diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 658f8afd..50fe2df8 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -25,15 +25,6 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View */ version = "2018.2" -val versionSuffixParameter = "versionSuffix" -val teamcitySuffixParameter = "teamcitySuffix" -val releaseVersionParameter = "releaseVersion" - -val bintrayUserName = "%env.BINTRAY_USER%" -val bintrayToken = "%env.BINTRAY_API_KEY%" - -val platforms = listOf("Windows", "Linux", "Mac OS X") -val jdk = "JDK_18_x64" project { // Disable editing of project and build settings from the UI to avoid issues with TeamCity @@ -112,7 +103,7 @@ fun Project.buildAll(versionBuild: BuildType) = BuildType { commonConfigure() }.also { buildType(it) } -fun Project.build(platform: String, versionBuild: BuildType) = platform(platform, "Build") { +fun Project.build(platform: Platform, versionBuild: BuildType) = buildType("Build", platform) { dependsOnSnapshot(versionBuild) @@ -123,7 +114,7 @@ fun Project.build(platform: String, versionBuild: BuildType) = platform(platform steps { gradle { - name = "Build and Test $platform Binaries" + name = "Build and Test ${platform.buildTypeName()} Binaries" jdkHome = "%env.$jdk%" jvmArgs = "-Xmx1g" tasks = "clean publishToBuildLocal check" @@ -182,7 +173,7 @@ fun Project.deployPublish(configureBuild: BuildType) = BuildType { }.also { buildType(it) } -fun Project.deploy(platform: String, configureBuild: BuildType) = platform(platform, "Deploy") { +fun Project.deploy(platform: Platform, configureBuild: BuildType) = buildType("Deploy", platform) { type = BuildTypeSettings.Type.DEPLOYMENT enablePersonalBuilds = false maxRunningBuilds = 1 @@ -199,7 +190,7 @@ fun Project.deploy(platform: String, configureBuild: BuildType) = platform(platf steps { gradle { - name = "Deploy $platform Binaries" + name = "Deploy ${platform.buildTypeName()} Binaries" jdkHome = "%env.$jdk%" jvmArgs = "-Xmx1g" gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter% -PbintrayApiKey=%bintray-key% -PbintrayUser=%bintray-user%" diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt new file mode 100644 index 00000000..4483bfde --- /dev/null +++ b/.teamcity/utils.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +import jetbrains.buildServer.configs.kotlin.v2018_2.* + +const val versionSuffixParameter = "versionSuffix" +const val teamcitySuffixParameter = "teamcitySuffix" +const val releaseVersionParameter = "releaseVersion" + +const val bintrayUserName = "%env.BINTRAY_USER%" +const val bintrayToken = "%env.BINTRAY_API_KEY%" + +val platforms = Platform.values() +const val jdk = "JDK_18_x64" + +enum class Platform { + Windows, Linux, MacOS; +} + +fun Platform.nativeTaskPrefix(): String = when(this) { + Platform.Windows -> "mingwX64" + Platform.Linux -> "linuxX64" + Platform.MacOS -> "macosX64" +} +fun Platform.buildTypeName(): String = when (this) { + Platform.Windows, Platform.Linux -> name + Platform.MacOS -> "Mac OS X" +} +fun Platform.buildTypeId(): String = buildTypeName().substringBefore(" ") +fun Platform.teamcityAgentName(): String = buildTypeName() + + +fun Project.buildType(name: String, platform: Platform, configure: BuildType.() -> Unit) = BuildType { + // ID is prepended with Project ID, so don't repeat it here + // ID should conform to identifier rules, so just letters, numbers and underscore + id("${name}_${platform.buildTypeId()}") + // Display name of the build configuration + this.name = "$name (${platform.buildTypeName()})" + + requirements { + contains("teamcity.agent.jvm.os.name", platform.teamcityAgentName()) + } + + params { + // This parameter is needed for macOS agent to be compatible + if (platform == Platform.MacOS) param("env.JDK_17", "") + } + + commonConfigure() + configure() +}.also { buildType(it) } + + +fun BuildType.commonConfigure() { + requirements { + noLessThan("teamcity.agent.hardware.memorySizeMb", "6144") + } + + // Allow to fetch build status through API for badges + allowExternalStatus = true + + // Configure VCS, by default use the same and only VCS root from which this configuration is fetched + vcs { + root(DslContext.settingsRoot) + showDependenciesChanges = true + checkoutMode = CheckoutMode.ON_AGENT + } + + failureConditions { + errorMessage = true + nonZeroExitCode = true + executionTimeoutMin = 120 + } + + features { + feature { + id = "perfmon" + type = "perfmon" + } + } +} + +fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = + apply { + dependencies.dependency(build, configure) + } + +fun BuildType.dependsOnSnapshot(build: BuildType, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { + dependencies.dependency(build) { + snapshot { + configure() + onDependencyFailure = onFailure + onDependencyCancel = FailureAction.CANCEL + } + } +} \ No newline at end of file From 75f78f8c3c981ca16077a9263bd65511b852e250 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 06:47:39 +0300 Subject: [PATCH 026/162] Extract `additionalConfiguration` to separate file --- .teamcity/Benchmarks.kt | 4 ---- .teamcity/additionalConfiguration.kt | 10 ++++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .teamcity/additionalConfiguration.kt diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index be70b248..9d72719f 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -7,10 +7,6 @@ import jetbrains.buildServer.configs.kotlin.v2018_2.* import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.gradle import java.lang.IllegalArgumentException -fun Project.additionalConfiguration() { - subProject(benchmarksProject()) -} - fun benchmarksProject() = Project { this.id("Benchmarks") this.name = "Benchmarks" diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt new file mode 100644 index 00000000..fb759852 --- /dev/null +++ b/.teamcity/additionalConfiguration.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +import jetbrains.buildServer.configs.kotlin.v2018_2.Project + +fun Project.additionalConfiguration() { + subProject(benchmarksProject()) +} \ No newline at end of file From d19f73f43a0ed9980d65374e325ed2cf318e6a78 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 07:52:15 +0300 Subject: [PATCH 027/162] Update Kotlin DSL version to v2019_2 --- .teamcity/Benchmarks.kt | 4 ++-- .teamcity/additionalConfiguration.kt | 2 +- .teamcity/settings.kts | 6 +++--- .teamcity/utils.kt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 9d72719f..50abc892 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -3,8 +3,8 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2018_2.* -import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.gradle +import jetbrains.buildServer.configs.kotlin.v2019_2.* +import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle import java.lang.IllegalArgumentException fun benchmarksProject() = Project { diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index fb759852..6a2046a2 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2018_2.Project +import jetbrains.buildServer.configs.kotlin.v2019_2.Project fun Project.additionalConfiguration() { subProject(benchmarksProject()) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 50fe2df8..75eb3f4b 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -1,6 +1,6 @@ -import jetbrains.buildServer.configs.kotlin.v2018_2.* -import jetbrains.buildServer.configs.kotlin.v2018_2.buildSteps.* -import jetbrains.buildServer.configs.kotlin.v2018_2.triggers.* +import jetbrains.buildServer.configs.kotlin.v2019_2.* +import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.* +import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.* /* The settings script is an entry point for defining a TeamCity diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index 4483bfde..12c98ce0 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2018_2.* +import jetbrains.buildServer.configs.kotlin.v2019_2.* const val versionSuffixParameter = "versionSuffix" const val teamcitySuffixParameter = "teamcitySuffix" From 6fdb2dd53e6925a3fe42e336242d9c0d6b276055 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 08:02:02 +0300 Subject: [PATCH 028/162] Update Teamcity config version to 2020.1 --- .teamcity/settings.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 75eb3f4b..9c180f8e 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -24,7 +24,7 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View 'Debug' option is available in the context menu for the task. */ -version = "2018.2" +version = "2020.1" project { // Disable editing of project and build settings from the UI to avoid issues with TeamCity From 6c4cf0aa89e85f40ba0deed8353f610a60a47bbe Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 7 Jul 2020 08:46:23 +0300 Subject: [PATCH 029/162] Run linux and windows benchmarks in both m5d.xlarge and m5.xlarge instances --- .teamcity/Benchmarks.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 50abc892..47bf3d21 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -72,6 +72,7 @@ fun benchmarkTask(target: String, platform: Platform): String = when(target) { } fun Requirements.benchmarkAgentInstanceTypeRequirement(platform: Platform) { - if (platform == Platform.Linux) equals("system.ec2.instance-type", "m5d.xlarge") - else if (platform == Platform.Windows) equals("system.ec2.instance-type", "m5.xlarge") + if (platform == Platform.Linux || platform == Platform.Windows) { + matches("system.ec2.instance-type", "m5d?.xlarge") + } } From 2c831ff3ec31a500017988331be8ed20c9d1ac29 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 17 Jul 2020 05:44:03 +0300 Subject: [PATCH 030/162] Forward build number from Build_Version to Benchmark --- .teamcity/Benchmarks.kt | 26 +++++++++++++++++++------- .teamcity/additionalConfiguration.kt | 5 +++-- .teamcity/settings.kts | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 47bf3d21..2819a929 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -7,7 +7,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.* import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle import java.lang.IllegalArgumentException -fun benchmarksProject() = Project { +fun benchmarksProject(buildVersion: BuildType) = Project { this.id("Benchmarks") this.name = "Benchmarks" @@ -15,11 +15,11 @@ fun benchmarksProject() = Project { param("teamcity.ui.settings.readOnly", "true") } - val benchmarkAll = benchmarkAll() + val benchmarkAll = benchmarkAll(buildVersion) val benchmarks = listOf( - benchmark("js", Platform.Linux), - benchmark("jvm", Platform.Linux), - *platforms.map { benchmark("native", it) }.toTypedArray() + benchmark("js", Platform.Linux, buildVersion), + benchmark("jvm", Platform.Linux, buildVersion), + *platforms.map { benchmark("native", it, buildVersion) }.toTypedArray() ) benchmarks.forEach { benchmark -> @@ -34,21 +34,33 @@ fun benchmarksProject() = Project { buildTypesOrder = listOf(benchmarkAll, *benchmarks.toTypedArray()) } -fun Project.benchmarkAll() = BuildType { +fun Project.benchmarkAll(buildVersion: BuildType) = BuildType { id("Benchmark_All") this.name = "Benchmark (All)" type = BuildTypeSettings.Type.COMPOSITE + dependsOnSnapshot(buildVersion) + buildNumberPattern = buildVersion.depParamRefs.buildNumber.ref + commonConfigure() }.also { buildType(it) } -fun Project.benchmark(target: String, platform: Platform) = buildType("${target}Benchmark", platform) { +fun Project.benchmark(target: String, platform: Platform, buildVersion: BuildType) = buildType("${target}Benchmark", platform) { + + dependsOnSnapshot(buildVersion) + + params { + param(versionSuffixParameter, buildVersion.depParamRefs[versionSuffixParameter].ref) + param(teamcitySuffixParameter, buildVersion.depParamRefs[teamcitySuffixParameter].ref) + } + steps { gradle { name = "Benchmark" tasks = benchmarkTask(target, platform) jdkHome = "%env.$jdk%" param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") + gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$teamcitySuffixParameter=%$teamcitySuffixParameter%" buildFile = "" gradleWrapperPath = "" } diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index 6a2046a2..70827b9f 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -3,8 +3,9 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ +import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType import jetbrains.buildServer.configs.kotlin.v2019_2.Project -fun Project.additionalConfiguration() { - subProject(benchmarksProject()) +fun Project.additionalConfiguration(buildVersion: BuildType) { + subProject(benchmarksProject(buildVersion)) } \ No newline at end of file diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 9c180f8e..f644c815 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -58,7 +58,7 @@ project { buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray()) - additionalConfiguration() + additionalConfiguration(buildVersion) } fun Project.buildVersion() = BuildType { From f0514a97665259caff7277893fc12fb60cb05606 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 27 Jul 2020 04:03:27 +0300 Subject: [PATCH 031/162] Introduce extension functions that return existing project configurations --- .teamcity/additionalConfiguration.kt | 5 ++--- .teamcity/settings.kts | 20 ++++++++++---------- .teamcity/utils.kt | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index 70827b9f..ed990323 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -3,9 +3,8 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType import jetbrains.buildServer.configs.kotlin.v2019_2.Project -fun Project.additionalConfiguration(buildVersion: BuildType) { - subProject(benchmarksProject(buildVersion)) +fun Project.additionalConfiguration() { + subProject(benchmarksProject(existingBuildVersion())) } \ No newline at end of file diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index f644c815..320cc9b2 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -45,24 +45,24 @@ project { } } - val deployConfigure = deployConfigure().apply { + val deployVersion = deployVersion().apply { dependsOnSnapshot(buildAll, onFailure = FailureAction.IGNORE) } - val deploys = platforms.map { deploy(it, deployConfigure) } - val deployPublish = deployPublish(deployConfigure).apply { + val deploys = platforms.map { deploy(it, deployVersion) } + val deployPublish = deployPublish(deployVersion).apply { dependsOnSnapshot(buildAll, onFailure = FailureAction.IGNORE) deploys.forEach { dependsOnSnapshot(it) } } - buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployConfigure, *deploys.toTypedArray()) + buildTypesOrder = listOf(buildAll, buildVersion, *builds.toTypedArray(), deployPublish, deployVersion, *deploys.toTypedArray()) - additionalConfiguration(buildVersion) + additionalConfiguration() } fun Project.buildVersion() = BuildType { - id("Build_Version") + id(BUILD_CONFIGURE_VERSION_ID) this.name = "Build (Configure Version)" commonConfigure() @@ -84,7 +84,7 @@ fun Project.buildVersion() = BuildType { }.also { buildType(it) } fun Project.buildAll(versionBuild: BuildType) = BuildType { - id("Build_All") + id(BUILD_ALL_ID) this.name = "Build (All)" type = BuildTypeSettings.Type.COMPOSITE @@ -129,8 +129,8 @@ fun Project.build(platform: Platform, versionBuild: BuildType) = buildType("Buil artifactRules = "+:build/maven=>maven\n+:build/api=>api" } -fun Project.deployConfigure() = BuildType { - id("Deploy_Configure") +fun Project.deployVersion() = BuildType { + id(DEPLOY_CONFIGURE_VERSION_ID) this.name = "Deploy (Configure Version)" commonConfigure() @@ -159,7 +159,7 @@ fun Project.deployConfigure() = BuildType { }.also { buildType(it) } fun Project.deployPublish(configureBuild: BuildType) = BuildType { - id("Deploy_Publish") + id(DEPLOY_PUBLISH_ID) this.name = "Deploy (Publish)" type = BuildTypeSettings.Type.COMPOSITE dependsOnSnapshot(configureBuild) diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index 12c98ce0..7b087dde 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -32,6 +32,22 @@ fun Platform.buildTypeId(): String = buildTypeName().substringBefore(" ") fun Platform.teamcityAgentName(): String = buildTypeName() +const val BUILD_CONFIGURE_VERSION_ID = "Build_Version" +const val BUILD_ALL_ID = "Build_All" +const val DEPLOY_CONFIGURE_VERSION_ID = "Deploy_Configure" +const val DEPLOY_PUBLISH_ID = "Deploy_Publish" + +private fun existingBuildId(suffix: String): String = "RootProjectId_$suffix" +private fun Project.existingBuildWithId(id: String): BuildType = buildTypes.single { it.id.toString() == existingBuildId(id) } + +fun Project.existingBuildVersion(): BuildType = existingBuildWithId(BUILD_CONFIGURE_VERSION_ID) +fun Project.existingBuildAll(): BuildType = existingBuildWithId(BUILD_ALL_ID) +fun Project.existingBuild(platform: Platform): BuildType = existingBuildWithId("Build_${platform.buildTypeId()}") +fun Project.existingDeployVersion(): BuildType = existingBuildWithId(DEPLOY_CONFIGURE_VERSION_ID) +fun Project.existingDeployPublish(): BuildType = existingBuildWithId(DEPLOY_PUBLISH_ID) +fun Project.existingDeploy(platform: Platform): BuildType = existingBuildWithId("Deploy_${platform.buildTypeId()}") + + fun Project.buildType(name: String, platform: Platform, configure: BuildType.() -> Unit) = BuildType { // ID is prepended with Project ID, so don't repeat it here // ID should conform to identifier rules, so just letters, numbers and underscore From 068b69d31b4e66955c7f9557cb6709f32b1dda4a Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 28 Jul 2020 05:11:43 +0300 Subject: [PATCH 032/162] Use KnownBuilds class to group access to created project configurations --- .teamcity/additionalConfiguration.kt | 2 +- .teamcity/utils.kt | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index ed990323..f041037e 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -6,5 +6,5 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.Project fun Project.additionalConfiguration() { - subProject(benchmarksProject(existingBuildVersion())) + subProject(benchmarksProject(knownBuilds.buildVersion)) } \ No newline at end of file diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index 7b087dde..61e2b838 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -37,15 +37,21 @@ const val BUILD_ALL_ID = "Build_All" const val DEPLOY_CONFIGURE_VERSION_ID = "Deploy_Configure" const val DEPLOY_PUBLISH_ID = "Deploy_Publish" -private fun existingBuildId(suffix: String): String = "RootProjectId_$suffix" -private fun Project.existingBuildWithId(id: String): BuildType = buildTypes.single { it.id.toString() == existingBuildId(id) } - -fun Project.existingBuildVersion(): BuildType = existingBuildWithId(BUILD_CONFIGURE_VERSION_ID) -fun Project.existingBuildAll(): BuildType = existingBuildWithId(BUILD_ALL_ID) -fun Project.existingBuild(platform: Platform): BuildType = existingBuildWithId("Build_${platform.buildTypeId()}") -fun Project.existingDeployVersion(): BuildType = existingBuildWithId(DEPLOY_CONFIGURE_VERSION_ID) -fun Project.existingDeployPublish(): BuildType = existingBuildWithId(DEPLOY_PUBLISH_ID) -fun Project.existingDeploy(platform: Platform): BuildType = existingBuildWithId("Deploy_${platform.buildTypeId()}") +class KnownBuilds(private val project: Project) { + private fun buildWithId(id: String): BuildType { + val fullId = "RootProjectId_$id" + return project.buildTypes.single { it.id.toString() == fullId } + } + + val buildVersion: BuildType get() = buildWithId(BUILD_CONFIGURE_VERSION_ID) + val buildAll: BuildType get() = buildWithId(BUILD_ALL_ID) + fun buildOn(platform: Platform): BuildType = buildWithId("Build_${platform.buildTypeId()}") + val deployVersion: BuildType get() = buildWithId(DEPLOY_CONFIGURE_VERSION_ID) + val deployPublish: BuildType get() = buildWithId(DEPLOY_PUBLISH_ID) + fun deployOn(platform: Platform): BuildType = buildWithId("Deploy_${platform.buildTypeId()}") +} + +val Project.knownBuilds: KnownBuilds get() = KnownBuilds(this) fun Project.buildType(name: String, platform: Platform, configure: BuildType.() -> Unit) = BuildType { From 8e409b557b391f743b2339429d1b8a5ac0dcac6c Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 28 Jul 2020 06:03:49 +0300 Subject: [PATCH 033/162] Remove "org.jfrog.artifactory..." gradle param from benchmark build step --- .teamcity/Benchmarks.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 2819a929..9cadefb8 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -59,7 +59,6 @@ fun Project.benchmark(target: String, platform: Platform, buildVersion: BuildTyp name = "Benchmark" tasks = benchmarkTask(target, platform) jdkHome = "%env.$jdk%" - param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL") gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$teamcitySuffixParameter=%$teamcitySuffixParameter%" buildFile = "" gradleWrapperPath = "" From f5aa4d27c1de37a5030c5da4c64698f8eb897979 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 3 Aug 2020 19:01:40 +0300 Subject: [PATCH 034/162] Run main benchmarks --- .teamcity/Benchmarks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 9cadefb8..245d1031 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -77,8 +77,8 @@ fun Project.benchmark(target: String, platform: Platform, buildVersion: BuildTyp } fun benchmarkTask(target: String, platform: Platform): String = when(target) { - "js", "jvm" -> "${target}FastBenchmark" - "native" -> "${platform.nativeTaskPrefix()}FastBenchmark" + "js", "jvm" -> "${target}Benchmark" + "native" -> "${platform.nativeTaskPrefix()}Benchmark" else -> throw IllegalArgumentException("Unknown target: $target") } From 519d8846e8e2225436c06facc079aa88f39f292b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 3 Aug 2020 19:08:50 +0300 Subject: [PATCH 035/162] Upgrade infra to 0.1.0-dev-53 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e08b0c4e..7fddddc3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ buildscript { } plugins { - id("kotlinx.team.infra") version "0.1.0-dev-52" + id("kotlinx.team.infra") version "0.1.0-dev-53" } infra { From 0880e94fe0e230e38728abdae378adf93b481fbc Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 10 Aug 2020 02:29:24 +0300 Subject: [PATCH 036/162] Check if build id ends with given id instead of equality check --- .teamcity/utils.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index 61e2b838..ea580099 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -39,8 +39,7 @@ const val DEPLOY_PUBLISH_ID = "Deploy_Publish" class KnownBuilds(private val project: Project) { private fun buildWithId(id: String): BuildType { - val fullId = "RootProjectId_$id" - return project.buildTypes.single { it.id.toString() == fullId } + return project.buildTypes.single { it.id.toString().endsWith(id) } } val buildVersion: BuildType get() = buildWithId(BUILD_CONFIGURE_VERSION_ID) From 7d0a6d509c08b355daee66a30f1602a9fe7cb0ea Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 17 Aug 2020 16:21:20 +0300 Subject: [PATCH 037/162] PersistentOrderedSetIterator breaks if hash of an element changes #76 --- ...sistentOrderedMapBuilderContentIterators.kt | 5 ++++- .../PersistentOrderedMapContentIterators.kt | 5 ++++- .../PersistentOrderedSetIterator.kt | 4 +++- .../src/contract/map/ImmutableMapTest.kt | 18 ++++++++++++++++++ .../src/contract/set/ImmutableSetTest.kt | 18 ++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentIterators.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentIterators.kt index 3c1f771d..78c359ef 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentIterators.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentIterators.kt @@ -28,7 +28,10 @@ internal open class PersistentOrderedMapBuilderLinksIterator( lastIteratedKey = nextKey nextWasInvoked = true index++ - val result = builder.hashMapBuilder[nextKey]!! + @Suppress("UNCHECKED_CAST") + val result = builder.hashMapBuilder.getOrElse(nextKey as K) { + throw ConcurrentModificationException("Hash code of a key ($nextKey) has changed after it was added to the persistent map.") + } nextKey = result.next return result } diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentIterators.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentIterators.kt index ef54d02b..e09aef40 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentIterators.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentIterators.kt @@ -21,7 +21,10 @@ internal open class PersistentOrderedMapLinksIterator( if (!hasNext()) { throw NoSuchElementException() } - val result = hashMap[nextKey]!! + @Suppress("UNCHECKED_CAST") + val result = hashMap.getOrElse(nextKey as K) { + throw ConcurrentModificationException("Hash code of a key ($nextKey) has changed after it was added to the persistent map.") + } index++ nextKey = result.next return result diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetIterator.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetIterator.kt index a354a9e1..093349ce 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetIterator.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetIterator.kt @@ -19,7 +19,9 @@ internal open class PersistentOrderedSetIterator(private var nextElement: Any @Suppress("UNCHECKED_CAST") val result = nextElement as E index++ - nextElement = map[result]!!.next + nextElement = map.getOrElse(result) { + throw ConcurrentModificationException("Hash code of an element ($result) has changed after it was added to the persistent set.") + }.next return result } diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index e5d174d3..b9594e52 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -38,6 +38,24 @@ class ImmutableOrderedMapTest : ImmutableMapTest() { compare(listOf(1, 2), map.values) { collectionBehavior(ordered = true) } compare(mapOf("y" to 1, "x" to 2).entries, map.entries) { setBehavior(ordered = true) } } + + @Test fun keyHashCodeChanged() { + val changing = mutableSetOf("ok") + val persistent: PersistentMap = immutableMapOf("constant" to "fixed", changing to "modified") + assertEquals(1, persistent.keys.filter { it === changing }.size) + changing.add("break iteration") + assertFailsWith { persistent.keys.filter { it === changing } } + } + + @Test fun builderKeyHashCodeChanged() { + val changing = mutableSetOf("ok") + val builder: PersistentMap.Builder = immutableMapOf().builder().apply { + putAll(listOf("constant" to "fixed", changing to "modified")) + } + assertEquals(1, builder.filter { it.key === changing }.size) + changing.add("break iteration") + assertFailsWith { builder.filter { it.key === changing } } + } } abstract class ImmutableMapTest { diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index d7f704f5..bbadb6ab 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -24,6 +24,24 @@ class ImmutableOrderedSetTest : ImmutableSetTestBase() { override fun testBuilderToPersistentSet(builder: PersistentSet.Builder) { assertSame(builder.build(), builder.toPersistentSet(), "toPersistent should call build()") } + + @Test fun elementHashCodeChanged() { + val changing = mutableSetOf("ok") + val persistent: PersistentSet = immutableSetOf("constant", changing, "fix") + assertEquals(1, persistent.filter { it === changing }.size) + changing.add("break iteration") + assertFailsWith { persistent.filter { it === changing } } + } + + @Test fun builderElementHashCodeChanged() { + val changing = mutableSetOf("ok") + val builder: PersistentSet.Builder = immutableSetOf().builder().apply { + addAll(listOf("constant", changing, "fix")) + } + assertEquals(1, builder.filter { it === changing }.size) + changing.add("break iteration") + assertFailsWith { builder.filter { it === changing } } + } } abstract class ImmutableSetTestBase { From b4de092e2e73fc05f33d2a1c6dcac2fe66247b4d Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Sun, 9 Aug 2020 20:19:36 +0300 Subject: [PATCH 038/162] Weaken receiver type of `toPersistentHashSet` to Iterable #77 --- core/commonMain/src/extensions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index 2322f495..30784fba 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -623,7 +623,7 @@ fun Iterable.toPersistentSet(): PersistentSet = * * Order of the elements in the returned set is unspecified. */ -fun Set.toPersistentHashSet(): PersistentSet +fun Iterable.toPersistentHashSet(): PersistentSet = this as? PersistentHashSet ?: (this as? PersistentHashSetBuilder)?.build() ?: PersistentHashSet.emptyOf() + this From 6a4b9116b083c31fb4185eb301717ad24c1456cd Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 17 Aug 2020 17:33:02 +0300 Subject: [PATCH 039/162] Add CharSequence.toPersistentHashSet as an alternative to CharSequence.toPersistentSet --- core/commonMain/src/extensions.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index 30784fba..b5c940e8 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -589,6 +589,14 @@ fun CharSequence.toPersistentList(): PersistentList = fun CharSequence.toPersistentSet(): PersistentSet = persistentSetOf().mutate { this.toCollection(it) } +/** + * Returns a persistent set of all characters. + * + * Order of the elements in the returned set is unspecified. + */ +fun CharSequence.toPersistentHashSet(): PersistentSet = + persistentHashSetOf().mutate { this.toCollection(it) } + /** * Returns an immutable set of all elements of this collection. From 47bdf03cd9fe583597816537cd762530902175f7 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 17 Apr 2020 11:07:06 +0300 Subject: [PATCH 040/162] Upgrade kotlin to 1.4.0 --- benchmarks/build.gradle.kts | 1 + build.gradle.kts | 4 +++- core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index eca23c27..f823e32f 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -12,6 +12,7 @@ evaluationDependsOn(":kotlinx-collections-immutable") val JDK_6: String by project repositories { + maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") maven(url = "https://dl.bintray.com/kotlin/kotlinx") } diff --git a/build.gradle.kts b/build.gradle.kts index 7fddddc3..3cd4af2c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,11 @@ buildscript { repositories { mavenCentral() + maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.71") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0") } } @@ -43,6 +44,7 @@ val JDK_6 by ext(System.getenv("JDK_6") ?: findProperty("JDK_6") as String? ?: e allprojects { repositories { mavenCentral() + maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } } \ No newline at end of file diff --git a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt index b72eaa77..e2e10cd2 100644 --- a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt +++ b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt @@ -11,10 +11,10 @@ import kotlin.time.* @UseExperimental(ExperimentalTime::class) abstract class ExecutionTimeMeasuringTest { - private var clockMark: ClockMark? = null + private var clockMark: TimeMark? = null private fun markExecutionStart() { - clockMark = MonoClock.markNow() + clockMark = TimeSource.Monotonic.markNow() } private fun printExecutionTime() { From 76f03b6628481bdb57e2c972bb6255cda81d8187 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 17 Apr 2020 11:07:24 +0300 Subject: [PATCH 041/162] Enable HMPP --- gradle.properties | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c7828ca9..798abae8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,7 @@ group=org.jetbrains.kotlinx version=0.4 versionSuffix=SNAPSHOT -org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 \ No newline at end of file +org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +kotlin.mpp.enableGranularSourceSetsMetadata=true +kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file From 61d1980b9358d4f2a0a0f0b431e7599da2e52276 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 17 Apr 2020 11:08:01 +0300 Subject: [PATCH 042/162] Use prebuilt K/N to avoid error at platform libs generation --- gradle.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 798abae8..637247ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,6 @@ versionSuffix=SNAPSHOT org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.mpp.enableCompatibilityMetadataVariant=true \ No newline at end of file +kotlin.mpp.enableCompatibilityMetadataVariant=true + +kotlin.native.distribution.type=prebuilt \ No newline at end of file From 2a1bf189f8baf2141d120310721f99d7b228b599 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 17 Apr 2020 11:07:33 +0300 Subject: [PATCH 043/162] Upgrade gradle to 6.3 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b0acbdcd..6623300b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 6542999fc6fa8dc68bfcb35319e6541abbbb1431 Mon Sep 17 00:00:00 2001 From: Sergey Igushkin Date: Fri, 17 Apr 2020 13:22:35 +0300 Subject: [PATCH 044/162] Upgrade kotlinx-benchmark to 0.2.0-dev-20 --- benchmarks/build.gradle.kts | 4 ++-- settings.gradle | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index f823e32f..10339925 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("kotlinx.benchmark") version "0.2.0-dev-8" + id("kotlinx.benchmark") version "0.2.0-dev-20" } @@ -48,7 +48,7 @@ kotlin { commonMain { dependencies { api("org.jetbrains.kotlin:kotlin-stdlib-common") - api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-8") + api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") api(project(":kotlinx-collections-immutable")) } } diff --git a/settings.gradle b/settings.gradle index 8f34a2e7..bc2384de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,6 +2,7 @@ pluginManagement { repositories { maven { url 'https://dl.bintray.com/kotlin/kotlinx' } maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } + maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } gradlePluginPortal() } } From f8dfecd0961199e3c8d45286c986c508daa75349 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 Aug 2020 08:07:06 +0300 Subject: [PATCH 045/162] Upgrade infra to 0.2.0-dev-55 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3cd4af2c..cceb81a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { } plugins { - id("kotlinx.team.infra") version "0.1.0-dev-53" + id("kotlinx.team.infra") version "0.2.0-dev-55" } infra { From ab7e5c42a6453601dcbb30b58fc68bc13f50d74b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 Aug 2020 08:57:09 +0300 Subject: [PATCH 046/162] Switch to both mode when building with Kotlin/JS --- core/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index fcba2b31..655f25d3 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -28,7 +28,7 @@ kotlin { } } - js { + js(BOTH) { nodejs { testTask { useMocha { From 3e9c57c06da6ac1e67c0fbbec085d70b2d8c1575 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 21 Aug 2020 18:49:12 +0300 Subject: [PATCH 047/162] Workaround for Bintray treating .sha512 files as artifacts --- gradle.properties | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 637247ea..733103e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,8 @@ org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.enableCompatibilityMetadataVariant=true -kotlin.native.distribution.type=prebuilt \ No newline at end of file +kotlin.native.distribution.type=prebuilt + +# Workaround for Bintray treating .sha512 files as artifacts +# https://github.com/gradle/gradle/issues/11412 +systemProp.org.gradle.internal.publish.checksums.insecure=true \ No newline at end of file From 9e9673cd152d85902d53b8be6432c098f99b4bff Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 24 Aug 2020 04:52:49 +0300 Subject: [PATCH 048/162] Remove now redundant @UseExperimental(ExperimentalStdlibApi::class) from bit operations usages --- core/commonMain/src/implementations/immutableMap/TrieNode.kt | 4 ---- core/commonMain/src/implementations/immutableSet/TrieNode.kt | 1 - 2 files changed, 5 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 2cff74c3..89f23990 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -87,7 +87,6 @@ internal class TrieNode( private set /** Returns number of entries stored in this trie node (not counting subnodes) */ - @UseExperimental(ExperimentalStdlibApi::class) internal fun entryCount(): Int = dataMap.countOneBits() // here and later: @@ -105,13 +104,11 @@ internal class TrieNode( } /** Gets the index in buffer of the data entry key corresponding to the position specified by [positionMask]. */ - @UseExperimental(ExperimentalStdlibApi::class) internal fun entryKeyIndex(positionMask: Int): Int { return ENTRY_SIZE * (dataMap and (positionMask - 1)).countOneBits() } /** Gets the index in buffer of the subtrie node entry corresponding to the position specified by [positionMask]. */ - @UseExperimental(ExperimentalStdlibApi::class) internal fun nodeIndex(positionMask: Int): Int { return buffer.size - 1 - (nodeMap and (positionMask - 1)).countOneBits() } @@ -677,7 +674,6 @@ internal class TrieNode( accept(visitor, 0, 0) } - @UseExperimental(ExperimentalStdlibApi::class) private fun accept( visitor: (node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int) -> Unit, hash: Int, diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 8cc97736..564441eb 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -53,7 +53,6 @@ internal class TrieNode( return bitmap and positionMask == 0 } - @UseExperimental(ExperimentalStdlibApi::class) internal fun indexOfCellAt(positionMask: Int): Int { return (bitmap and (positionMask - 1)).countOneBits() } From df9fdb6d9863a213e08243d80d12b2b94592be27 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 24 Aug 2020 04:57:13 +0300 Subject: [PATCH 049/162] Update README.md with version 0.3.3 usages --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f54823e2..bcce05ff 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ collection.mutate { some_actions_on(it) } The library is published to [kotlinx](https://bintray.com/kotlin/kotlinx/kotlinx.collections.immutable) bintray repository and available in jcenter too. -The library depends on the Kotlin Standard Library of the version at least `1.3.70`. +The library depends on the Kotlin Standard Library of the version at least `1.4.0`. ### Gradle @@ -132,7 +132,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.2") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.3") } } } @@ -142,7 +142,7 @@ kotlin { To use the library in a JVM-only project add the platform to the artifact name, e.g.: ```groovy -implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.2") +implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.3") ``` ### Maven @@ -166,7 +166,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.2 + 0.3.3 ``` From 128114a9d8385bf492c2bee1fa3da37e7185fb10 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 24 Aug 2020 05:03:14 +0300 Subject: [PATCH 050/162] Add CHANGELOG.md for new version 0.3.3 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7311a4..c867cb0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## 0.3.3 + +- Upgrade Kotlin version up to 1.4.0 +- Weaken receiver type of toPersistentHashSet to Iterable [#77](https://github.com/Kotlin/kotlinx.collections.immutable/issues/77). +- Throw ConcurrentModificationException if hashCode of ordered set element changes [#76](https://github.com/Kotlin/kotlinx.collections.immutable/issues/76). +- Fix transition from PersistentVector to SmallPersistentVector [#75](https://github.com/Kotlin/kotlinx.collections.immutable/issues/75) +- Add CharSequence.toPersistentHashSet() as an alternative to CharSequence.toPersistentSet() + ## 0.3.2 - Introduce `persistentHashSetOf`, `persistentMapOf` and `persistentHashMapOf` methods that take no arguments and create an empty instance [#67](https://github.com/Kotlin/kotlinx.collections.immutable/issues/67). From 0d99bf5db6178f658b35c9db1efe4c70e837d60e Mon Sep 17 00:00:00 2001 From: anatawa12 Date: Wed, 19 Aug 2020 11:31:18 +0900 Subject: [PATCH 051/162] Add extension functions to convert Sequence to persistent collections --- core/commonMain/src/extensions.kt | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index b5c940e8..ba8df34c 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -575,6 +575,16 @@ fun Iterable.toPersistentList(): PersistentList = */ fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() +/** + * Returns an immutable list containing all elements of this sequence. + */ +fun Sequence.toImmutableList(): ImmutableList = toPersistentList() + +/** + * Returns a persistent list containing all elements of this sequence. + */ +fun Sequence.toPersistentList(): PersistentList = persistentListOf() + this + /** * Returns a persistent list containing all characters. */ @@ -623,6 +633,21 @@ fun Iterable.toPersistentSet(): PersistentSet = ?: (this as? PersistentOrderedSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf() + this +/** + * Returns an immutable set of all elements of this sequence. + */ +fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() + +/** + * Returns a persistent set of all elements of this sequence. + */ +fun Sequence.toPersistentSet(): PersistentSet = persistentSetOf() + this + +/** + * Returns a persistent set of all elements of this sequence. + */ +fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this + /** * Returns a persistent set containing all elements from this set. * @@ -673,4 +698,4 @@ fun Map.toPersistentMap(): PersistentMap fun Map.toPersistentHashMap(): PersistentMap = this as? PersistentHashMap ?: (this as? PersistentHashMapBuilder)?.build() - ?: PersistentHashMap.emptyOf().putAll(this) \ No newline at end of file + ?: PersistentHashMap.emptyOf().putAll(this) From bdde02ae6ab3f9f163c7fe3d5752f3e156b9e8c6 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 25 Aug 2020 01:26:13 +0300 Subject: [PATCH 052/162] Document iteration order of the returned persistent sets --- core/commonMain/src/extensions.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index ba8df34c..f368eb79 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -613,7 +613,7 @@ fun CharSequence.toPersistentHashSet(): PersistentSet = * * If the receiver is already an immutable set, returns it as is. * - * Elements of the returned set are iterated in the same order as in this collection + * Elements of the returned set are iterated in the same order as in this collection. */ fun Iterable.toImmutableSet(): ImmutableSet = this as? ImmutableSet @@ -626,7 +626,7 @@ fun Iterable.toImmutableSet(): ImmutableSet = * If the receiver is already a persistent set, returns it as is. * If the receiver is a persistent set builder, calls `build` on it and returns the result. * - * Elements of the returned set are iterated in the same order as in this collection + * Elements of the returned set are iterated in the same order as in this collection. */ fun Iterable.toPersistentSet(): PersistentSet = this as? PersistentOrderedSet @@ -635,16 +635,22 @@ fun Iterable.toPersistentSet(): PersistentSet = /** * Returns an immutable set of all elements of this sequence. + * + * Elements of the returned set are iterated in the same order as in this sequence. */ fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() /** * Returns a persistent set of all elements of this sequence. + * + * Elements of the returned set are iterated in the same order as in this sequence. */ fun Sequence.toPersistentSet(): PersistentSet = persistentSetOf() + this /** * Returns a persistent set of all elements of this sequence. + * + * Order of the elements in the returned set is unspecified. */ fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this From da99446c1edef1786c9765c152e969041569ca38 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 25 Aug 2020 01:53:09 +0300 Subject: [PATCH 053/162] Reorder toPersistentX extension function to improve readability --- core/commonMain/src/extensions.kt | 82 ++++++++++++++++--------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index f368eb79..6506d811 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -556,29 +556,29 @@ fun Iterable.toImmutableList(): ImmutableList = ?: this.toPersistentList() /** - * Returns a persistent list containing all elements of this collection. - * - * If the receiver is already a persistent list, returns it as is. - * If the receiver is a persistent list builder, calls `build` on it and returns the result. + * Returns an immutable list containing all elements of this sequence. */ -fun Iterable.toPersistentList(): PersistentList = - this as? PersistentList - ?: (this as? PersistentList.Builder)?.build() - ?: persistentListOf() + this - - -// fun Array.toImmutableList(): ImmutableList = immutableListOf() + this.asList() - +fun Sequence.toImmutableList(): ImmutableList = toPersistentList() /** * Returns an immutable list containing all characters. */ fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() + +// fun Array.toImmutableList(): ImmutableList = immutableListOf() + this.asList() + + /** - * Returns an immutable list containing all elements of this sequence. + * Returns a persistent list containing all elements of this collection. + * + * If the receiver is already a persistent list, returns it as is. + * If the receiver is a persistent list builder, calls `build` on it and returns the result. */ -fun Sequence.toImmutableList(): ImmutableList = toPersistentList() +fun Iterable.toPersistentList(): PersistentList = + this as? PersistentList + ?: (this as? PersistentList.Builder)?.build() + ?: persistentListOf() + this /** * Returns a persistent list containing all elements of this sequence. @@ -591,22 +591,6 @@ fun Sequence.toPersistentList(): PersistentList = persistentListOf( fun CharSequence.toPersistentList(): PersistentList = persistentListOf().mutate { this.toCollection(it) } -/** - * Returns a persistent set of all characters. - * - * Elements of the returned set are iterated in the same order as in this char sequence. - */ -fun CharSequence.toPersistentSet(): PersistentSet = - persistentSetOf().mutate { this.toCollection(it) } - -/** - * Returns a persistent set of all characters. - * - * Order of the elements in the returned set is unspecified. - */ -fun CharSequence.toPersistentHashSet(): PersistentSet = - persistentHashSetOf().mutate { this.toCollection(it) } - /** * Returns an immutable set of all elements of this collection. @@ -620,6 +604,14 @@ fun Iterable.toImmutableSet(): ImmutableSet = ?: (this as? PersistentSet.Builder)?.build() ?: persistentSetOf() + this +/** + * Returns an immutable set of all elements of this sequence. + * + * Elements of the returned set are iterated in the same order as in this sequence. + */ +fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() + + /** * Returns a persistent set of all elements of this collection. * @@ -633,13 +625,6 @@ fun Iterable.toPersistentSet(): PersistentSet = ?: (this as? PersistentOrderedSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf() + this -/** - * Returns an immutable set of all elements of this sequence. - * - * Elements of the returned set are iterated in the same order as in this sequence. - */ -fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() - /** * Returns a persistent set of all elements of this sequence. * @@ -648,11 +633,13 @@ fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() fun Sequence.toPersistentSet(): PersistentSet = persistentSetOf() + this /** - * Returns a persistent set of all elements of this sequence. + * Returns a persistent set of all characters. * - * Order of the elements in the returned set is unspecified. + * Elements of the returned set are iterated in the same order as in this char sequence. */ -fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this +fun CharSequence.toPersistentSet(): PersistentSet = + persistentSetOf().mutate { this.toCollection(it) } + /** * Returns a persistent set containing all elements from this set. @@ -667,6 +654,21 @@ fun Iterable.toPersistentHashSet(): PersistentSet ?: (this as? PersistentHashSetBuilder)?.build() ?: PersistentHashSet.emptyOf() + this +/** + * Returns a persistent set of all elements of this sequence. + * + * Order of the elements in the returned set is unspecified. + */ +fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this + +/** + * Returns a persistent set of all characters. + * + * Order of the elements in the returned set is unspecified. + */ +fun CharSequence.toPersistentHashSet(): PersistentSet = + persistentHashSetOf().mutate { this.toCollection(it) } + /** * Returns an immutable map containing all entries from this map. From 0501885f669370fd27299e4db7c351ddd121cdc7 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 25 Aug 2020 02:05:44 +0300 Subject: [PATCH 054/162] Add missing CharSequence.toImmutableSet() method --- core/commonMain/src/extensions.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index 6506d811..1da99627 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -611,6 +611,13 @@ fun Iterable.toImmutableSet(): ImmutableSet = */ fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() +/** + * Returns an immutable set of all characters. + * + * Elements of the returned set are iterated in the same order as in this char sequence. + */ +fun CharSequence.toImmutableSet(): PersistentSet = toPersistentSet() + /** * Returns a persistent set of all elements of this collection. From 690343ace1aa4a47463f2ce42ae319876014ee47 Mon Sep 17 00:00:00 2001 From: Mikhail Belyaev Date: Fri, 18 Sep 2020 00:04:55 +0300 Subject: [PATCH 055/162] Faster addAll/putAll implementation (#86) --- .../immutableMap/PersistentHashMap.kt | 5 +- .../immutableMap/PersistentHashMapBuilder.kt | 13 ++ .../implementations/immutableMap/TrieNode.kt | 181 ++++++++++++++++++ .../immutableSet/PersistentHashSet.kt | 1 + .../immutableSet/PersistentHashSetBuilder.kt | 16 ++ .../implementations/immutableSet/TrieNode.kt | 170 ++++++++++++++++ core/commonMain/src/internal/ForEachOneBit.kt | 18 ++ .../commonMain/src/internal/MutableCounter.kt | 10 + .../src/contract/map/ImmutableMapTest.kt | 35 ++++ .../src/contract/set/ImmutableSetTest.kt | 37 ++++ 10 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 core/commonMain/src/internal/ForEachOneBit.kt create mode 100644 core/commonMain/src/internal/MutableCounter.kt diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt index 6df0dc8e..39514f0a 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt @@ -5,7 +5,10 @@ package kotlinx.collections.immutable.implementations.immutableMap -import kotlinx.collections.immutable.* +import kotlinx.collections.immutable.ImmutableCollection +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.mutate internal class PersistentHashMap(internal val node: TrieNode, override val size: Int): AbstractMap(), PersistentMap { diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index 2f1383e2..8536e79a 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -6,6 +6,7 @@ package kotlinx.collections.immutable.implementations.immutableMap import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MutabilityOwnership internal class PersistentHashMapBuilder(private var map: PersistentHashMap) : PersistentMap.Builder, AbstractMutableMap() { @@ -61,6 +62,18 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap return operationResult } + override fun putAll(from: Map) { + val map = from as? PersistentHashMap ?: (from as? PersistentHashMapBuilder)?.map + if (map != null) @Suppress("UNCHECKED_CAST") { + val intersectionCounter = DeltaCounter() + val oldSize = this.size + node = node.mutablePutAll(map.node as TrieNode, 0, intersectionCounter, this) + val newSize = oldSize + map.size - intersectionCounter.count + if(oldSize != newSize) this.size = newSize + } + else super.putAll(from) + } + override fun remove(key: K): V? { operationResult = null @Suppress("UNCHECKED_CAST") diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 89f23990..8f5cbcf9 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -5,7 +5,10 @@ package kotlinx.collections.immutable.implementations.immutableMap +import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MutabilityOwnership +import kotlinx.collections.immutable.internal.assert +import kotlinx.collections.immutable.internal.forEachOneBit internal const val MAX_BRANCHING_FACTOR = 32 @@ -436,6 +439,87 @@ internal class TrieNode( return this } + private fun mutableCollisionPutAll(otherNode: TrieNode, + intersectionCounter: DeltaCounter, + owner: MutabilityOwnership): TrieNode { + assert(nodeMap == 0) + assert(dataMap == 0) + assert(otherNode.nodeMap == 0) + assert(otherNode.dataMap == 0) + val tempBuffer = this.buffer.copyOf(newSize = this.buffer.size + otherNode.buffer.size) + var i = this.buffer.size + for (j in 0 until otherNode.buffer.size step ENTRY_SIZE) { + @Suppress("UNCHECKED_CAST") + if (!this.collisionContainsKey(otherNode.buffer[j] as K)) { + tempBuffer[i] = otherNode.buffer[j] + tempBuffer[i + 1] = otherNode.buffer[j + 1] + i += ENTRY_SIZE + } else intersectionCounter.count++ + } + + return when (val newSize = i) { + this.buffer.size -> this + otherNode.buffer.size -> otherNode + tempBuffer.size -> TrieNode(0, 0, tempBuffer, owner) + else -> TrieNode(0, 0, tempBuffer.copyOf(newSize), owner) + } + } + + private fun mutablePutAllFromOtherNodeCell(other: TrieNode, + positionMask: Int, + shift: Int, + intersectionCounter: DeltaCounter, + mutator: PersistentHashMapBuilder): TrieNode { + return when { + other.hasNodeAt(positionMask) -> { + mutablePutAll( + other.nodeAtIndex(other.nodeIndex(positionMask)), + shift + LOG_MAX_BRANCHING_FACTOR, + intersectionCounter, + mutator + ) + } + other.hasEntryAt(positionMask) -> { + val keyIndex = other.entryKeyIndex(positionMask) + val key = other.keyAtIndex(keyIndex) + val value = other.valueAtKeyIndex(keyIndex) + val oldSize = mutator.size + val newNode = mutablePut( + key.hashCode(), + key, + value, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator + ) + if (mutator.size == oldSize) { + intersectionCounter.count++ + } + newNode + } + else -> this + } + } + + private fun calculateSize(): Int { + if (nodeMap == 0) return buffer.size / ENTRY_SIZE + val numValues = dataMap.countOneBits() + var result = numValues + for(i in (numValues * ENTRY_SIZE) until buffer.size) { + result += nodeAtIndex(i).calculateSize() + } + return result + } + + private fun elementsIdentityEquals(otherNode: TrieNode): Boolean { + if (this === otherNode) return true + if (nodeMap != otherNode.nodeMap) return false + if (dataMap != otherNode.dataMap) return false + for (i in 0 until buffer.size) { + if(buffer[i] !== otherNode.buffer[i]) return false + } + return true + } + fun containsKey(keyHash: Int, key: K, shift: Int): Boolean { val keyPositionMask = 1 shl indexSegment(keyHash, shift) @@ -477,6 +561,103 @@ internal class TrieNode( return null } + fun mutablePutAll(otherNode: TrieNode, + shift: Int, + intersectionCounter: DeltaCounter, + mutator: PersistentHashMapBuilder): TrieNode { + if (this === otherNode) { + intersectionCounter += calculateSize() + return this + } + // the collision case + if (shift > MAX_SHIFT) { + return mutableCollisionPutAll(otherNode, intersectionCounter, mutator.ownership) + } + + // new nodes are where either of the old ones were + var newNodeMap = nodeMap or otherNode.nodeMap + // entries stay being entries only if one bits were in exactly one of input nodes + // but not in the new data nodes + var newDataMap = dataMap xor otherNode.dataMap and newNodeMap.inv() + // (**) now, this is tricky: we have a number of entry-entry pairs and we don't know yet whether + // they result in an entry (if they are equal) or a new node (if they are not) + // but we want to keep it to single allocation, so we check and mark equal ones here + (dataMap and otherNode.dataMap).forEachOneBit { positionMask, _ -> + val leftKey = this.keyAtIndex(this.entryKeyIndex(positionMask)) + val rightKey = otherNode.keyAtIndex(otherNode.entryKeyIndex(positionMask)) + // if they are equal, put them in the data map + if (leftKey == rightKey) newDataMap = newDataMap or positionMask + // if they are not, put them in the node map + else newNodeMap = newNodeMap or positionMask + // we can use this later to skip calling equals() again + } + assert(newNodeMap and newDataMap == 0) + val mutableNode = when { + this.ownedBy == mutator.ownership && this.dataMap == newDataMap && this.nodeMap == newNodeMap -> this + else -> { + val newBuffer = arrayOfNulls(newDataMap.countOneBits() * ENTRY_SIZE + newNodeMap.countOneBits()) + TrieNode(newDataMap, newNodeMap, newBuffer) + } + } + newNodeMap.forEachOneBit { positionMask, index -> + val newNodeIndex = mutableNode.buffer.size - 1 - index + mutableNode.buffer[newNodeIndex] = when { + hasNodeAt(positionMask) -> { + val before = nodeAtIndex(nodeIndex(positionMask)) + before.mutablePutAllFromOtherNodeCell(otherNode, positionMask, shift, intersectionCounter, mutator) + } + + otherNode.hasNodeAt(positionMask) -> { + val before = otherNode.nodeAtIndex(otherNode.nodeIndex(positionMask)) + before.mutablePutAllFromOtherNodeCell(this, positionMask, shift, intersectionCounter, mutator) + } + + else -> { // two entries, and they are not equal by key (see ** above) + val thisKeyIndex = this.entryKeyIndex(positionMask) + val thisKey = this.keyAtIndex(thisKeyIndex) + val thisValue = this.valueAtKeyIndex(thisKeyIndex) + val otherKeyIndex = otherNode.entryKeyIndex(positionMask) + val otherKey = otherNode.keyAtIndex(otherKeyIndex) + val otherValue = otherNode.valueAtKeyIndex(otherKeyIndex) + makeNode( + thisKey.hashCode(), + thisKey, + thisValue, + otherKey.hashCode(), + otherKey, + otherValue, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator.ownership + ) + } + } + } + newDataMap.forEachOneBit { positionMask, index -> + val newKeyIndex = index * ENTRY_SIZE + when { + !otherNode.hasEntryAt(positionMask) -> { + val oldKeyIndex = this.entryKeyIndex(positionMask) + mutableNode.buffer[newKeyIndex] = this.keyAtIndex(oldKeyIndex) + mutableNode.buffer[newKeyIndex + 1] = this.valueAtKeyIndex(oldKeyIndex) + } + // there is either only one entry in otherNode, or + // both entries are here => they are equal, see ** above + // so just overwrite that + else -> { + val oldKeyIndex = otherNode.entryKeyIndex(positionMask) + mutableNode.buffer[newKeyIndex] = otherNode.keyAtIndex(oldKeyIndex) + mutableNode.buffer[newKeyIndex + 1] = otherNode.valueAtKeyIndex(oldKeyIndex) + if (this.hasEntryAt(positionMask)) intersectionCounter.count++ + } + } + } + return when { + this.elementsIdentityEquals(mutableNode) -> this + otherNode.elementsIdentityEquals(mutableNode) -> otherNode + else -> mutableNode + } + } + fun put(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): ModificationResult? { val keyPositionMask = 1 shl indexSegment(keyHash, shift) diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt index 972a4f43..2b04cfe2 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt @@ -6,6 +6,7 @@ package kotlinx.collections.immutable.implementations.immutableSet import kotlinx.collections.immutable.PersistentSet +import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.mutate internal class PersistentHashSet(internal val node: TrieNode, diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt index 37ebad0a..ce688de6 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt @@ -6,6 +6,7 @@ package kotlinx.collections.immutable.implementations.immutableSet import kotlinx.collections.immutable.PersistentSet +import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MutabilityOwnership internal class PersistentHashSetBuilder(private var set: PersistentHashSet) : AbstractMutableSet(), PersistentSet.Builder { @@ -43,6 +44,21 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet return size != this.size } + override fun addAll(elements: Collection): Boolean { + val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.set + if (set !== null) { + val deltaCounter = DeltaCounter() + val oldSize = this.size + node = node.mutableAddAll(set.node, 0, deltaCounter, this) + val newSize = oldSize + elements.size - deltaCounter.count + if (oldSize != newSize) { + this.size = newSize + } + return oldSize != this.size + } + return super.addAll(elements) + } + override fun remove(element: E): Boolean { val size = this.size @Suppress("UNCHECKED_CAST") diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 564441eb..da08a78a 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -5,7 +5,10 @@ package kotlinx.collections.immutable.implementations.immutableSet +import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MutabilityOwnership +import kotlinx.collections.immutable.internal.assert +import kotlinx.collections.immutable.internal.forEachOneBit internal const val MAX_BRANCHING_FACTOR = 32 @@ -38,6 +41,24 @@ private fun Array.removeCellAtIndex(cellIndex: Int): Array { return newBuffer } +private inline fun Array.filterTo( + newArray: Array, + newArrayOffset: Int = 0, + predicate: (Any?) -> Boolean): Int { + var i = 0 + var j = 0 + while (i < size) { + val e = this[i] + if (predicate(e)) { + newArray[newArrayOffset + j] = this[i] + ++j + assert(newArrayOffset + j <= newArray.size) + } + ++i + } + return j +} + internal class TrieNode( var bitmap: Int, var buffer: Array, @@ -258,6 +279,53 @@ internal class TrieNode( return this } + private fun mutableCollisionAddAll(otherNode: TrieNode, + intersectionSizeRef: DeltaCounter, + owner: MutabilityOwnership): TrieNode { + if (this === otherNode) { + intersectionSizeRef += buffer.size + return this + } + val tempBuffer = this.buffer.copyOf(newSize = this.buffer.size + otherNode.buffer.size) + val totalWritten = otherNode.buffer.filterTo(tempBuffer, newArrayOffset = this.buffer.size) { + @Suppress("UNCHECKED_CAST") + !this.collisionContainsElement(it as E) + } + val totalSize = totalWritten + this.buffer.size + intersectionSizeRef += (tempBuffer.size - totalSize) + if(totalSize == this.buffer.size) return this + if(totalSize == otherNode.buffer.size) return otherNode + + val newBuffer = if(totalSize == tempBuffer.size) tempBuffer else tempBuffer.copyOf(newSize = totalSize) + return if(ownedBy == owner) { + this.buffer = newBuffer + this + } else { + TrieNode(0, newBuffer, owner) + } + } + + private fun calculateSize(): Int { + if (bitmap == 0) return buffer.size + var result = 0 + for (e in buffer) { + result += when(e) { + is TrieNode<*> -> e.calculateSize() + else -> 1 + } + } + return result + } + + private fun elementsIdentityEquals(otherNode: TrieNode): Boolean { + if (this === otherNode) return true + if (bitmap != otherNode.bitmap) return false + for (i in 0 until buffer.size) { + if (buffer[i] !== otherNode.buffer[i]) return false + } + return true + } + fun contains(elementHash: Int, element: E, shift: Int): Boolean { val cellPositionMask = 1 shl indexSegment(elementHash, shift) @@ -277,6 +345,108 @@ internal class TrieNode( return element == buffer[cellIndex] } + fun mutableAddAll(otherNode: TrieNode, + shift: Int, + intersectionSizeRef: DeltaCounter, + mutator: PersistentHashSetBuilder<*>): TrieNode { + if (this === otherNode) { + intersectionSizeRef.count += this.calculateSize() + return this + } + if (shift > MAX_SHIFT) { + return mutableCollisionAddAll(otherNode, intersectionSizeRef, mutator.ownership) + } + // union mask contains all the bits from input masks + val newBitMap = bitmap or otherNode.bitmap + // first allocate the node and then fill it in + // we are doing a union, so all the array elements are guaranteed to exist + val mutableNode = when { + newBitMap == bitmap && ownedBy == mutator.ownership -> this + else -> TrieNode(newBitMap, arrayOfNulls(newBitMap.countOneBits()), mutator.ownership) + } + // for each bit set in the resulting mask, + // either left, right or both masks contain the same bit + // Note: we shouldn't overrun MAX_SHIFT because both sides are correct TrieNodes, right? + newBitMap.forEachOneBit { positionMask, newNodeIndex -> + val thisIndex = indexOfCellAt(positionMask) + val otherNodeIndex = otherNode.indexOfCellAt(positionMask) + mutableNode.buffer[newNodeIndex] = when { + // no element on left -> pick right + hasNoCellAt(positionMask) -> otherNode.buffer[otherNodeIndex] + // no element on right -> pick left + otherNode.hasNoCellAt(positionMask) -> buffer[thisIndex] + // both nodes contain something at the masked bit + else -> { + val thisCell = buffer[thisIndex] + val otherNodeCell = otherNode.buffer[otherNodeIndex] + val thisIsNode = thisCell is TrieNode<*> + val otherIsNode = otherNodeCell is TrieNode<*> + when { + // both are nodes -> merge them recursively + thisIsNode && otherIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as TrieNode + thisCell.mutableAddAll( + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + intersectionSizeRef, + mutator + ) + } + // one of them is a node -> add the other one to it + thisIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as E + val oldSize = mutator.size + thisCell.mutableAdd( + otherNodeCell.hashCode(), + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator + ).also { + if (mutator.size == oldSize) intersectionSizeRef.count++ + } + } + // same as last case, but reversed + otherIsNode -> @Suppress("UNCHECKED_CAST") { + otherNodeCell as TrieNode + thisCell as E + val oldSize = mutator.size + otherNodeCell.mutableAdd( + thisCell.hashCode(), + thisCell, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator + ).also { + if (mutator.size == oldSize) intersectionSizeRef.count++ + } + } + // both are just E => compare them + thisCell == otherNodeCell -> thisCell.also { intersectionSizeRef.count++ } + // both are just E, but different => make a collision-ish node + else -> @Suppress("UNCHECKED_CAST") { + thisCell as E + otherNodeCell as E + makeNode( + thisCell.hashCode(), + thisCell, + otherNodeCell.hashCode(), + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator.ownership + ) + } + } + } + } + } + return when { + this.elementsIdentityEquals(mutableNode) -> this + otherNode.elementsIdentityEquals(mutableNode) -> otherNode + else -> mutableNode + } + } + fun add(elementHash: Int, element: E, shift: Int): TrieNode { val cellPositionMask = 1 shl indexSegment(elementHash, shift) diff --git a/core/commonMain/src/internal/ForEachOneBit.kt b/core/commonMain/src/internal/ForEachOneBit.kt new file mode 100644 index 00000000..017fe997 --- /dev/null +++ b/core/commonMain/src/internal/ForEachOneBit.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.collections.immutable.internal + +// 'iterate' all the bits set to one in a given integer, in the form of one-bit masks +internal inline fun Int.forEachOneBit(body: (mask: Int, index: Int) -> Unit) { + var mask = this + var index = 0 + while (mask != 0) { + val bit = mask.takeLowestOneBit() + body(bit, index) + index++ + mask = mask xor bit + } +} diff --git a/core/commonMain/src/internal/MutableCounter.kt b/core/commonMain/src/internal/MutableCounter.kt new file mode 100644 index 00000000..ae79bee7 --- /dev/null +++ b/core/commonMain/src/internal/MutableCounter.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2016-2020 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.collections.immutable.internal + +internal data class DeltaCounter(var count: Int = 0) { + operator fun plusAssign(that: Int) { count += that } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index b9594e52..ee0d7885 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -10,6 +10,7 @@ import tests.contract.collectionBehavior import tests.contract.compare import tests.contract.mapBehavior import tests.contract.setBehavior +import tests.stress.IntWrapper import kotlin.test.* class ImmutableHashMapTest : ImmutableMapTest() { @@ -17,6 +18,40 @@ class ImmutableHashMapTest : ImmutableMapTest() { override fun testBuilderToPersistentMap(builder: PersistentMap.Builder) { assertNotSame(builder.build(), builder.toPersistentMap(), "toPersistent shouldn't call build()") } + + @Test fun putAllElements() { + run { + val x = immutableMapOf() + (11..200).map { "$it" to it } + compareMaps(x.toMap(), x.putAll(immutableMapOf())) + compareMaps(x.toMap(), immutableMapOf().putAll(x)) + } + + run { + val x = immutableMapOf() + (11..200).map { it to it } + val y = immutableMapOf() + (120..400).map { it to it } + compareMaps(x.toMap() + y.toMap(), x.putAll(y)) + } + + run { + val x = immutableMapOf() + (11..200).map { "$it" to it } + val y = immutableMapOf() + (120..400).map { "$it" to it } + compareMaps(x.toMap() + y.toMap(), x.putAll(y)) + } + + run { + val x = immutableMapOf() + (11..200).map { IntWrapper(it, it % 30) to it } + val y = immutableMapOf() + (120..400).map { IntWrapper(it, it % 30) to it } + compareMaps(x.toMap() + y.toMap(), x.putAll(y)) + compareMaps(y.toMap() + x.toMap(), y.putAll(x)) + } + + run { + val bcase = (1..2000).toList() // to preserve reference equality + val left = immutableMapOf() + bcase.map { "$it" to it } + assertSame(left, left + left) + assertSame(left, left + immutableMapOf()) + } + } } class ImmutableOrderedMapTest : ImmutableMapTest() { override fun immutableMapOf(vararg pairs: Pair): PersistentMap = persistentMapOf(*pairs) diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index bbadb6ab..6d04a39d 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -10,6 +10,7 @@ import tests.contract.compare import tests.contract.setBehavior import tests.isDigit import tests.isUpperCase +import tests.stress.IntWrapper import kotlin.test.* class ImmutableHashSetTest : ImmutableSetTestBase() { @@ -17,6 +18,42 @@ class ImmutableHashSetTest : ImmutableSetTestBase() { override fun testBuilderToPersistentSet(builder: PersistentSet.Builder) { assertNotSame(builder.build(), builder.toPersistentSet(), "toPersistent shouldn't call build()") } + + @Test fun addAllElements() { + run { + val left = immutableSetOf() + (1..2000) + assertSame(left, left.addAll(immutableSetOf())) + compareSets(left, immutableSetOf().addAll(left)) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (200..3000) + compareSets(left.toSet() + right.toSet(), left.addAll(right)) + } + + run { + val left = immutableSetOf() + (1..2000).map { IntWrapper(it, it % 200) } + val right = immutableSetOf() + (200..3000).map { IntWrapper(it, it % 200) } + compareSets(left.toSet() + right.toSet(), left.addAll(right)) + } + + run { + val left = immutableSetOf() + (1..2000).map { "$it" } + val right = immutableSetOf() + (200..3000).map { "$it" } + compareSets(left.toSet() + right.toSet(), left.addAll(right)) + } + + run { + val left = immutableSetOf() + (1..2000) + assertSame(left, left + left) + assertSame(left, left + immutableSetOf()) + val right = immutableSetOf() + (1..2000) + assertSame(right, right + left) + assertSame(left, left + right) + } + } + } class ImmutableOrderedSetTest : ImmutableSetTestBase() { override fun immutableSetOf(vararg elements: T) = persistentSetOf(*elements) From 8dded6bcef71b08b818f07f5bf6f1dc6ab2d3349 Mon Sep 17 00:00:00 2001 From: Mikhail Belyaev Date: Fri, 2 Oct 2020 11:42:44 +0300 Subject: [PATCH 056/162] Use the root node of the passed builder --- .../immutableMap/PersistentHashMapBuilder.kt | 2 +- .../immutableSet/PersistentHashSetBuilder.kt | 4 ++-- .../src/contract/map/ImmutableMapTest.kt | 23 +++++++++++++++++++ .../src/contract/set/ImmutableSetTest.kt | 23 +++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index 8536e79a..0ea24c1e 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -63,7 +63,7 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap } override fun putAll(from: Map) { - val map = from as? PersistentHashMap ?: (from as? PersistentHashMapBuilder)?.map + val map = from as? PersistentHashMap ?: (from as? PersistentHashMapBuilder)?.build() if (map != null) @Suppress("UNCHECKED_CAST") { val intersectionCounter = DeltaCounter() val oldSize = this.size diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt index ce688de6..9109f371 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt @@ -24,7 +24,7 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet modCount++ } - override fun build(): PersistentSet { + override fun build(): PersistentHashSet { set = if (node === set.node) { set } else { @@ -45,7 +45,7 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet } override fun addAll(elements: Collection): Boolean { - val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.set + val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() if (set !== null) { val deltaCounter = DeltaCounter() val oldSize = this.size diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index ee0d7885..42783ce2 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -52,6 +52,29 @@ class ImmutableHashMapTest : ImmutableMapTest() { assertSame(left, left + immutableMapOf()) } } + + @Test fun putAllElementsFromBuilder() { + val builder1 = immutableMapOf().builder() + val builder2 = immutableMapOf().builder() + val expected = mutableMapOf() + for(i in 300..400) { + builder1.put("$i", i) + expected.put("$i", i) + } + for(i in 0..200) { + builder2.put("$i", i) + expected.put("$i", i) + } + builder1.putAll(builder2) + + // make sure we work with current state of builder2, not previous + compareMaps(expected, builder1) + builder2.put("200", 1000) + // make sure we can't modify builder1 through builder2 + compareMaps(expected, builder1) + // make sure builder builds correct map + compareMaps(expected, builder1.build()) + } } class ImmutableOrderedMapTest : ImmutableMapTest() { override fun immutableMapOf(vararg pairs: Pair): PersistentMap = persistentMapOf(*pairs) diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index 6d04a39d..4bbfcabd 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -54,6 +54,29 @@ class ImmutableHashSetTest : ImmutableSetTestBase() { } } + @Test fun addAllElementsFromBuilder() { + val builder1 = immutableSetOf().builder() + val builder2 = immutableSetOf().builder() + val expected = mutableSetOf() + for(i in 300..400) { + builder1.add("$i") + expected.add("$i") + } + for(i in 0..200) { + builder2.add("$i") + expected.add("$i") + } + builder1.addAll(builder2) + + // make sure we work with current state of builder2, not previous + compareSets(expected, builder1) + builder2.add("200") + // make sure we can't modify builder1 through builder2 + compareSets(expected, builder1) + // make sure builder builds correct map + compareSets(expected, builder1.build()) + } + } class ImmutableOrderedSetTest : ImmutableSetTestBase() { override fun immutableSetOf(vararg elements: T) = persistentSetOf(*elements) From b6885b332979f581bd91987fad880aa5f3c29b5d Mon Sep 17 00:00:00 2001 From: Mikhail Belyaev Date: Fri, 18 Sep 2020 14:39:40 +0300 Subject: [PATCH 057/162] Faster bulk operations (removeAll, retainAll, containsAll) (#90) --- core/commonMain/src/ImmutableCollection.kt | 10 + core/commonMain/src/ImmutableList.kt | 10 + core/commonMain/src/ImmutableSet.kt | 10 + core/commonMain/src/extensions.kt | 20 ++ .../immutableList/AbstractPersistentList.kt | 4 + .../immutableSet/PersistentHashSet.kt | 15 +- .../immutableSet/PersistentHashSetBuilder.kt | 62 +++- .../implementations/immutableSet/TrieNode.kt | 309 +++++++++++++++++- .../PersistentOrderedSet.kt | 4 + .../src/contract/set/ImmutableSetTest.kt | 123 ++++++- 10 files changed, 553 insertions(+), 14 deletions(-) diff --git a/core/commonMain/src/ImmutableCollection.kt b/core/commonMain/src/ImmutableCollection.kt index d05979fc..dbd38b55 100644 --- a/core/commonMain/src/ImmutableCollection.kt +++ b/core/commonMain/src/ImmutableCollection.kt @@ -67,6 +67,16 @@ public interface PersistentCollection : ImmutableCollection { */ fun removeAll(predicate: (E) -> Boolean): PersistentCollection + /** + * Returns all elements in this collection that are also + * contained in the specified [elements] collection. + * + * @return a new persistent set with elements in this set that are also + * contained in the specified [elements] collection; + * or this instance if no modifications were made in the result of this operation. + */ + fun retainAll(elements: Collection<@UnsafeVariance E>): PersistentCollection + /** * Returns an empty persistent collection. */ diff --git a/core/commonMain/src/ImmutableList.kt b/core/commonMain/src/ImmutableList.kt index 4f54afee..ed126d4b 100644 --- a/core/commonMain/src/ImmutableList.kt +++ b/core/commonMain/src/ImmutableList.kt @@ -101,6 +101,16 @@ public interface PersistentList : ImmutableList, PersistentCollection< */ override fun removeAll(predicate: (E) -> Boolean): PersistentList + /** + * Returns all elements in this list that are also + * contained in the specified [elements] collection. + * + * @return a new persistent list with elements in this list that are also + * contained in the specified [elements] collection; + * or this instance if no modifications were made in the result of this operation. + */ + override fun retainAll(elements: Collection<@UnsafeVariance E>): PersistentList + /** * Returns an empty persistent list. */ diff --git a/core/commonMain/src/ImmutableSet.kt b/core/commonMain/src/ImmutableSet.kt index db050787..7a66609c 100644 --- a/core/commonMain/src/ImmutableSet.kt +++ b/core/commonMain/src/ImmutableSet.kt @@ -69,6 +69,16 @@ public interface PersistentSet : ImmutableSet, PersistentCollection */ override fun removeAll(predicate: (E) -> Boolean): PersistentSet + /** + * Returns all elements in this set that are also + * contained in the specified [elements] collection. + * + * @return a new persistent set with elements in this set that are also + * contained in the specified [elements] collection; + * or this instance if no modifications were made in the result of this operation. + */ + override fun retainAll(elements: Collection<@UnsafeVariance E>): PersistentSet + /** * Returns an empty persistent set. */ diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index 1da99627..f6d75641 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -291,6 +291,26 @@ operator fun PersistentSet.minus(elements: Array): PersistentSet PersistentSet.minus(elements: Sequence): PersistentSet = mutate { it.removeAll(elements) } +/** + * Returns all elements in this set that are also + * contained in the specified [elements] collection. + * + * @return a new persistent set with elements in this set that are also + * contained in the specified [elements] collection; + * or this instance if no modifications were made in the result of this operation. + */ +infix fun PersistentSet.intersect(elements: Iterable): PersistentSet + = if (elements is Collection) retainAll(elements) else mutate { it.retainAll(elements) } + +/** + * Returns all elements in this collection that are also + * contained in the specified [elements] collection. + * + * @return a new persistent set with elements in this collection that are also + * contained in the specified [elements] collection + */ +infix fun PersistentCollection.intersect(elements: Iterable): PersistentSet + = this.toPersistentSet().intersect(elements) /** * Returns the result of adding an entry to this map from the specified key-value [pair]. diff --git a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt index c8a37a73..37af4bce 100644 --- a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt +++ b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt @@ -34,6 +34,10 @@ abstract class AbstractPersistentList : PersistentList, AbstractList() return removeAll { elements.contains(it) } } + override fun retainAll(elements: Collection): PersistentList { + return removeAll { !elements.contains(it) } + } + override fun clear(): PersistentList { return persistentVectorOf() } diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt index 2b04cfe2..8686b1ee 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt @@ -6,7 +6,6 @@ package kotlinx.collections.immutable.implementations.immutableSet import kotlinx.collections.immutable.PersistentSet -import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.mutate internal class PersistentHashSet(internal val node: TrieNode, @@ -39,6 +38,20 @@ internal class PersistentHashSet(internal val node: TrieNode, return mutate { it.removeAll(predicate) } } + override fun retainAll(elements: Collection): PersistentSet { + return mutate { it.retainAll(elements) } + } + + override fun containsAll(elements: Collection): Boolean { + if (elements is PersistentHashSet) { + return node.containsAll(elements.node, 0) + } + if (elements is PersistentHashSetBuilder) { + return node.containsAll(elements.node, 0) + } + return super.containsAll(elements) + } + override fun clear(): PersistentSet { return PersistentHashSet.emptyOf() } diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt index 9109f371..a9bce437 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt @@ -48,17 +48,69 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() if (set !== null) { val deltaCounter = DeltaCounter() - val oldSize = this.size - node = node.mutableAddAll(set.node, 0, deltaCounter, this) - val newSize = oldSize + elements.size - deltaCounter.count - if (oldSize != newSize) { + val size = this.size + val result = node.mutableAddAll(set.node, 0, deltaCounter, this) + val newSize = size + elements.size - deltaCounter.count + if (size != newSize) { + this.node = result this.size = newSize } - return oldSize != this.size + return size != this.size } return super.addAll(elements) } + override fun retainAll(elements: Collection): Boolean { + val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() + if (set !== null) { + val deltaCounter = DeltaCounter() + val size = this.size + val result = node.mutableRetainAll(set.node, 0, deltaCounter, this) + when (val newSize = deltaCounter.count) { + 0 -> clear() + size -> {} + else -> { + @Suppress("UNCHECKED_CAST") + this.node = result as TrieNode + this.size = newSize + } + } + return size != this.size + } + return super.retainAll(elements) + } + + override fun removeAll(elements: Collection): Boolean { + val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() + if (set !== null) { + val counter = DeltaCounter() + val size = this.size + val result = node.mutableRemoveAll(set.node, 0, counter, this) + + when (val newSize = size - counter.count) { + 0 -> clear() + size -> {} + else -> { + @Suppress("UNCHECKED_CAST") + this.node = result as TrieNode + this.size = newSize + } + } + return size != this.size + } + return super.removeAll(elements) + } + + override fun containsAll(elements: Collection): Boolean { + if (elements is PersistentHashSet) { + return node.containsAll(elements.node, 0) + } + if (elements is PersistentHashSetBuilder) { + return node.containsAll(elements.node, 0) + } + return super.containsAll(elements) + } + override fun remove(element: E): Boolean { val size = this.size @Suppress("UNCHECKED_CAST") diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index da08a78a..f8f251c9 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -41,13 +41,20 @@ private fun Array.removeCellAtIndex(cellIndex: Int): Array { return newBuffer } +/** + * Writes all elements from [this] to [newArray], starting with [newArrayOffset], filtering + * on the fly using [predicate]. By default filters out [TrieNode.EMPTY] instances + * + * return number of elements written to [newArray] + **/ private inline fun Array.filterTo( newArray: Array, newArrayOffset: Int = 0, - predicate: (Any?) -> Boolean): Int { + predicate: (Any?) -> Boolean = { it !== TrieNode.EMPTY }): Int { var i = 0 var j = 0 while (i < size) { + assert(j <= i) // this is extremely important if newArray === this val e = this[i] if (predicate(e)) { newArray[newArrayOffset + j] = this[i] @@ -293,11 +300,11 @@ internal class TrieNode( } val totalSize = totalWritten + this.buffer.size intersectionSizeRef += (tempBuffer.size - totalSize) - if(totalSize == this.buffer.size) return this - if(totalSize == otherNode.buffer.size) return otherNode + if (totalSize == this.buffer.size) return this + if (totalSize == otherNode.buffer.size) return otherNode - val newBuffer = if(totalSize == tempBuffer.size) tempBuffer else tempBuffer.copyOf(newSize = totalSize) - return if(ownedBy == owner) { + val newBuffer = if (totalSize == tempBuffer.size) tempBuffer else tempBuffer.copyOf(newSize = totalSize) + return if (ownedBy == owner) { this.buffer = newBuffer this } else { @@ -305,11 +312,57 @@ internal class TrieNode( } } + private fun mutableCollisionRetainAll(otherNode: TrieNode, intersectionSizeRef: DeltaCounter, + owner: MutabilityOwnership): Any? { + if (this === otherNode) { + intersectionSizeRef += buffer.size + return this + } + val tempBuffer = + if (owner == ownedBy) buffer + else arrayOfNulls(minOf(buffer.size, otherNode.buffer.size)) + val totalWritten = buffer.filterTo(tempBuffer) { + @Suppress("UNCHECKED_CAST") + otherNode.collisionContainsElement(it as E) + } + intersectionSizeRef += totalWritten + return when (totalWritten) { + 0 -> EMPTY + 1 -> tempBuffer[0] + this.buffer.size -> this + otherNode.buffer.size -> otherNode + tempBuffer.size -> TrieNode(0, tempBuffer, owner) + else -> TrieNode(0, tempBuffer.copyOf(newSize = totalWritten), owner) + } + } + + private fun mutableCollisionRemoveAll(otherNode: TrieNode, + intersectionSizeRef: DeltaCounter, + owner: MutabilityOwnership): Any? { + if (this === otherNode) { + intersectionSizeRef += buffer.size + return EMPTY + } + val tempBuffer = if (owner == ownedBy) buffer else arrayOfNulls(buffer.size) + val totalWritten = buffer.filterTo(tempBuffer) { + @Suppress("UNCHECKED_CAST") + !otherNode.collisionContainsElement(it as E) + } + intersectionSizeRef += (buffer.size - totalWritten) + return when (totalWritten) { + 0 -> EMPTY + 1 -> tempBuffer[0] + this.buffer.size -> this + tempBuffer.size -> TrieNode(0, tempBuffer, owner) + else -> TrieNode(0, tempBuffer.copyOf(newSize = totalWritten), owner) + } + } + private fun calculateSize(): Int { if (bitmap == 0) return buffer.size var result = 0 for (e in buffer) { - result += when(e) { + result += when (e) { is TrieNode<*> -> e.calculateSize() else -> 1 } @@ -447,6 +500,250 @@ internal class TrieNode( } } + fun mutableRetainAll(otherNode: TrieNode, + shift: Int, + intersectionSizeRef: DeltaCounter, + mutator: PersistentHashSetBuilder<*>): Any? { + if (this === otherNode) { + intersectionSizeRef += calculateSize(); + return this + } + if (shift > MAX_SHIFT) { + return mutableCollisionRetainAll(otherNode, intersectionSizeRef, mutator.ownership) + } + // intersection mask contains bits that are set in both inputs + // this mask is not final 'cos some children may have no intersection + val newBitMap = bitmap and otherNode.bitmap + // zero means no nodes intersect + if (newBitMap == 0) return EMPTY + val mutableNode = + if (ownedBy == mutator.ownership && newBitMap == bitmap) this + else TrieNode(newBitMap, arrayOfNulls(newBitMap.countOneBits()), mutator.ownership) + // we need to keep track of the real mask 'cos some of the children may intersect to nothing + var realBitMap = 0 + // for each bit in intersection mask, try to intersect children + newBitMap.forEachOneBit { positionMask, newNodeIndex -> + val thisIndex = indexOfCellAt(positionMask) + val otherNodeIndex = otherNode.indexOfCellAt(positionMask) + val newValue = run { + val thisCell = buffer[thisIndex] + val otherNodeCell = otherNode.buffer[otherNodeIndex] + val thisIsNode = thisCell is TrieNode<*> + val otherIsNode = otherNodeCell is TrieNode<*> + when { + // both are nodes -> merge them recursively + thisIsNode && otherIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as TrieNode + thisCell.mutableRetainAll( + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + intersectionSizeRef, + mutator + ) + } + // one of them is a node -> check containment + thisIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as E + if (thisCell.contains(otherNodeCell.hashCode(), otherNodeCell, shift + LOG_MAX_BRANCHING_FACTOR)) { + intersectionSizeRef += 1 + otherNodeCell + } else EMPTY + } + // same as last case, but reversed + otherIsNode -> @Suppress("UNCHECKED_CAST") { + otherNodeCell as TrieNode + thisCell as E + if (otherNodeCell.contains(thisCell.hashCode(), thisCell, shift + LOG_MAX_BRANCHING_FACTOR)) { + intersectionSizeRef += 1 + thisCell + } else EMPTY + } + // both are just E => compare them + thisCell == otherNodeCell -> thisCell.also { intersectionSizeRef += 1 } + // both are just E, but different => return nothing + else -> EMPTY + } + } + if (newValue !== EMPTY) { + // elements that are not in realBitMap will be removed later + realBitMap = realBitMap or positionMask + } + mutableNode.buffer[newNodeIndex] = newValue + } + // resulting array's size is the popcount of resulting mask + val realSize = realBitMap.countOneBits() + return when { + realBitMap == 0 -> EMPTY + realBitMap == newBitMap -> { + when { + mutableNode.elementsIdentityEquals(this) -> this + mutableNode.elementsIdentityEquals(otherNode) -> otherNode + else -> mutableNode + } + } + // single values are kept only on root level + realSize == 1 && shift != 0 -> when (val single = mutableNode.buffer[mutableNode.indexOfCellAt(realBitMap)]) { + is TrieNode<*> -> TrieNode(realBitMap, arrayOf(single), mutator.ownership) + else -> single + } + else -> { + // clean up all the EMPTYs in the resulting buffer + val realBuffer = arrayOfNulls(realSize) + mutableNode.buffer.filterTo(realBuffer) + TrieNode(realBitMap, realBuffer, mutator.ownership) + } + } + } + + fun mutableRemoveAll(otherNode: TrieNode, shift: Int, + intersectionSizeRef: DeltaCounter, + mutator: PersistentHashSetBuilder<*>): Any? { + if (this === otherNode) { + intersectionSizeRef += calculateSize(); + return EMPTY + } + if (shift > MAX_SHIFT) { + return mutableCollisionRemoveAll(otherNode, intersectionSizeRef, mutator.ownership) + } + // same as with intersection, only children of both nodes are considered + // this mask is not final 'cos some children may have no intersection + val removalBitmap = bitmap and otherNode.bitmap + // zero means no intersection => nothing to remove + if (removalBitmap == 0) return this + // node here is either us (if we are mutable) or a mutable copy + val mutableNode = + if (ownedBy == mutator.ownership) this + else TrieNode(bitmap, buffer.copyOf(), mutator.ownership) + // keep track of the real mask + var realBitMap = bitmap + removalBitmap.forEachOneBit { positionMask, _ -> + val thisIndex = indexOfCellAt(positionMask) + val otherNodeIndex = otherNode.indexOfCellAt(positionMask) + val newValue = run { + val thisCell = buffer[thisIndex] + val otherNodeCell = otherNode.buffer[otherNodeIndex] + val thisIsNode = thisCell is TrieNode<*> + val otherIsNode = otherNodeCell is TrieNode<*> + when { + // both are nodes -> merge them recursively + thisIsNode && otherIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as TrieNode + thisCell.mutableRemoveAll( + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + intersectionSizeRef, + mutator + ) + } + // one of them is a node -> remove single element + thisIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as E + val oldSize = mutator.size + val removed = thisCell.mutableRemove( + otherNodeCell.hashCode(), + otherNodeCell, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator) + // additional check needed for removal + if (oldSize != mutator.size) { + intersectionSizeRef += 1 + if (removed.buffer.size == 1 && removed.buffer[0] !is TrieNode<*>) removed.buffer[0] + else removed + } else thisCell + } + // same as last case, but reversed + otherIsNode -> @Suppress("UNCHECKED_CAST") { + otherNodeCell as TrieNode + thisCell as E + // "removing" a node from a value is basically checking if the value is contained in the node + if (otherNodeCell.contains(thisCell.hashCode(), thisCell, shift + LOG_MAX_BRANCHING_FACTOR)) { + intersectionSizeRef += 1 + EMPTY + } else thisCell + } + // both are just E => compare them + thisCell == otherNodeCell -> { + intersectionSizeRef += 1 + EMPTY + } + // both are just E, but different => nothing to remove, return left + else -> thisCell + } + } + if (newValue === EMPTY) { + // if we removed something, keep track + realBitMap = realBitMap xor positionMask + } + mutableNode.buffer[thisIndex] = newValue + } + // resulting size is popcount of the resulting mask + val realSize = realBitMap.countOneBits() + return when { + realBitMap == 0 -> EMPTY + realBitMap == bitmap -> { + when { + mutableNode.elementsIdentityEquals(this) -> this + else -> mutableNode + } + } + // single values are kept only on root level + realSize == 1 && shift != 0 -> when (val single = mutableNode.buffer[mutableNode.indexOfCellAt(realBitMap)]) { + is TrieNode<*> -> TrieNode(realBitMap, arrayOf(single), mutator.ownership) + else -> single + } + else -> { + // clean up all the EMPTYs in the resulting buffer + val realBuffer = arrayOfNulls(realSize) + mutableNode.buffer.filterTo(realBuffer) + TrieNode(realBitMap, realBuffer, mutator.ownership) + } + } + } + + fun containsAll(otherNode: TrieNode, shift: Int): Boolean { + if (this === otherNode) return true + // essentially `buffer.containsAll(otherNode.buffer)` + if (shift > MAX_SHIFT) return otherNode.buffer.all { it in buffer } + + // potential bitmap is an intersection of input bitmaps + val potentialBitMap = bitmap and otherNode.bitmap + // left bitmap must contain right bitmap => right bitmap must be equal to intersection + if (potentialBitMap != otherNode.bitmap) return false + // check each child, shortcut to false if any one isn't contained + potentialBitMap.forEachOneBit { positionMask, _ -> + val thisIndex = indexOfCellAt(positionMask) + val otherNodeIndex = otherNode.indexOfCellAt(positionMask) + val thisCell = buffer[thisIndex] + val otherNodeCell = otherNode.buffer[otherNodeIndex] + val thisIsNode = thisCell is TrieNode<*> + val otherIsNode = otherNodeCell is TrieNode<*> + when { + // both are nodes => check recursively + thisIsNode && otherIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as TrieNode + thisCell.containsAll(otherNodeCell, shift + LOG_MAX_BRANCHING_FACTOR) || return false + } + // left is node, right is just E => check containment + thisIsNode -> @Suppress("UNCHECKED_CAST") { + thisCell as TrieNode + otherNodeCell as E + thisCell.contains(otherNodeCell.hashCode(), otherNodeCell, shift + LOG_MAX_BRANCHING_FACTOR) || return false + } + // left is just E, right is node => not possible + otherIsNode -> return false + // both are just E => containment is just equality + else -> thisCell == otherNodeCell || return false + } + } + return true + } + + fun add(elementHash: Int, element: E, shift: Int): TrieNode { val cellPositionMask = 1 shl indexSegment(elementHash, shift) diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt index 03a22e2a..656af288 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt @@ -85,6 +85,10 @@ internal class PersistentOrderedSet( return mutate { it.removeAll(predicate) } } + override fun retainAll(elements: Collection): PersistentSet { + return mutate { it.retainAll(elements) } + } + override fun clear(): PersistentSet { return PersistentOrderedSet.emptyOf() } diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index 4bbfcabd..b8cd2497 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -58,11 +58,11 @@ class ImmutableHashSetTest : ImmutableSetTestBase() { val builder1 = immutableSetOf().builder() val builder2 = immutableSetOf().builder() val expected = mutableSetOf() - for(i in 300..400) { + for (i in 300..400) { builder1.add("$i") expected.add("$i") } - for(i in 0..200) { + for (i in 0..200) { builder2.add("$i") expected.add("$i") } @@ -77,6 +77,125 @@ class ImmutableHashSetTest : ImmutableSetTestBase() { compareSets(expected, builder1.build()) } + @Test fun containsAll() { + assertTrue(immutableSetOf(1).containsAll(immutableSetOf())) + assertTrue(immutableSetOf(1, 2).containsAll(immutableSetOf(1))) + assertTrue((immutableSetOf() + (1..2000)).containsAll(immutableSetOf() + (400..1000))) + assertFalse((immutableSetOf() + (1..2000)).containsAll(immutableSetOf() + (1999..2001))) + } + + @Test fun retainAllElements() { + run { + val left = immutableSetOf() + (1..2000) + compareSets(immutableSetOf(), left.retainAll(immutableSetOf())) + compareSets(immutableSetOf(), immutableSetOf().retainAll(left)) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (2000..3000) + compareSets(immutableSetOf(2000), left intersect right) + compareSets(immutableSetOf(2000), right intersect left) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (2001..3000) + compareSets(immutableSetOf(), left intersect right) + compareSets(immutableSetOf(), right intersect left) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (200..3000) + compareSets(left.toSet() intersect right.toSet(), left intersect right) + } + + run { + val left = immutableSetOf() + (1..2000).map { IntWrapper(it, it % 200) } + val right = immutableSetOf() + (200..3000).map { IntWrapper(it, it % 200) } + compareSets(left.toSet() intersect right.toSet(), left intersect right) + } + + run { + val left = immutableSetOf() + (1..2000).map { IntWrapper(it, it % 200) } + val right = immutableSetOf() + (2001..3000).map { IntWrapper(it, it % 200) } + compareSets(left.toSet() intersect right.toSet(), left intersect right) + } + + run { + val left = immutableSetOf() + (1..2000).map { "$it" } + val right = immutableSetOf() + (200..3000).map { "$it" } + compareSets(left.toSet() intersect right.toSet(), left intersect right) + } + + run { + val left = immutableSetOf() + (1..2000) + assertSame(left, left intersect left) + val right = immutableSetOf() + (1..2000) + assertSame(right, right intersect left) + assertSame(left, left intersect right) + } + } + + @Test fun removeAllElements() { + run { + val left = immutableSetOf() + (1..2000) + assertSame(left, left.removeAll(immutableSetOf())) + assertSame(immutableSetOf(), immutableSetOf().removeAll(left)) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (2000..3000) + compareSets((1..1999).toSet(), left - right) + compareSets((2001..3000).toSet(), right - left) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (2001..3000) + assertSame(left, left - right) + assertSame(right, right - left) + } + + run { + val left = immutableSetOf() + (1..2000) + val right = immutableSetOf() + (200..3000) + compareSets(left.toSet() - right.toSet(), left - right) + compareSets(right.toSet() - left.toSet(), right - left) + } + + run { + val left = immutableSetOf() + (1..2000).map { IntWrapper(it, it % 200) } + val right = immutableSetOf() + (200..3000).map { IntWrapper(it, it % 200) } + compareSets(left.toSet() - right.toSet(), left - right) + compareSets(right.toSet() - left.toSet(), right - left) + } + + run { + val left = immutableSetOf() + (1..2000).map { IntWrapper(it, it % 200) } + val right = immutableSetOf() + (2001..3000).map { IntWrapper(it, it % 200) } + compareSets(left.toSet() - right.toSet(), left - right) + compareSets(right.toSet() - left.toSet(), right - left) + } + + run { + val left = immutableSetOf() + (1..2000).map { "$it" } + val right = immutableSetOf() + (200..3000).map { "$it" } + compareSets(left.toSet() - right.toSet(), left - right) + compareSets(right.toSet() - left.toSet(), right - left) + } + + run { + val left = immutableSetOf() + (1..2000) + compareSets(immutableSetOf(), left - left) + val right = immutableSetOf() + (1..2000) + compareSets(immutableSetOf(), right - left) + compareSets(immutableSetOf(), left - right) + } + } + } class ImmutableOrderedSetTest : ImmutableSetTestBase() { override fun immutableSetOf(vararg elements: T) = persistentSetOf(*elements) From 304e8e7c744e79cab25dd153d97c9144d7f167bc Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 19 Nov 2020 14:15:41 +0300 Subject: [PATCH 058/162] Bug in PersistentList - list is broken after removeAll call #92 --- .../immutableList/PersistentVectorBuilder.kt | 2 +- .../src/contract/list/ImmutableListTest.kt | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt index ccbb041b..6d788ab5 100644 --- a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt +++ b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt @@ -755,7 +755,7 @@ internal class PersistentVectorBuilder(private var vector: PersistentList, while (lastIndex shr rootShift == 0) { rootShift -= LOG_MAX_BUFFER_SIZE @Suppress("UNCHECKED_CAST") - newRoot = root[0] as Array + newRoot = newRoot[0] as Array } return nullifyAfter(newRoot, lastIndex, rootShift) } diff --git a/core/commonTest/src/contract/list/ImmutableListTest.kt b/core/commonTest/src/contract/list/ImmutableListTest.kt index b6be9de2..4c959f35 100644 --- a/core/commonTest/src/contract/list/ImmutableListTest.kt +++ b/core/commonTest/src/contract/list/ImmutableListTest.kt @@ -29,6 +29,19 @@ class ImmutableListTest { } + @Test + fun persistentListFails() { + var xs = persistentListOf( + *(1..1885).map { it }.toTypedArray() + ) + + xs = xs.removeAll( + (1..1837).map { it } + ) + + assertEquals((1838..1885).toList(), xs) + } + @Test fun ofElements() { val list0 = listOf("a", "d", 1, null) val list1 = persistentListOf("a", "d", 1, null) From 2c1b70cdc5b40831a2c30a6328c9360346447796 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 30 Nov 2020 03:17:14 +0300 Subject: [PATCH 059/162] Add some stress tests --- core/commonTest/src/stress/list/PersistentListTest.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/commonTest/src/stress/list/PersistentListTest.kt b/core/commonTest/src/stress/list/PersistentListTest.kt index 182c0471..d6e3f38a 100644 --- a/core/commonTest/src/stress/list/PersistentListTest.kt +++ b/core/commonTest/src/stress/list/PersistentListTest.kt @@ -369,7 +369,9 @@ class PersistentListTest : ExecutionTimeMeasuringTest() { removeElements.add(List(1) { Random.nextInt(initialSize) }) // a random element removeElements.add(List(maxBufferSize) { Random.nextInt(initialSize) }) // random elements removeElements.add(List(initialSize / 2) { Random.nextInt(initialSize) }) // ~half elements - removeElements.add(List(initialSize) { Random.nextInt(initialSize) }) // ~all elements + removeElements.add(List(initialSize) { Random.nextInt(initialSize) }) // ~more than half elements + removeElements.add(List(initialSize / 2) { it }) // ~first half + removeElements.add(List(initialSize / 2) { initialSize - it }) // ~last half } if (initialSize > maxBufferSize) { val rootSize = (initialSize - 1) and (maxBufferSize - 1).inv() @@ -377,11 +379,18 @@ class PersistentListTest : ExecutionTimeMeasuringTest() { removeElements.add(List(maxBufferSize) { it }) // first leaf removeElements.add(List(tailSize) { rootSize + it }) // tail removeElements.add(List(maxBufferSize) { rootSize - it }) // last leaf + for (shift in 5 until 30 step 5) { + val branches = 1 shl shift + if (branches > rootSize) break + removeElements.add(initialElements.shuffled().take(initialSize - rootSize / branches)) // decrease root height + } } for (elements in removeElements) { val expected = initialElements.toMutableList().also { it.removeAll(elements) } +// println("${initialElements.size} -> ${expected.size} : ${initialElements.size.toDouble() / expected.size}") + val result = list.removeAll(elements) val resultPredicate = list.let { From 531b1654c46289fe9da515f80451df6e9f340cb8 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 9 Feb 2021 04:19:59 +0300 Subject: [PATCH 060/162] Move JDK_6 environment setup to additionalConfiguration ...to avoid overwriting when regenerating TC project --- .teamcity/additionalConfiguration.kt | 3 +++ .teamcity/settings.kts | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index f041037e..f25bae31 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -6,5 +6,8 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.Project fun Project.additionalConfiguration() { + params { + param("env.JDK_6", "%env.JDK_16%") + } subProject(benchmarksProject(knownBuilds.buildVersion)) } \ No newline at end of file diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index 320cc9b2..b8d418b9 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -30,7 +30,6 @@ project { // Disable editing of project and build settings from the UI to avoid issues with TeamCity params { param("teamcity.ui.settings.readOnly", "true") - param("env.JDK_6", "%env.JDK_16%") } val buildVersion = buildVersion() From 494df92f420fd264e658c16b8373432bcf8a0ba3 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Mon, 8 Feb 2021 21:36:52 +0300 Subject: [PATCH 061/162] Setup Sonatype publishing with kotlinx-team-infra plugin --- .teamcity/settings.kts | 12 +++++++++++- .teamcity/utils.kt | 7 +++++-- build.gradle.kts | 22 ++++------------------ core/build.gradle.kts | 6 ++++++ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index b8d418b9..f4610a64 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -46,10 +46,16 @@ project { val deployVersion = deployVersion().apply { dependsOnSnapshot(buildAll, onFailure = FailureAction.IGNORE) + dependsOnSnapshot(BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID) { + reuseBuilds = ReuseBuilds.NO + } } val deploys = platforms.map { deploy(it, deployVersion) } val deployPublish = deployPublish(deployVersion).apply { dependsOnSnapshot(buildAll, onFailure = FailureAction.IGNORE) + dependsOnSnapshot(BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID) { + reuseBuilds = ReuseBuilds.NO + } deploys.forEach { dependsOnSnapshot(it) } @@ -139,6 +145,8 @@ fun Project.deployVersion() = BuildType { param("bintray-user", bintrayUserName) password("bintray-key", bintrayToken) param(versionSuffixParameter, "dev-%build.counter%") + param("reverse.dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.system.libs.repo.description", libraryStagingRepoDescription) + param("env.libs.repository.id", "%dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.env.libs.repository.id%") } requirements { @@ -149,7 +157,7 @@ fun Project.deployVersion() = BuildType { steps { gradle { name = "Verify Gradle Configuration" - tasks = "clean publishBintrayCreateVersion" + tasks = "clean publishPrepareVersion" gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter% -PbintrayApiKey=%bintray-key% -PbintrayUser=%bintray-user%" buildFile = "" jdkHome = "%env.$jdk%" @@ -167,6 +175,7 @@ fun Project.deployPublish(configureBuild: BuildType) = BuildType { // Tell configuration build how to get release version parameter from this build // "dev" is the default and means publishing is not releasing to public text(configureBuild.reverseDepParamRefs[releaseVersionParameter].name, "dev", display = ParameterDisplay.PROMPT, label = "Release Version") + param("env.libs.repository.id", "%dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.env.libs.repository.id%") } commonConfigure() }.also { buildType(it) } @@ -181,6 +190,7 @@ fun Project.deploy(platform: Platform, configureBuild: BuildType) = buildType("D param(releaseVersionParameter, "${configureBuild.depParamRefs[releaseVersionParameter]}") param("bintray-user", bintrayUserName) password("bintray-key", bintrayToken) + param("env.libs.repository.id", "%dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.env.libs.repository.id%") } vcs { diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index ea580099..6fa0973f 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -11,6 +11,7 @@ const val releaseVersionParameter = "releaseVersion" const val bintrayUserName = "%env.BINTRAY_USER%" const val bintrayToken = "%env.BINTRAY_API_KEY%" +const val libraryStagingRepoDescription = "Kotlin-Immutable-Collections" val platforms = Platform.values() const val jdk = "JDK_18_x64" @@ -37,6 +38,8 @@ const val BUILD_ALL_ID = "Build_All" const val DEPLOY_CONFIGURE_VERSION_ID = "Deploy_Configure" const val DEPLOY_PUBLISH_ID = "Deploy_Publish" +val BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID = AbsoluteId("KotlinTools_CreateSonatypeStagingRepository") + class KnownBuilds(private val project: Project) { private fun buildWithId(id: String): BuildType { return project.buildTypes.single { it.id.toString().endsWith(id) } @@ -103,12 +106,12 @@ fun BuildType.commonConfigure() { } } -fun BuildType.dependsOn(build: BuildType, configure: Dependency.() -> Unit) = +fun BuildType.dependsOn(build: IdOwner, configure: Dependency.() -> Unit) = apply { dependencies.dependency(build, configure) } -fun BuildType.dependsOnSnapshot(build: BuildType, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { +fun BuildType.dependsOnSnapshot(build: IdOwner, onFailure: FailureAction = FailureAction.FAIL_TO_START, configure: SnapshotDependency.() -> Unit = {}) = apply { dependencies.dependency(build) { snapshot { configure() diff --git a/build.gradle.kts b/build.gradle.kts index cceb81a2..b9d15fce 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,32 +10,18 @@ buildscript { } plugins { - id("kotlinx.team.infra") version "0.2.0-dev-55" + id("kotlinx.team.infra") version "0.3.0-dev-64" } infra { teamcity { - bintrayUser = "%env.BINTRAY_USER%" - bintrayToken = "%env.BINTRAY_API_KEY%" + libraryStagingRepoDescription = project.name } publishing { include(":kotlinx-collections-immutable") - bintray { - organization = "kotlin" - repository = "kotlinx" - library = "kotlinx.collections.immutable" - username = findProperty("bintrayUser") as String? - password = findProperty("bintrayApiKey") as String? - } - - bintrayDev { - organization = "kotlin" - repository = "kotlin-dev" - library = "kotlinx.collections.immutable" - username = findProperty("bintrayUser") as String? - password = findProperty("bintrayApiKey") as String? - } + libraryRepoUrl = "https://github.com/Kotlin/kotlinx.collections.immutable" + sonatype {} } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 655f25d3..6ae9bea2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,3 +1,5 @@ +import kotlinx.team.infra.mavenPublicationsPom + plugins { id("kotlin-multiplatform") `maven-publish` @@ -7,6 +9,10 @@ base { archivesBaseName = "kotlinx-collections-immutable" // doesn't work } +mavenPublicationsPom { + description.set("Kotlin Immutable Collections multiplatform library") +} + val JDK_6: String by project kotlin { From d6f4a4025cd68a648be4ba37e8415c854041ae35 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 19 Feb 2021 22:00:58 +0300 Subject: [PATCH 062/162] Upgrade gradle version to 6.8 --- gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 35 +++++++++++------------ gradlew.bat | 25 +++++----------- 4 files changed, 24 insertions(+), 38 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f 100644 GIT binary patch delta 23334 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 WDWbsF$p{Gi@ZUasP|4|kdH)CXgbPdn delta 19998 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6623300b..da9702f9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 8e25e6c1..4f906e0c 100755 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -125,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -154,19 +156,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 9618d8d9..107acd32 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From fe62b2024c712627e58c531b4a643012aad5691f Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 19 Feb 2021 22:06:00 +0300 Subject: [PATCH 063/162] Upgrade kotlin version to 1.4.30 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index b9d15fce..86dd3a6f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30") } } From a61d68a7c32de4119d8c1c408b5c2104d53b8a3e Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 19 Feb 2021 23:49:44 +0300 Subject: [PATCH 064/162] Upgrade benchmark library and plugin version to 0.3.0 --- benchmarks/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 10339925..bd7e7981 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("kotlinx.benchmark") version "0.2.0-dev-20" + id("org.jetbrains.kotlinx.benchmark") version "0.3.0" } @@ -48,7 +48,7 @@ kotlin { commonMain { dependencies { api("org.jetbrains.kotlin:kotlin-stdlib-common") - api("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") + api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") api(project(":kotlinx-collections-immutable")) } } From cd3c09d26a176ec2d98d5fd9476ed081ad06ca15 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 19 Feb 2021 23:53:18 +0300 Subject: [PATCH 065/162] Remove redundant bintray repository declarations --- benchmarks/build.gradle.kts | 6 ------ benchmarks/runner/build.gradle | 6 ------ build.gradle.kts | 7 ------- settings.gradle | 4 +--- 4 files changed, 1 insertion(+), 22 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index bd7e7981..7fadb46b 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -11,12 +11,6 @@ evaluationDependsOn(":kotlinx-collections-immutable") val JDK_6: String by project -repositories { - maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") - maven(url = "https://dl.bintray.com/kotlin/kotlinx") -} - - kotlin { infra { target("macosX64") diff --git a/benchmarks/runner/build.gradle b/benchmarks/runner/build.gradle index 3a080dbb..9d7f3728 100644 --- a/benchmarks/runner/build.gradle +++ b/benchmarks/runner/build.gradle @@ -2,12 +2,6 @@ plugins { id 'org.jetbrains.kotlin.jvm' } -repositories { - mavenCentral() - jcenter() - maven { url 'https://dl.bintray.com/orangy/maven' } -} - dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib" implementation 'org.openjdk.jmh:jmh-core:1.21' diff --git a/build.gradle.kts b/build.gradle.kts index 86dd3a6f..fe9aba73 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,4 @@ buildscript { - repositories { - mavenCentral() - maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") - maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") - } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30") } @@ -30,7 +25,5 @@ val JDK_6 by ext(System.getenv("JDK_6") ?: findProperty("JDK_6") as String? ?: e allprojects { repositories { mavenCentral() - maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") - maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bc2384de..ebeb575e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,6 @@ pluginManagement { repositories { - maven { url 'https://dl.bintray.com/kotlin/kotlinx' } - maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } - maven { url 'https://dl.bintray.com/kotlin/kotlin-dev' } + maven { url 'https://dl.bintray.com/kotlin/kotlinx' } // for infra gradlePluginPortal() } } From b3e76c94881d9d0177739179331f8722763a78a5 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 23 Mar 2021 02:00:22 +0300 Subject: [PATCH 066/162] Check for Map.Entry in KT-30016 workaround --- .../immutableMap/PersistentHashMapBuilderContentViews.kt | 4 ++-- .../PersistentOrderedMapBuilderContentViews.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt index b8027a27..aa3578c6 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt @@ -21,7 +21,7 @@ internal class PersistentHashMapBuilderEntries(private val builder: Persis override fun remove(element: MutableMap.MutableEntry): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is MutableMap.MutableEntry<*, *>) return false + if ((element as Any?) !is Map.Entry<*, *>) return false return builder.remove(element.key, element.value) } @@ -30,7 +30,7 @@ internal class PersistentHashMapBuilderEntries(private val builder: Persis override fun contains(element: MutableMap.MutableEntry): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is MutableMap.MutableEntry<*, *>) return false + if ((element as Any?) !is Map.Entry<*, *>) return false return builder[element.key]?.let { candidate -> candidate == element.value } ?: (element.value == null && builder.containsKey(element.key)) } diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt index 1b4d4c76..cd9e165d 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt @@ -21,7 +21,7 @@ internal class PersistentOrderedMapBuilderEntries(private val builder: Per override fun remove(element: MutableMap.MutableEntry): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is MutableMap.MutableEntry<*, *>) return false + if ((element as Any?) !is Map.Entry<*, *>) return false return builder.remove(element.key, element.value) } @@ -30,7 +30,7 @@ internal class PersistentOrderedMapBuilderEntries(private val builder: Per override fun contains(element: MutableMap.MutableEntry): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is MutableMap.MutableEntry<*, *>) return false + if ((element as Any?) !is Map.Entry<*, *>) return false return builder[element.key]?.let { candidate -> candidate == element.value } ?: (element.value == null && builder.containsKey(element.key)) } From d22b14991431f473af1a47b5e13c88917fae03f6 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 23 Mar 2021 02:35:07 +0300 Subject: [PATCH 067/162] Correctly implement specialized MutableEntrySet.contains KT-41278 --- .../PersistentHashMapBuilderContentViews.kt | 16 +++- ...PersistentOrderedMapBuilderContentViews.kt | 8 +- .../src/contract/map/KT41278Test.kt | 82 +++++++++++++++++++ 3 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 core/commonTest/src/contract/map/KT41278Test.kt diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt index aa3578c6..3746f504 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt @@ -5,8 +5,18 @@ package kotlinx.collections.immutable.implementations.immutableMap +// intermediate abstract class to workaround KT-43321 +internal abstract class AbstractMapBuilderEntries, K, V> : AbstractMutableSet() { + final override fun contains(element: E): Boolean { + // TODO: Eliminate this check after KT-30016 gets fixed. + if ((element as? Any?) !is Map.Entry<*, *>) return false + return containsEntry(element) + } + abstract fun containsEntry(element: Map.Entry): Boolean +} + internal class PersistentHashMapBuilderEntries(private val builder: PersistentHashMapBuilder) - : MutableSet>, AbstractMutableSet>() { + : AbstractMapBuilderEntries, K, V>() { override fun add(element: MutableMap.MutableEntry): Boolean { throw UnsupportedOperationException() } @@ -28,9 +38,7 @@ internal class PersistentHashMapBuilderEntries(private val builder: Persis override val size: Int get() = builder.size - override fun contains(element: MutableMap.MutableEntry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false + override fun containsEntry(element: Map.Entry): Boolean { return builder[element.key]?.let { candidate -> candidate == element.value } ?: (element.value == null && builder.containsKey(element.key)) } diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt index cd9e165d..07787c6d 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt @@ -5,8 +5,10 @@ package kotlinx.collections.immutable.implementations.persistentOrderedMap +import kotlinx.collections.immutable.implementations.immutableMap.AbstractMapBuilderEntries + internal class PersistentOrderedMapBuilderEntries(private val builder: PersistentOrderedMapBuilder) - : MutableSet>, AbstractMutableSet>() { + : AbstractMapBuilderEntries, K, V>() { override fun add(element: MutableMap.MutableEntry): Boolean { throw UnsupportedOperationException() } @@ -28,9 +30,7 @@ internal class PersistentOrderedMapBuilderEntries(private val builder: Per override val size: Int get() = builder.size - override fun contains(element: MutableMap.MutableEntry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false + override fun containsEntry(element: Map.Entry): Boolean { return builder[element.key]?.let { candidate -> candidate == element.value } ?: (element.value == null && builder.containsKey(element.key)) } diff --git a/core/commonTest/src/contract/map/KT41278Test.kt b/core/commonTest/src/contract/map/KT41278Test.kt new file mode 100644 index 00000000..5d194ae4 --- /dev/null +++ b/core/commonTest/src/contract/map/KT41278Test.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.map + +import kotlinx.collections.immutable.persistentHashMapOf +import kotlinx.collections.immutable.persistentMapOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class KT41278Test { + // Based on https://youtrack.jetbrains.com/issue/KT-42428. + private fun doTest(map: Map, key: String, value: Int, createEntry: (String, Int) -> Map.Entry) { + assertTrue(map.keys.contains(key)) + assertEquals(value, map[key]) + // This one requires special efforts to make it work this way. + // map.entries can in fact be `MutableSet`, + // which [contains] method takes [MutableEntry], so the compiler may generate special bridge + // returning false for values that aren't [MutableEntry]. + assertTrue(map.entries.contains(createEntry(key, value))) + assertTrue(map.entries.toSet().contains(createEntry(key, value))) + + assertFalse(map.entries.contains(null as Any?)) + assertFalse(map.entries.contains("not an entry" as Any?)) + } + + @Test + fun persistentOrderedMap() { + val mapLetterToIndex = ('a'..'z').mapIndexed { i, c -> "$c" to i }.fold(persistentMapOf()) { map, pair -> + map.put(pair.first, pair.second) + } + + doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + } + + @Test + fun persistentHashMap() { + val mapLetterToIndex = ('a'..'z').mapIndexed { i, c -> "$c" to i }.fold(persistentHashMapOf()) { map, pair -> + map.put(pair.first, pair.second) + } + + doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + } + + @Test + fun persistentOrderedMapBuilder() { + val mapLetterToIndex = persistentMapOf().builder().apply { putAll(('a'..'z').mapIndexed { i, c -> "$c" to i }) } + + doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + } + + @Test + fun persistentHashMapBuilder() { + val mapLetterToIndex = persistentHashMapOf().builder().apply { putAll(('a'..'z').mapIndexed { i, c -> "$c" to i }) } + + doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + } +} + +private class TestMapEntry(override val key: K, override val value: V) : Map.Entry { + override fun toString(): String = "$key=$value" + override fun hashCode(): Int = key.hashCode() xor value.hashCode() + override fun equals(other: Any?): Boolean = + other is Map.Entry<*, *> && key == other.key && value == other.value +} + +private class TestMutableMapEntry(override val key: K, override val value: V) : MutableMap.MutableEntry { + override fun toString(): String = "$key=$value" + override fun hashCode(): Int = key.hashCode() xor value.hashCode() + override fun equals(other: Any?): Boolean = + other is Map.Entry<*, *> && key == other.key && value == other.value + + override fun setValue(newValue: V): V = TODO("Not yet implemented") +} From 1fdfa9df66c89d24077330be27061333a4663f0b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 23 Mar 2021 08:17:34 +0300 Subject: [PATCH 068/162] Correctly implement specialized MutableEntrySet.remove KT-41278 --- .../PersistentHashMapBuilderContentViews.kt | 11 +++-- ...PersistentOrderedMapBuilderContentViews.kt | 4 +- .../src/contract/map/KT41278Test.kt | 41 +++++++++++++++---- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt index 3746f504..07e4f1f0 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt @@ -13,6 +13,13 @@ internal abstract class AbstractMapBuilderEntries, K, V> : A return containsEntry(element) } abstract fun containsEntry(element: Map.Entry): Boolean + + final override fun remove(element: E): Boolean { + // TODO: Eliminate this check after KT-30016 gets fixed. + if ((element as? Any?) !is Map.Entry<*, *>) return false + return removeEntry(element) + } + abstract fun removeEntry(element: Map.Entry): Boolean } internal class PersistentHashMapBuilderEntries(private val builder: PersistentHashMapBuilder) @@ -29,9 +36,7 @@ internal class PersistentHashMapBuilderEntries(private val builder: Persis return PersistentHashMapBuilderEntriesIterator(builder) } - override fun remove(element: MutableMap.MutableEntry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false + override fun removeEntry(element: Map.Entry): Boolean { return builder.remove(element.key, element.value) } diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt index 07787c6d..9f8e6bc0 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt @@ -21,9 +21,7 @@ internal class PersistentOrderedMapBuilderEntries(private val builder: Per return PersistentOrderedMapBuilderEntriesIterator(builder) } - override fun remove(element: MutableMap.MutableEntry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false + override fun removeEntry(element: Map.Entry): Boolean { return builder.remove(element.key, element.value) } diff --git a/core/commonTest/src/contract/map/KT41278Test.kt b/core/commonTest/src/contract/map/KT41278Test.kt index 5d194ae4..47041b4d 100644 --- a/core/commonTest/src/contract/map/KT41278Test.kt +++ b/core/commonTest/src/contract/map/KT41278Test.kt @@ -14,7 +14,7 @@ import kotlin.test.assertTrue class KT41278Test { // Based on https://youtrack.jetbrains.com/issue/KT-42428. - private fun doTest(map: Map, key: String, value: Int, createEntry: (String, Int) -> Map.Entry) { + private fun doContainsTest(map: Map, key: String, value: Int, createEntry: (String, Int) -> Map.Entry) { assertTrue(map.keys.contains(key)) assertEquals(value, map[key]) // This one requires special efforts to make it work this way. @@ -28,14 +28,28 @@ class KT41278Test { assertFalse(map.entries.contains("not an entry" as Any?)) } + private fun doRemoveTest(map: MutableMap, key: String, value: Int, createEntry: (String, Int) -> Map.Entry) { + assertTrue(map.keys.contains(key)) + assertEquals(value, map[key]) + // This one requires special efforts to make it work this way. + // map.entries can in fact be `MutableSet`, + // which [remove] method takes [MutableEntry], so the compiler may generate special bridge + // returning false for values that aren't [MutableEntry]. + assertTrue(map.entries.toMutableSet().remove(createEntry(key, value))) + assertTrue(map.entries.remove(createEntry(key, value))) + } + @Test fun persistentOrderedMap() { val mapLetterToIndex = ('a'..'z').mapIndexed { i, c -> "$c" to i }.fold(persistentMapOf()) { map, pair -> map.put(pair.first, pair.second) } - doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) - doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + + doRemoveTest(mapLetterToIndex.builder(), "h", 7, ::TestMapEntry) + doRemoveTest(mapLetterToIndex.builder(), "h", 7, ::TestMutableMapEntry) } @Test @@ -44,24 +58,33 @@ class KT41278Test { map.put(pair.first, pair.second) } - doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) - doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + + doRemoveTest(mapLetterToIndex.builder(), "h", 7, ::TestMapEntry) + doRemoveTest(mapLetterToIndex.builder(), "h", 7, ::TestMutableMapEntry) } @Test fun persistentOrderedMapBuilder() { val mapLetterToIndex = persistentMapOf().builder().apply { putAll(('a'..'z').mapIndexed { i, c -> "$c" to i }) } - doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) - doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + + doRemoveTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doRemoveTest(mapLetterToIndex, "b", 1, ::TestMutableMapEntry) } @Test fun persistentHashMapBuilder() { val mapLetterToIndex = persistentHashMapOf().builder().apply { putAll(('a'..'z').mapIndexed { i, c -> "$c" to i }) } - doTest(mapLetterToIndex, "h", 7, ::TestMapEntry) - doTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doContainsTest(mapLetterToIndex, "h", 7, ::TestMutableMapEntry) + + doRemoveTest(mapLetterToIndex, "h", 7, ::TestMapEntry) + doRemoveTest(mapLetterToIndex, "b", 1, ::TestMutableMapEntry) } } From 0fd4b0617b7bcc62b71a8826c74567f5f5cf80ab Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 30 Mar 2021 00:56:49 +0300 Subject: [PATCH 069/162] Add Maven Central and Teamcity badges --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bcce05ff..5041427f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Immutable Collections Library for Kotlin -[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [ ![Download](https://api.bintray.com/packages/kotlin/kotlinx/kotlinx.collections.immutable/images/download.svg) ](https://bintray.com/kotlin/kotlinx/kotlinx.collections.immutable/_latestVersion) +[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) +[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) +[![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) +[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.jetbrains.kotlinx%22%20AND%20a:%22kotlinx-collections-immutable%22) Immutable collection interfaces and implementation prototypes for Kotlin. From 3b6a3371c1e037a6e97ad52be7dcfe4d420cd469 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 30 Mar 2021 01:34:53 +0300 Subject: [PATCH 070/162] Update CHANGELOG.md for 0.3.4 release --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c867cb0f..90002f12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,18 @@ # CHANGELOG -## 0.3.3 +## 0.3.4 + +- Upgrade Kotlin version up to 1.4.30 +- Publish the library to Maven Central instead of Bintray #[96](https://github.com/Kotlin/kotlinx.collections.immutable/issues/96). +- Add license information to published POMs [#98](https://github.com/Kotlin/kotlinx.collections.immutable/issues/98). +- Implement workaround for specialized MutableEntrySet.contains/remove [KT-41278](https://youtrack.jetbrains.com/issue/KT-41278). +- Bug in PersistentList - list is broken after removeAll call [#92](https://github.com/Kotlin/kotlinx.collections.immutable/issues/92). +- Faster bulk operations for non-ordered sets (removeAll, retainAll, containsAll) [#91](https://github.com/Kotlin/kotlinx.collections.immutable/issues/91). +- Faster addAll/putAll implementation for non-ordered sets/maps [#83](https://github.com/Kotlin/kotlinx.collections.immutable/issues/83). +- Add extension functions to convert Sequence to persistent collections [84](https://github.com/Kotlin/kotlinx.collections.immutable/issues/84). +- Add missing CharSequence.toImmutableSet() extension function. + +## 0.3.3 - Upgrade Kotlin version up to 1.4.0 - Weaken receiver type of toPersistentHashSet to Iterable [#77](https://github.com/Kotlin/kotlinx.collections.immutable/issues/77). From 75ed654e34e7111a076d87b8c075fd140e6b4cf7 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 30 Mar 2021 01:58:49 +0300 Subject: [PATCH 071/162] Update README.md for 0.3.4 release --- README.md | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 5041427f..8353f0da 100644 --- a/README.md +++ b/README.md @@ -114,62 +114,44 @@ collection.mutate { some_actions_on(it) } > Note that the library is experimental and the API is subject to change. -The library is published to [kotlinx](https://bintray.com/kotlin/kotlinx/kotlinx.collections.immutable) bintray repository and available in jcenter too. +The library is published to Maven Central repository. -The library depends on the Kotlin Standard Library of the version at least `1.4.0`. +The library depends on the Kotlin Standard Library of the version at least `1.4.30`. ### Gradle -Add the bintray repository: +Add the Maven Central repository: ```groovy repositories { - jcenter() + mavenCentral() } ``` -In multiplatform projects add the following dependency to the common source set: +Add the library to dependencies of the platform source set, e.g.: ```groovy kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.3") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.4") } } } } ``` -To use the library in a JVM-only project add the platform to the artifact name, e.g.: - -```groovy -implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.3") -``` - ### Maven -Add the bintray repository to `` section: - -```xml - - - false - - jcenter - jcenter - https://jcenter.bintray.com/ - -``` - +The Maven Central repository is available for dependency lookup by default. Add dependencies (you can also add other modules that you need): ```xml org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.3 + 0.3.4 ``` From 2e582b90541ba59f5d1427778240ae900addf386 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 20 Apr 2021 20:32:20 +0300 Subject: [PATCH 072/162] Space instead of bintray for hosting kotlinx.team.infra --- settings.gradle | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/settings.gradle b/settings.gradle index ebeb575e..3708b453 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,12 +1,10 @@ pluginManagement { repositories { - maven { url 'https://dl.bintray.com/kotlin/kotlinx' } // for infra + maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlinx/maven' } gradlePluginPortal() } } -enableFeaturePreview('GRADLE_METADATA') - rootProject.name = 'Kotlin-Immutable-Collections' // TODO: Make readable name when it's not used in js module names include ':core' From 57892e67eea5c402e4dea3fc8af4bf1478ff7e1c Mon Sep 17 00:00:00 2001 From: Sebastian Aigner Date: Mon, 19 Apr 2021 17:30:50 +0200 Subject: [PATCH 073/162] Add "JS IR supported" badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8353f0da..339912d6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.jetbrains.kotlinx%22%20AND%20a:%22kotlinx-collections-immutable%22) +[![IR](https://img.shields.io/badge/Kotlin%2FJS-IR%20supported-yellow)](https://kotl.in/jsirsupported) Immutable collection interfaces and implementation prototypes for Kotlin. From b3d7f0b4bc3c7468d2293731a32cf67c63a419fd Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 14 May 2021 03:45:50 +0300 Subject: [PATCH 074/162] Extract key index search in collision node to a separate function Co-authored-by: Mikhail Belyaev --- .../implementations/immutableMap/TrieNode.kt | 90 +++++++++---------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 8f5cbcf9..ddabde41 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -346,32 +346,31 @@ internal class TrieNode( return TrieNode(0, 0, newBuffer, mutator.ownership) } - private fun collisionContainsKey(key: K): Boolean { + private fun collisionKeyIndex(key: Any?): Int { for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == buffer[i]) return true + if (key == keyAtIndex(i)) return i } - return false + return -1 + } + + private fun collisionContainsKey(key: K): Boolean { + return collisionKeyIndex(key) != -1 } private fun collisionGet(key: K): V? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i)) { - return valueAtKeyIndex(i) - } - } - return null + val keyIndex = collisionKeyIndex(key) + return if (keyIndex != -1) valueAtKeyIndex(keyIndex) else null } private fun collisionPut(key: K, value: V): ModificationResult? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i)) { - if (value === valueAtKeyIndex(i)) { - return null - } - val newBuffer = buffer.copyOf() - newBuffer[i + 1] = value - return TrieNode(0, 0, newBuffer).asUpdateResult() + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1) { + if (value === valueAtKeyIndex(keyIndex)) { + return null } + val newBuffer = buffer.copyOf() + newBuffer[keyIndex + 1] = value + return TrieNode(0, 0, newBuffer).asUpdateResult() } val newBuffer = buffer.insertEntryAtIndex(0, key, value) return TrieNode(0, 0, newBuffer).asInsertResult() @@ -379,23 +378,22 @@ internal class TrieNode( private fun mutableCollisionPut(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode { // Check if there is an entry with the specified key. - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i)) { // found entry with the specified key - mutator.operationResult = valueAtKeyIndex(i) + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1) { // found entry with the specified key + mutator.operationResult = valueAtKeyIndex(keyIndex) - // If the [mutator] is exclusive owner of this node, update value of the entry in-place. - if (ownedBy === mutator.ownership) { - buffer[i + 1] = value - return this - } - - // Structural change due to node replacement. - mutator.modCount++ - // Create new node with updated entry value. - val newBuffer = buffer.copyOf() - newBuffer[i + 1] = value - return TrieNode(0, 0, newBuffer, mutator.ownership) + // If the [mutator] is exclusive owner of this node, update value of the entry in-place. + if (ownedBy === mutator.ownership) { + buffer[keyIndex + 1] = value + return this } + + // Structural change due to node replacement. + mutator.modCount++ + // Create new node with updated entry value. + val newBuffer = buffer.copyOf() + newBuffer[keyIndex + 1] = value + return TrieNode(0, 0, newBuffer, mutator.ownership) } // Create new collision node with the specified entry added to it. mutator.size++ @@ -404,37 +402,33 @@ internal class TrieNode( } private fun collisionRemove(key: K): TrieNode? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i)) { - return collisionRemoveEntryAtIndex(i) - } + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1) { + return collisionRemoveEntryAtIndex(keyIndex) } return this } private fun mutableCollisionRemove(key: K, mutator: PersistentHashMapBuilder): TrieNode? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i)) { - return mutableCollisionRemoveEntryAtIndex(i, mutator) - } + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1) { + return mutableCollisionRemoveEntryAtIndex(keyIndex, mutator) } return this } private fun collisionRemove(key: K, value: V): TrieNode? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { - return collisionRemoveEntryAtIndex(i) - } + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1 && value == valueAtKeyIndex(keyIndex)) { + return collisionRemoveEntryAtIndex(keyIndex) } return this } private fun mutableCollisionRemove(key: K, value: V, mutator: PersistentHashMapBuilder): TrieNode? { - for (i in 0 until buffer.size step ENTRY_SIZE) { - if (key == keyAtIndex(i) && value == valueAtKeyIndex(i)) { - return mutableCollisionRemoveEntryAtIndex(i, mutator) - } + val keyIndex = collisionKeyIndex(key) + if (keyIndex != -1 && value == valueAtKeyIndex(keyIndex)) { + return mutableCollisionRemoveEntryAtIndex(keyIndex, mutator) } return this } From 2d579a890dc34b301bfc5a8229a6572ecc857ed5 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 14 May 2021 03:55:17 +0300 Subject: [PATCH 075/162] Extract map containsEntry workaround to a separate function Co-authored-by: Mikhail Belyaev --- .../PersistentHashMapBuilderContentViews.kt | 5 +++-- .../immutableMap/PersistentHashMapContentViews.kt | 6 ++---- .../PersistentOrderedMapBuilderContentViews.kt | 4 ++-- .../PersistentOrderedMapContentViews.kt | 6 ++---- core/commonMain/src/internal/MapImplementation.kt | 15 +++++++++++++++ 5 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 core/commonMain/src/internal/MapImplementation.kt diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt index 07e4f1f0..d4dd6e4d 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt @@ -5,6 +5,8 @@ package kotlinx.collections.immutable.implementations.immutableMap +import kotlinx.collections.immutable.internal.MapImplementation + // intermediate abstract class to workaround KT-43321 internal abstract class AbstractMapBuilderEntries, K, V> : AbstractMutableSet() { final override fun contains(element: E): Boolean { @@ -44,8 +46,7 @@ internal class PersistentHashMapBuilderEntries(private val builder: Persis get() = builder.size override fun containsEntry(element: Map.Entry): Boolean { - return builder[element.key]?.let { candidate -> candidate == element.value } - ?: (element.value == null && builder.containsKey(element.key)) + return MapImplementation.containsEntry(builder, element) } } diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentViews.kt index a3b4671f..bf5f3be6 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapContentViews.kt @@ -7,15 +7,13 @@ package kotlinx.collections.immutable.implementations.immutableMap import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.internal.MapImplementation internal class PersistentHashMapEntries(private val map: PersistentHashMap) : ImmutableSet>, AbstractSet>() { override val size: Int get() = map.size override fun contains(element: Map.Entry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false - return map[element.key]?.let { candidate -> candidate == element.value } - ?: (element.value == null && map.containsKey(element.key)) + return MapImplementation.containsEntry(map, element) } override fun iterator(): Iterator> { diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt index 9f8e6bc0..2763943a 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilderContentViews.kt @@ -6,6 +6,7 @@ package kotlinx.collections.immutable.implementations.persistentOrderedMap import kotlinx.collections.immutable.implementations.immutableMap.AbstractMapBuilderEntries +import kotlinx.collections.immutable.internal.MapImplementation internal class PersistentOrderedMapBuilderEntries(private val builder: PersistentOrderedMapBuilder) : AbstractMapBuilderEntries, K, V>() { @@ -29,8 +30,7 @@ internal class PersistentOrderedMapBuilderEntries(private val builder: Per get() = builder.size override fun containsEntry(element: Map.Entry): Boolean { - return builder[element.key]?.let { candidate -> candidate == element.value } - ?: (element.value == null && builder.containsKey(element.key)) + return MapImplementation.containsEntry(builder, element) } } diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentViews.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentViews.kt index cd61d3d6..358ad15c 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentViews.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapContentViews.kt @@ -7,15 +7,13 @@ package kotlinx.collections.immutable.implementations.persistentOrderedMap import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.internal.MapImplementation internal class PersistentOrderedMapEntries(private val map: PersistentOrderedMap) : ImmutableSet>, AbstractSet>() { override val size: Int get() = map.size override fun contains(element: Map.Entry): Boolean { - // TODO: Eliminate this check after KT-30016 gets fixed. - if ((element as Any?) !is Map.Entry<*, *>) return false - return map[element.key]?.let { candidate -> candidate == element.value } - ?: (element.value == null && map.containsKey(element.key)) + return MapImplementation.containsEntry(map, element) } override fun iterator(): Iterator> { diff --git a/core/commonMain/src/internal/MapImplementation.kt b/core/commonMain/src/internal/MapImplementation.kt new file mode 100644 index 00000000..a6bd491f --- /dev/null +++ b/core/commonMain/src/internal/MapImplementation.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.collections.immutable.internal + +internal object MapImplementation { + internal fun containsEntry(map: Map, element: Map.Entry): Boolean { + @Suppress("USELESS_CAST") + if ((element as Any?) !is Map.Entry<*, *>) return false + return map[element.key]?.let { candidate -> candidate == element.value } + ?: (element.value == null && map.containsKey(element.key)) + } +} From ec42c9648fee141d23a040cf596e7d653930a036 Mon Sep 17 00:00:00 2001 From: Mikhail Belyaev Date: Fri, 14 May 2021 03:58:22 +0300 Subject: [PATCH 076/162] Optimize equals for maps and sets Co-authored-by: Abduqodiri Qurbonzoda --- .../src/benchmarks/immutableMap/Equals.kt | 43 +++++++++++++++++ .../benchmarks/immutableMap/builder/Equals.kt | 46 ++++++++++++++++++ .../src/benchmarks/immutableSet/Equals.kt | 43 +++++++++++++++++ .../benchmarks/immutableSet/builder/Equals.kt | 45 ++++++++++++++++++ .../immutableMap/PersistentHashMap.kt | 34 ++++++++++++++ .../immutableMap/PersistentHashMapBuilder.kt | 37 +++++++++++++++ .../implementations/immutableMap/TrieNode.kt | 27 +++++++++++ .../PersistentOrderedMap.kt | 37 +++++++++++++++ .../PersistentOrderedMapBuilder.kt | 41 ++++++++++++++++ .../PersistentOrderedSet.kt | 22 +++++++++ .../PersistentOrderedSetBuilder.kt | 22 +++++++++ .../src/internal/MapImplementation.kt | 11 +++++ .../src/contract/map/ImmutableMapTest.kt | 47 ++++++++++++++++++- .../src/contract/set/ImmutableSetTest.kt | 43 ++++++++++++++++- 14 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt create mode 100644 benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt create mode 100644 benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt create mode 100644 benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt diff --git a/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt new file mode 100644 index 00000000..d2d874bd --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableMap + +import benchmarks.* +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentMapOf + +@State(Scope.Benchmark) +open class Equals { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) + var size: Int = 0 + + @Param(HASH_IMPL, ORDERED_IMPL) + var implementation = "" + + @Param(ASCENDING_HASH_CODE, RANDOM_HASH_CODE, COLLISION_HASH_CODE, NON_EXISTING_HASH_CODE) + var hashCodeType = "" + + private var persistentMap = persistentMapOf() + private var sameMap = persistentMapOf() + private var slightlyDifferentMap = persistentMapOf() + private var veryDifferentMap = persistentMapOf() + + @Setup + fun prepare() { + val keys = generateKeys(hashCodeType, size * 2) + persistentMap = persistentMapPut(implementation, keys.take(size)) + sameMap = persistentMapPut(implementation, keys.take(size)) + slightlyDifferentMap = sameMap.put(keys[size + 1], "different value").remove(keys[0]) + veryDifferentMap = persistentMapPut(implementation, keys.drop(size)) + } + + @Benchmark + fun equalsTrue() = persistentMap == sameMap + @Benchmark + fun nearlyEquals() = persistentMap == slightlyDifferentMap + @Benchmark + fun notEquals() = persistentMap == veryDifferentMap +} \ No newline at end of file diff --git a/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt new file mode 100644 index 00000000..22156057 --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableMap.builder + +import benchmarks.* +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentMapOf + +@State(Scope.Benchmark) +open class Equals { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) + var size: Int = 0 + + @Param(HASH_IMPL, ORDERED_IMPL) + var implementation = "" + + @Param(ASCENDING_HASH_CODE, RANDOM_HASH_CODE, COLLISION_HASH_CODE, NON_EXISTING_HASH_CODE) + var hashCodeType = "" + + private var persistentMap = persistentMapOf().builder() + private var sameMap = persistentMapOf().builder() + private var slightlyDifferentMap = persistentMapOf().builder() + private var veryDifferentMap = persistentMapOf().builder() + + @Setup + fun prepare() { + val keys = generateKeys(hashCodeType, size * 2) + persistentMap = persistentMapBuilderPut(implementation, keys.take(size), 0.0) + sameMap = persistentMapBuilderPut(implementation, keys.take(size), 0.0) + slightlyDifferentMap = sameMap.build().builder() + slightlyDifferentMap.put(keys[size + 1], "different value") + slightlyDifferentMap.remove(keys[0]) + veryDifferentMap = persistentMapBuilderPut(implementation, keys.drop(size), 0.0) + } + + @Benchmark + fun equalsTrue() = persistentMap == sameMap + @Benchmark + fun nearlyEquals() = persistentMap == slightlyDifferentMap + @Benchmark + fun notEquals() = persistentMap == veryDifferentMap + +} \ No newline at end of file diff --git a/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt new file mode 100644 index 00000000..7beca385 --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableSet + +import benchmarks.* +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentSetOf + +@State(Scope.Benchmark) +open class Equals { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) + var size: Int = 0 + + @Param(HASH_IMPL, ORDERED_IMPL) + var implementation = "" + + @Param(ASCENDING_HASH_CODE, RANDOM_HASH_CODE, COLLISION_HASH_CODE, NON_EXISTING_HASH_CODE) + var hashCodeType = "" + + private var persistentSet = persistentSetOf() + private var sameSet = persistentSetOf() + private var slightlyDifferentSet = persistentSetOf() + private var veryDifferentSet = persistentSetOf() + + @Setup + fun prepare() { + val keys = generateKeys(hashCodeType, size * 2) + persistentSet = persistentSetAdd(implementation, keys.take(size)) + sameSet = persistentSetAdd(implementation, keys.take(size)) + slightlyDifferentSet = sameSet.add(keys[size + 1]).remove(keys[0]) + veryDifferentSet = persistentSetAdd(implementation, keys.drop(size)) + } + + @Benchmark + fun equalsTrue() = persistentSet == sameSet + @Benchmark + fun nearlyEquals() = persistentSet == slightlyDifferentSet + @Benchmark + fun notEquals() = persistentSet == veryDifferentSet +} \ No newline at end of file diff --git a/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt new file mode 100644 index 00000000..8304b4cd --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableSet.builder + +import benchmarks.* +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentSetOf + +@State(Scope.Benchmark) +open class Equals { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) + var size: Int = 0 + + @Param(HASH_IMPL, ORDERED_IMPL) + var implementation = "" + + @Param(ASCENDING_HASH_CODE, RANDOM_HASH_CODE, COLLISION_HASH_CODE, NON_EXISTING_HASH_CODE) + var hashCodeType = "" + + private var persistentSet = persistentSetOf().builder() + private var sameSet = persistentSetOf().builder() + private var slightlyDifferentSet = persistentSetOf().builder() + private var veryDifferentSet = persistentSetOf().builder() + + @Setup + fun prepare() { + val keys = generateKeys(hashCodeType, size * 2) + persistentSet = persistentSetBuilderAdd(implementation, keys.take(size), 0.0) + sameSet = persistentSetBuilderAdd(implementation, keys.take(size), 0.0) + slightlyDifferentSet = sameSet.build().builder() + slightlyDifferentSet.add(keys[size + 1]) + slightlyDifferentSet.remove(keys[0]) + veryDifferentSet = persistentSetBuilderAdd(implementation, keys.drop(size), 0.0) + } + + @Benchmark + fun equalsTrue() = persistentSet == sameSet + @Benchmark + fun nearlyEquals() = persistentSet == slightlyDifferentSet + @Benchmark + fun notEquals() = persistentSet == veryDifferentSet +} \ No newline at end of file diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt index 39514f0a..332e5e40 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt @@ -8,6 +8,8 @@ package kotlinx.collections.immutable.implementations.immutableMap import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.implementations.persistentOrderedMap.PersistentOrderedMap +import kotlinx.collections.immutable.implementations.persistentOrderedMap.PersistentOrderedMapBuilder import kotlinx.collections.immutable.mutate internal class PersistentHashMap(internal val node: TrieNode, @@ -77,6 +79,38 @@ internal class PersistentHashMap(internal val node: TrieNode, return PersistentHashMapBuilder(this) } + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false + + return when (other) { + is PersistentOrderedMap<*, *> -> { + node.equalsWith(other.hashMap.node) { a, b -> + a == b.value + } + } + is PersistentOrderedMapBuilder<*, *> -> { + node.equalsWith(other.hashMapBuilder.node) { a, b -> + a == b.value + } + } + is PersistentHashMap<*, *> -> { + node.equalsWith(other.node) { a, b -> a == b } + } + is PersistentHashMapBuilder<*, *> -> { + node.equalsWith(other.node) { a, b -> a == b } + } + else -> super.equals(other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * However, the implementation from [AbstractMap] is enough. + */ + override fun hashCode(): Int = super.hashCode() + internal companion object { private val EMPTY = PersistentHashMap(TrieNode.EMPTY, 0) @Suppress("UNCHECKED_CAST") diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index 0ea24c1e..983a319f 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -6,7 +6,10 @@ package kotlinx.collections.immutable.implementations.immutableMap import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.implementations.persistentOrderedMap.PersistentOrderedMap +import kotlinx.collections.immutable.implementations.persistentOrderedMap.PersistentOrderedMapBuilder import kotlinx.collections.immutable.internal.DeltaCounter +import kotlinx.collections.immutable.internal.MapImplementation import kotlinx.collections.immutable.internal.MutabilityOwnership internal class PersistentHashMapBuilder(private var map: PersistentHashMap) : PersistentMap.Builder, AbstractMutableMap() { @@ -93,4 +96,38 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap node = TrieNode.EMPTY as TrieNode size = 0 } + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false + + return when (other) { + is PersistentHashMap<*, *> -> { + node.equalsWith(other.node) { a, b -> a == b } + } + is PersistentHashMapBuilder<*, *> -> { + node.equalsWith(other.node) { a, b -> a == b } + } + is PersistentOrderedMap<*, *> -> { + node.equalsWith(other.hashMap.node) { a, b -> + a == b.value + } + } + is PersistentOrderedMapBuilder<*, *> -> { + node.equalsWith(other.hashMapBuilder.node) { a, b -> + a == b.value + } + } + // should be super.equals(other), but https://youtrack.jetbrains.com/issue/KT-45673 + else -> MapImplementation.equals(this, other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * + * Should be super.hashCode(), but https://youtrack.jetbrains.com/issue/KT-45673 + */ + override fun hashCode(): Int = MapImplementation.hashCode(this) } \ No newline at end of file diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index ddabde41..9ad55038 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -844,6 +844,33 @@ internal class TrieNode( return this } + internal fun equalsWith(that: TrieNode, equalityComparator: (V, V1) -> Boolean): Boolean { + if (this === that) return true + if (dataMap != that.dataMap || nodeMap != that.nodeMap) return false + if (dataMap == 0 && nodeMap == 0) { // collision node + if (buffer.size != that.buffer.size) return false + return (0 until buffer.size step ENTRY_SIZE).all { i -> + val thatKey = that.keyAtIndex(i) + val thatValue = that.valueAtKeyIndex(i) + val keyIndex = collisionKeyIndex(thatKey) + if (keyIndex != -1) { + val value = valueAtKeyIndex(keyIndex) + equalityComparator(value, thatValue) + } else false + } + } + + val valueSize = dataMap.countOneBits() * ENTRY_SIZE + for (i in 0 until valueSize step ENTRY_SIZE) { + if (keyAtIndex(i) != that.keyAtIndex(i)) return false + if (!equalityComparator(valueAtKeyIndex(i), that.valueAtKeyIndex(i))) return false + } + for (i in valueSize until buffer.size) { + if (!nodeAtIndex(i).equalsWith(that.nodeAtIndex(i), equalityComparator)) return false + } + return true + } + // For testing trie structure internal fun accept(visitor: (node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int) -> Unit) { accept(visitor, 0, 0) diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt index 482ada9f..94d25929 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt @@ -9,6 +9,7 @@ import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap +import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMapBuilder import kotlinx.collections.immutable.internal.EndOfChain import kotlinx.collections.immutable.mutate @@ -127,6 +128,42 @@ internal class PersistentOrderedMap( return PersistentOrderedMapBuilder(this) } + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false + + return when (other) { + is PersistentOrderedMap<*, *> -> { + hashMap.node.equalsWith(other.hashMap.node) { a, b -> + a.value == b.value + } + } + is PersistentOrderedMapBuilder<*, *> -> { + hashMap.node.equalsWith(other.hashMapBuilder.node) { a, b -> + a.value == b.value + } + } + is PersistentHashMap<*, *> -> { + hashMap.node.equalsWith(other.node) { a, b -> + a.value == b + } + } + is PersistentHashMapBuilder<*, *> -> { + hashMap.node.equalsWith(other.node) { a, b -> + a.value == b + } + } + else -> super.equals(other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * However, the implementation from [AbstractMap] is enough. + */ + override fun hashCode(): Int = super.hashCode() + internal companion object { private val EMPTY = PersistentOrderedMap(EndOfChain, EndOfChain, PersistentHashMap.emptyOf()) @Suppress("UNCHECKED_CAST") diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt index 424c7a36..b39fb9b4 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt @@ -6,7 +6,10 @@ package kotlinx.collections.immutable.implementations.persistentOrderedMap import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap +import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMapBuilder import kotlinx.collections.immutable.internal.EndOfChain +import kotlinx.collections.immutable.internal.MapImplementation import kotlinx.collections.immutable.internal.assert internal class PersistentOrderedMapBuilder(private var map: PersistentOrderedMap) : AbstractMutableMap(), PersistentMap.Builder { @@ -116,4 +119,42 @@ internal class PersistentOrderedMapBuilder(private var map: PersistentOrde firstKey = EndOfChain lastKey = EndOfChain } + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false + + return when (other) { + is PersistentOrderedMap<*, *> -> { + hashMapBuilder.node.equalsWith(other.hashMap.node) { a, b -> + a.value == b.value + } + } + is PersistentOrderedMapBuilder<*, *> -> { + hashMapBuilder.node.equalsWith(other.hashMapBuilder.node) { a, b -> + a.value == b.value + } + } + is PersistentHashMap<*, *> -> { + hashMapBuilder.node.equalsWith(other.node) { a, b -> + a.value == b + } + } + is PersistentHashMapBuilder<*, *> -> { + hashMapBuilder.node.equalsWith(other.node) { a, b -> + a.value == b + } + } + // should be `super.equals(other)`, but https://youtrack.jetbrains.com/issue/KT-45673 + else -> MapImplementation.equals(this, other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * + * Should be `super.hashCode()`, but https://youtrack.jetbrains.com/issue/KT-45673 + */ + override fun hashCode(): Int = MapImplementation.hashCode(this) } \ No newline at end of file diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt index 656af288..a1f5dc1a 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt @@ -101,6 +101,28 @@ internal class PersistentOrderedSet( return PersistentOrderedSetBuilder(this) } + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Set<*>) return false + if (size != other.size) return false + + return when (other) { + is PersistentOrderedSet<*> -> { + hashMap.node.equalsWith(other.hashMap.node) { _, _ -> true } + } + is PersistentOrderedSetBuilder<*> -> { + hashMap.node.equalsWith(other.hashMapBuilder.node) { _, _ -> true } + } + else -> super.equals(other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * However, the implementation from [AbstractSet] is enough. + */ + override fun hashCode(): Int = super.hashCode() + internal companion object { private val EMPTY = PersistentOrderedSet(EndOfChain, EndOfChain, PersistentHashMap.emptyOf()) internal fun emptyOf(): PersistentSet = EMPTY diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt index b3eb4bbe..352a9576 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt @@ -86,4 +86,26 @@ internal class PersistentOrderedSetBuilder(private var set: PersistentOrdered override fun iterator(): MutableIterator { return PersistentOrderedSetMutableIterator(this) } + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Set<*>) return false + if (size != other.size) return false + + return when (other) { + is PersistentOrderedSet<*> -> { + hashMapBuilder.node.equalsWith(other.hashMap.node) { _, _ -> true } + } + is PersistentOrderedSetBuilder<*> -> { + hashMapBuilder.node.equalsWith(other.hashMapBuilder.node) { _, _ -> true } + } + else -> super.equals(other) + } + } + + /** + * We provide [equals], so as a matter of style, we should also provide [hashCode]. + * However, the implementation from [AbstractMutableSet] is enough. + */ + override fun hashCode(): Int = super.hashCode() } \ No newline at end of file diff --git a/core/commonMain/src/internal/MapImplementation.kt b/core/commonMain/src/internal/MapImplementation.kt index a6bd491f..32b5ba70 100644 --- a/core/commonMain/src/internal/MapImplementation.kt +++ b/core/commonMain/src/internal/MapImplementation.kt @@ -5,6 +5,9 @@ package kotlinx.collections.immutable.internal +/** + * This should not be needed after KT-30016 and KT-45673 are fixed + */ internal object MapImplementation { internal fun containsEntry(map: Map, element: Map.Entry): Boolean { @Suppress("USELESS_CAST") @@ -12,4 +15,12 @@ internal object MapImplementation { return map[element.key]?.let { candidate -> candidate == element.value } ?: (element.value == null && map.containsKey(element.key)) } + + internal fun equals(thisMap: Map, otherMap: Map<*, *>): Boolean { + require(thisMap.size == otherMap.size) + @Suppress("UNCHECKED_CAST") + return (otherMap as Map).all { entry -> containsEntry(thisMap, entry) } + } + + internal fun hashCode(map: Map): Int = map.entries.hashCode() } diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 42783ce2..73253079 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -57,11 +57,11 @@ class ImmutableHashMapTest : ImmutableMapTest() { val builder1 = immutableMapOf().builder() val builder2 = immutableMapOf().builder() val expected = mutableMapOf() - for(i in 300..400) { + for (i in 300..400) { builder1.put("$i", i) expected.put("$i", i) } - for(i in 0..200) { + for (i in 0..200) { builder2.put("$i", i) expected.put("$i", i) } @@ -76,6 +76,7 @@ class ImmutableHashMapTest : ImmutableMapTest() { compareMaps(expected, builder1.build()) } } + class ImmutableOrderedMapTest : ImmutableMapTest() { override fun immutableMapOf(vararg pairs: Pair): PersistentMap = persistentMapOf(*pairs) override fun compareMaps(expected: Map, actual: Map) = compare(expected, actual) { mapBehavior(ordered = true) } @@ -290,4 +291,46 @@ abstract class ImmutableMapTest { assertEquals>(mapOf(1 to "x", "x" to 1, "y" to null), mapANA) } + + private fun testSpecializedEquality(map: Map, pairs: Array>, isEqual: Boolean) { + fun testEqualsAndHashCode(lhs: Any, rhs: Any) { + assertEquals(isEqual, lhs == rhs) + if (isEqual) assertEquals(lhs.hashCode(), rhs.hashCode()) + } + + testEqualsAndHashCode(map, mapOf(*pairs)) + testEqualsAndHashCode(map, persistentHashMapOf(*pairs)) + testEqualsAndHashCode(map, persistentMapOf(*pairs)) + testEqualsAndHashCode(map, persistentHashMapOf().builder().apply { putAll(pairs) }) + testEqualsAndHashCode(map, persistentMapOf().builder().apply { putAll(pairs) }) + } + + private fun testEquality(data: Array>, changed: Array>) { + val base = immutableMapOf(*data) + testSpecializedEquality(base, data, isEqual = true) + testSpecializedEquality(base, changed, isEqual = false) + + val builder = immutableMapOf().builder().apply { putAll(data) } + testSpecializedEquality(builder, data, isEqual = true) + testSpecializedEquality(builder, changed, isEqual = false) + + testSpecializedEquality(base, data.copyOf().apply { shuffle() }, isEqual = true) + testSpecializedEquality(builder, data.copyOf().apply { shuffle() }, isEqual = true) + } + + @Test + fun equality() { + val data = (0..200).map { i -> i to "$i" }.toTypedArray() + val changed = data.copyOf().apply { this[42] = 42 to "Invalid" } + + testEquality(data, changed) + } + + @Test + fun collisionEquality() { + val data = (0..200).map { i -> IntWrapper(i, i % 50) to "$i" }.toTypedArray() + val changed = data.copyOf().apply { this[42] = IntWrapper(42, 42) to "Invalid" } + + testEquality(data, changed) + } } \ No newline at end of file diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index b8cd2497..5f1e422d 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -195,8 +195,8 @@ class ImmutableHashSetTest : ImmutableSetTestBase() { compareSets(immutableSetOf(), left - right) } } - } + class ImmutableOrderedSetTest : ImmutableSetTestBase() { override fun immutableSetOf(vararg elements: T) = persistentSetOf(*elements) override fun compareSets(expected: Set, actual: Set) = compare(expected, actual) { setBehavior(ordered = true) } @@ -354,4 +354,45 @@ abstract class ImmutableSetTestBase { assertTrue(this === buildResult) } + private fun testSpecializedEquality(set: Set, elements: Array, isEqual: Boolean) { + fun testEqualsAndHashCode(lhs: Any, rhs: Any) { + assertEquals(isEqual, lhs == rhs) + if (isEqual) assertEquals(lhs.hashCode(), rhs.hashCode()) + } + + testEqualsAndHashCode(set, setOf(*elements)) + testEqualsAndHashCode(set, persistentHashSetOf(*elements)) + testEqualsAndHashCode(set, persistentSetOf(*elements)) + testEqualsAndHashCode(set, persistentHashSetOf().builder().apply { addAll(elements) }) + testEqualsAndHashCode(set, persistentSetOf().builder().apply { addAll(elements) }) + } + + private fun testEquality(data: Array, changed: Array) { + val base = immutableSetOf(*data) + testSpecializedEquality(base, data, isEqual = true) + testSpecializedEquality(base, changed, isEqual = false) + + val builder = immutableSetOf().builder().apply { addAll(data) } + testSpecializedEquality(builder, data, isEqual = true) + testSpecializedEquality(builder, changed, isEqual = false) + + testSpecializedEquality(base, data.copyOf().apply { shuffle() }, isEqual = true) + testSpecializedEquality(builder, data.copyOf().apply { shuffle() }, isEqual = true) + } + + @Test + fun equality() { + val data = (0..200).toList().toTypedArray() + val changed = data.copyOf().apply { this[42] = 4242 } + + testEquality(data, changed) + } + + @Test + fun collisionEquality() { + val data = (0..200).map { i -> IntWrapper(i, i % 50) }.toTypedArray() + val changed = data.copyOf().apply { this[42] = IntWrapper(4242, 42) } + + testEquality(data, changed) + } } \ No newline at end of file From 363df272102490d7bd5ad889368a7147728aacce Mon Sep 17 00:00:00 2001 From: Mikhail Belyaev Date: Wed, 19 May 2021 00:59:18 +0000 Subject: [PATCH 077/162] Don't lift not newly created single-entry node up in map builder #109 (#111) Because builder `TrieNode` is not canonicalized it is possible to have `newNode` with a single entry that is not owned by the builder, thus modifying the `newNode` (by lifting it up) is not allowed. --- .../src/implementations/immutableMap/TrieNode.kt | 6 +++--- core/commonTest/src/contract/map/ImmutableMapTest.kt | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 9ad55038..8b257850 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -202,6 +202,7 @@ internal class TrieNode( /** The given [newNode] must not be a part of any persistent map instance. */ private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { + assert(newNode.ownedBy === owner) // assert(buffer[nodeIndex] !== newNode) // nodes (including collision nodes) that have only one entry are upped if they have no siblings @@ -784,10 +785,9 @@ internal class TrieNode( private fun mutableReplaceNode(targetNode: TrieNode, newNode: TrieNode?, nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership) = when { newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, positionMask, owner) - ownedBy === owner || targetNode !== newNode -> + targetNode !== newNode -> mutableUpdateNodeAtIndex(nodeIndex, newNode, owner) - else -> - this + else -> this } fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode? { diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 73253079..1f1e90a4 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -75,6 +75,18 @@ class ImmutableHashMapTest : ImmutableMapTest() { // make sure builder builds correct map compareMaps(expected, builder1.build()) } + + @Test fun regressionGithubIssue109() { + // https://github.com/Kotlin/kotlinx.collections.immutable/issues/109 + val map0 = immutableMapOf().put(0, 0).put(1, 1).put(32, 32) + val map1 = map0.mutate { it.remove(0) } + val map2 = map1.mutate { + it.remove(1) + it.remove(0) + } + + assertTrue(map1.containsKey(32)) + } } class ImmutableOrderedMapTest : ImmutableMapTest() { From 31597ce7db5db322a175a63e105f4842ddc29065 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 07:19:57 +0300 Subject: [PATCH 078/162] Check targetNode.ownerBy in mutableRemove to know if newNode can have a single element --- .../src/implementations/immutableSet/TrieNode.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index f8f251c9..10ec4f39 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -139,8 +139,6 @@ internal class TrieNode( /** The given [newNode] must not be a part of any persistent set instance. */ private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { -// assert(buffer[nodeIndex] !== newNode) - val cell: Any? val newNodeBuffer = newNode.buffer @@ -832,7 +830,11 @@ internal class TrieNode( } else { targetNode.mutableRemove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } - if (ownedBy === mutator.ownership || targetNode !== newNode) { + // If newNode is a single-element node, it is newly created, or targetNode is owned by mutator and a cell was removed in-place. + // Otherwise the single element would have been lifted up. + // If targetNode is owned by mutator, this node is also owned by mutator. Thus no new node will be created to replace this node. + // If newNode !== targetNode, it is newly created. + if (targetNode.ownedBy === mutator.ownership || targetNode !== newNode) { return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.ownership) } return this From 37b54f20f11c75eae46f524633333c7a2d9639a4 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 07:20:43 +0300 Subject: [PATCH 079/162] Move HashMapTrieNodeTest.kt to tests.implementations.map package --- .../src/implementations/{list => map}/HashMapTrieNodeTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename core/commonTest/src/implementations/{list => map}/HashMapTrieNodeTest.kt (99%) diff --git a/core/commonTest/src/implementations/list/HashMapTrieNodeTest.kt b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt similarity index 99% rename from core/commonTest/src/implementations/list/HashMapTrieNodeTest.kt rename to core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt index 1b2352d1..6e8407ec 100644 --- a/core/commonTest/src/implementations/list/HashMapTrieNodeTest.kt +++ b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package tests.implementations.list +package tests.implementations.map import kotlinx.collections.immutable.implementations.immutableMap.LOG_MAX_BRANCHING_FACTOR import kotlinx.collections.immutable.implementations.immutableMap.MAX_SHIFT From 84e69d1a00ccd35b63980e96c0e667313256d7c5 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 14:45:03 +0300 Subject: [PATCH 080/162] Extract setCellAtIndex function that take nullable MutabilityOwnership Mutable functions can invoke it passing mutator ownership. Immutable functions invoke it passing null ownership. --- .../src/implementations/immutableSet/TrieNode.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 10ec4f39..1d2dc4e3 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -132,9 +132,7 @@ internal class TrieNode( cell = newNode } - val newBuffer = buffer.copyOf() - newBuffer[nodeIndex] = cell - return TrieNode(bitmap, newBuffer) + return setCellAtIndex(nodeIndex, cell, owner = null) } /** The given [newNode] must not be a part of any persistent set instance. */ @@ -152,12 +150,16 @@ internal class TrieNode( cell = newNode } - if (ownedBy === owner) { - buffer[nodeIndex] = cell + return setCellAtIndex(nodeIndex, cell, owner) + } + + private fun setCellAtIndex(cellIndex: Int, newCell: Any?, owner: MutabilityOwnership?): TrieNode { + if (ownedBy != null && ownedBy === owner) { + buffer[cellIndex] = newCell return this } val newBuffer = buffer.copyOf() - newBuffer[nodeIndex] = cell + newBuffer[cellIndex] = newCell return TrieNode(bitmap, newBuffer, owner) } From 6b9cdc0da3576b42070d8b85ca0ce21827dcc34d Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 08:31:06 +0300 Subject: [PATCH 081/162] Use setCellAtIndex in add and mutableAdd functions Because adding new element won't cause canonicalization. --- core/commonMain/src/implementations/immutableSet/TrieNode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 1d2dc4e3..3458aa04 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -760,7 +760,7 @@ internal class TrieNode( targetNode.add(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) } if (targetNode === newNode) return this - return updateNodeAtIndex(cellIndex, newNode) + return setCellAtIndex(cellIndex, newNode, owner = null) } // element is directly in buffer if (element == buffer[cellIndex]) return this @@ -784,7 +784,7 @@ internal class TrieNode( targetNode.mutableAdd(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } if (targetNode === newNode) return this - return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.ownership) + return setCellAtIndex(cellIndex, newNode, mutator.ownership) } // element is directly in buffer if (element == buffer[cellIndex]) return this From c42841f32a350f9c444902c7508dd21db945fae2 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 08:36:42 +0300 Subject: [PATCH 082/162] Introduce canonicalizeNodeAtIndex function that takes nullable MutabilityOwnership It replaces updateNodeAtIndex and mutableUpdateNodeAtIndex --- .../implementations/immutableSet/TrieNode.kt | 28 +++---------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 3458aa04..2a3d86f4 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -117,7 +117,7 @@ internal class TrieNode( } /** The given [newNode] must not be a part of any persistent set instance. */ - private fun updateNodeAtIndex(nodeIndex: Int, newNode: TrieNode): TrieNode { + private fun canonicalizeNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership?): TrieNode { // assert(buffer[nodeIndex] !== newNode) val cell: Any? @@ -132,24 +132,6 @@ internal class TrieNode( cell = newNode } - return setCellAtIndex(nodeIndex, cell, owner = null) - } - - /** The given [newNode] must not be a part of any persistent set instance. */ - private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { - val cell: Any? - - val newNodeBuffer = newNode.buffer - if (newNodeBuffer.size == 1 && newNodeBuffer[0] !is TrieNode<*>) { - if (buffer.size == 1) { - newNode.bitmap = bitmap - return newNode - } - cell = newNodeBuffer[0] - } else { - cell = newNode - } - return setCellAtIndex(nodeIndex, cell, owner) } @@ -808,7 +790,7 @@ internal class TrieNode( targetNode.remove(elementHash, element, shift + LOG_MAX_BRANCHING_FACTOR) } if (targetNode === newNode) return this - return updateNodeAtIndex(cellIndex, newNode) + return canonicalizeNodeAtIndex(cellIndex, newNode, owner = null) } // element is directly in buffer if (element == buffer[cellIndex]) { @@ -836,10 +818,8 @@ internal class TrieNode( // Otherwise the single element would have been lifted up. // If targetNode is owned by mutator, this node is also owned by mutator. Thus no new node will be created to replace this node. // If newNode !== targetNode, it is newly created. - if (targetNode.ownedBy === mutator.ownership || targetNode !== newNode) { - return mutableUpdateNodeAtIndex(cellIndex, newNode, mutator.ownership) - } - return this + if (targetNode.ownedBy !== mutator.ownership && targetNode === newNode) return this + return canonicalizeNodeAtIndex(cellIndex, newNode, mutator.ownership) } // element is directly in buffer if (element == buffer[cellIndex]) { From 5ffa4764d2cb1df161a2afde26b28f5d06c0bf9b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 08:38:54 +0300 Subject: [PATCH 083/162] Introduce moveElementToNode function that takes nullable MutabilityOwnership It replaces moveElementToNode and mutableMoveElementToNode --- .../implementations/immutableSet/TrieNode.kt | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 2a3d86f4..38ca3d7b 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -153,21 +153,9 @@ internal class TrieNode( } private fun moveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E, - shift: Int): TrieNode { - val newBuffer = buffer.copyOf() - newBuffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, null) - return TrieNode(bitmap, newBuffer) - } - - private fun mutableMoveElementToNode(elementIndex: Int, newElementHash: Int, newElement: E, - shift: Int, owner: MutabilityOwnership): TrieNode { - if (ownedBy === owner) { - buffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, owner) - return this - } - val newBuffer = buffer.copyOf() - newBuffer[elementIndex] = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, owner) - return TrieNode(bitmap, newBuffer, owner) + shift: Int, owner: MutabilityOwnership?): TrieNode { + val node = makeNodeAtIndex(elementIndex, newElementHash, newElement, shift, owner) + return setCellAtIndex(elementIndex, node, owner) } private fun makeNode(elementHash1: Int, element1: E, elementHash2: Int, element2: E, @@ -746,7 +734,7 @@ internal class TrieNode( } // element is directly in buffer if (element == buffer[cellIndex]) return this - return moveElementToNode(cellIndex, elementHash, element, shift) + return moveElementToNode(cellIndex, elementHash, element, shift, owner = null) } fun mutableAdd(elementHash: Int, element: E, shift: Int, mutator: PersistentHashSetBuilder<*>): TrieNode { @@ -771,7 +759,7 @@ internal class TrieNode( // element is directly in buffer if (element == buffer[cellIndex]) return this mutator.size++ - return mutableMoveElementToNode(cellIndex, elementHash, element, shift, mutator.ownership) + return moveElementToNode(cellIndex, elementHash, element, shift, mutator.ownership) } fun remove(elementHash: Int, element: E, shift: Int): TrieNode { From dd791a11d139df55344c5c199430b3fce8d46d7a Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 09:20:23 +0300 Subject: [PATCH 084/162] Introduce setProperties function that takes nullable MutabilityOwnership It sets bitmap and buffer properties of this node if ownedBy === owner. Otherwise, it returns a new TrieNode with the specified properties and owner. --- .../implementations/immutableSet/TrieNode.kt | 64 ++++++++----------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 38ca3d7b..a8010c8e 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -99,21 +99,27 @@ internal class TrieNode( // assert(hasNoCellAt(positionMask)) val index = indexOfCellAt(positionMask) + val newBitmap = bitmap or positionMask val newBuffer = buffer.addElementAtIndex(index, element) - return TrieNode(bitmap or positionMask, newBuffer) + return setProperties(newBitmap, newBuffer, owner = null) } private fun mutableAddElementAt(positionMask: Int, element: E, owner: MutabilityOwnership): TrieNode { // assert(hasNoCellAt(positionMask)) val index = indexOfCellAt(positionMask) - if (ownedBy === owner) { - buffer = buffer.addElementAtIndex(index, element) - bitmap = bitmap or positionMask + val newBitmap = bitmap or positionMask + val newBuffer = buffer.addElementAtIndex(index, element) + return setProperties(newBitmap, newBuffer, owner) + } + + private fun setProperties(newBitmap: Int, newBuffer: Array, owner: MutabilityOwnership?): TrieNode { + if (ownedBy != null && ownedBy === owner) { + bitmap = newBitmap + buffer = newBuffer return this } - val newBuffer = buffer.addElementAtIndex(index, element) - return TrieNode(bitmap or positionMask, newBuffer, owner) + return TrieNode(newBitmap, newBuffer, owner) } /** The given [newNode] must not be a part of any persistent set instance. */ @@ -187,35 +193,28 @@ internal class TrieNode( // assert(!hasNoCellAt(positionMask)) // assert(buffer.size > 1) can be false only for the root node + val newBitmap = bitmap xor positionMask val newBuffer = buffer.removeCellAtIndex(cellIndex) - return TrieNode(bitmap xor positionMask, newBuffer) + return setProperties(newBitmap, newBuffer, owner = null) } private fun mutableRemoveCellAtIndex(cellIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode { // assert(!hasNoCellAt(positionMask)) // assert(buffer.size > 1) - if (ownedBy === owner) { - buffer = buffer.removeCellAtIndex(cellIndex) - bitmap = bitmap xor positionMask - return this - } + val newBitmap = bitmap xor positionMask val newBuffer = buffer.removeCellAtIndex(cellIndex) - return TrieNode(bitmap xor positionMask, newBuffer, owner) + return setProperties(newBitmap, newBuffer, owner) } private fun collisionRemoveElementAtIndex(i: Int): TrieNode { val newBuffer = buffer.removeCellAtIndex(i) - return TrieNode(0, newBuffer) + return setProperties(newBitmap = 0, newBuffer, owner = null) } private fun mutableCollisionRemoveElementAtIndex(i: Int, owner: MutabilityOwnership): TrieNode { - if (ownedBy === owner) { - buffer = buffer.removeCellAtIndex(i) - return this - } val newBuffer = buffer.removeCellAtIndex(i) - return TrieNode(0, newBuffer, owner) + return setProperties(newBitmap = 0, newBuffer, owner) } private fun collisionContainsElement(element: E): Boolean { @@ -225,18 +224,14 @@ internal class TrieNode( private fun collisionAdd(element: E): TrieNode { if (collisionContainsElement(element)) return this val newBuffer = buffer.addElementAtIndex(0, element) - return TrieNode(0, newBuffer) + return setProperties(newBitmap = 0, newBuffer, owner = null) } private fun mutableCollisionAdd(element: E, mutator: PersistentHashSetBuilder<*>): TrieNode { if (collisionContainsElement(element)) return this mutator.size++ - if (ownedBy === mutator.ownership) { - buffer = buffer.addElementAtIndex(0, element) - return this - } val newBuffer = buffer.addElementAtIndex(0, element) - return TrieNode(0, newBuffer, mutator.ownership) + return setProperties(newBitmap = 0, newBuffer, owner = mutator.ownership) } private fun collisionRemove(element: E): TrieNode { @@ -274,12 +269,7 @@ internal class TrieNode( if (totalSize == otherNode.buffer.size) return otherNode val newBuffer = if (totalSize == tempBuffer.size) tempBuffer else tempBuffer.copyOf(newSize = totalSize) - return if (ownedBy == owner) { - this.buffer = newBuffer - this - } else { - TrieNode(0, newBuffer, owner) - } + return setProperties(newBitmap = 0, newBuffer, owner) } private fun mutableCollisionRetainAll(otherNode: TrieNode, intersectionSizeRef: DeltaCounter, @@ -289,7 +279,7 @@ internal class TrieNode( return this } val tempBuffer = - if (owner == ownedBy) buffer + if (owner === ownedBy) buffer else arrayOfNulls(minOf(buffer.size, otherNode.buffer.size)) val totalWritten = buffer.filterTo(tempBuffer) { @Suppress("UNCHECKED_CAST") @@ -301,8 +291,8 @@ internal class TrieNode( 1 -> tempBuffer[0] this.buffer.size -> this otherNode.buffer.size -> otherNode - tempBuffer.size -> TrieNode(0, tempBuffer, owner) - else -> TrieNode(0, tempBuffer.copyOf(newSize = totalWritten), owner) + tempBuffer.size -> setProperties(newBitmap = 0, newBuffer = tempBuffer, owner) + else -> setProperties(newBitmap = 0, newBuffer = tempBuffer.copyOf(newSize = totalWritten), owner) } } @@ -313,7 +303,7 @@ internal class TrieNode( intersectionSizeRef += buffer.size return EMPTY } - val tempBuffer = if (owner == ownedBy) buffer else arrayOfNulls(buffer.size) + val tempBuffer = if (owner === ownedBy) buffer else arrayOfNulls(buffer.size) val totalWritten = buffer.filterTo(tempBuffer) { @Suppress("UNCHECKED_CAST") !otherNode.collisionContainsElement(it as E) @@ -323,8 +313,8 @@ internal class TrieNode( 0 -> EMPTY 1 -> tempBuffer[0] this.buffer.size -> this - tempBuffer.size -> TrieNode(0, tempBuffer, owner) - else -> TrieNode(0, tempBuffer.copyOf(newSize = totalWritten), owner) + tempBuffer.size -> setProperties(newBitmap = 0, newBuffer = tempBuffer, owner) + else -> setProperties(newBitmap = 0, newBuffer = tempBuffer.copyOf(newSize = totalWritten), owner) } } From 0483f2c71efc3c8e0e73611c4f712e8821cc3790 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 09:24:24 +0300 Subject: [PATCH 085/162] Introduce addElementAt function that takes nullable MutabilityOwnership It replaces addElementAt and mutableAddElementAt --- .../src/implementations/immutableSet/TrieNode.kt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index a8010c8e..e4e19cfc 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -95,16 +95,7 @@ internal class TrieNode( return buffer[index] as TrieNode } - private fun addElementAt(positionMask: Int, element: E): TrieNode { -// assert(hasNoCellAt(positionMask)) - - val index = indexOfCellAt(positionMask) - val newBitmap = bitmap or positionMask - val newBuffer = buffer.addElementAtIndex(index, element) - return setProperties(newBitmap, newBuffer, owner = null) - } - - private fun mutableAddElementAt(positionMask: Int, element: E, owner: MutabilityOwnership): TrieNode { + private fun addElementAt(positionMask: Int, element: E, owner: MutabilityOwnership?): TrieNode { // assert(hasNoCellAt(positionMask)) val index = indexOfCellAt(positionMask) @@ -708,7 +699,7 @@ internal class TrieNode( val cellPositionMask = 1 shl indexSegment(elementHash, shift) if (hasNoCellAt(cellPositionMask)) { // element is absent - return addElementAt(cellPositionMask, element) + return addElementAt(cellPositionMask, element, owner = null) } val cellIndex = indexOfCellAt(cellPositionMask) @@ -732,7 +723,7 @@ internal class TrieNode( if (hasNoCellAt(cellPosition)) { // element is absent mutator.size++ - return mutableAddElementAt(cellPosition, element, mutator.ownership) + return addElementAt(cellPosition, element, mutator.ownership) } val cellIndex = indexOfCellAt(cellPosition) From 56653c4a21fc24cddfb81f2474693426e38b6895 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 09:27:42 +0300 Subject: [PATCH 086/162] Introduce removeCellAtIndex function that takes nullable MutabilityOwnership It replaces removeCellAtIndex and mutableRemoveCellAtIndex --- .../src/implementations/immutableSet/TrieNode.kt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index e4e19cfc..53c2b4a1 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -180,19 +180,10 @@ internal class TrieNode( } - private fun removeCellAtIndex(cellIndex: Int, positionMask: Int): TrieNode { + private fun removeCellAtIndex(cellIndex: Int, positionMask: Int, owner: MutabilityOwnership?): TrieNode { // assert(!hasNoCellAt(positionMask)) // assert(buffer.size > 1) can be false only for the root node - val newBitmap = bitmap xor positionMask - val newBuffer = buffer.removeCellAtIndex(cellIndex) - return setProperties(newBitmap, newBuffer, owner = null) - } - - private fun mutableRemoveCellAtIndex(cellIndex: Int, positionMask: Int, owner: MutabilityOwnership): TrieNode { -// assert(!hasNoCellAt(positionMask)) -// assert(buffer.size > 1) - val newBitmap = bitmap xor positionMask val newBuffer = buffer.removeCellAtIndex(cellIndex) return setProperties(newBitmap, newBuffer, owner) @@ -763,7 +754,7 @@ internal class TrieNode( } // element is directly in buffer if (element == buffer[cellIndex]) { - return removeCellAtIndex(cellIndex, cellPositionMask) + return removeCellAtIndex(cellIndex, cellPositionMask, owner = null) } return this } @@ -793,7 +784,7 @@ internal class TrieNode( // element is directly in buffer if (element == buffer[cellIndex]) { mutator.size-- - return mutableRemoveCellAtIndex(cellIndex, cellPositionMask, mutator.ownership) // check is empty + return removeCellAtIndex(cellIndex, cellPositionMask, mutator.ownership) // check is empty } return this } From e20ec5338d2b3622b4de31e99a8f44e956bf4aa1 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 19 May 2021 09:29:13 +0300 Subject: [PATCH 087/162] Introduce collisionRemoveElementAtIndex function that takes nullable MutabilityOwnership It replaces collisionRemoveElementAtIndex and mutableCollisionRemoveElementAtIndex --- .../src/implementations/immutableSet/TrieNode.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 53c2b4a1..32ecca83 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -189,12 +189,7 @@ internal class TrieNode( return setProperties(newBitmap, newBuffer, owner) } - private fun collisionRemoveElementAtIndex(i: Int): TrieNode { - val newBuffer = buffer.removeCellAtIndex(i) - return setProperties(newBitmap = 0, newBuffer, owner = null) - } - - private fun mutableCollisionRemoveElementAtIndex(i: Int, owner: MutabilityOwnership): TrieNode { + private fun collisionRemoveElementAtIndex(i: Int, owner: MutabilityOwnership?): TrieNode { val newBuffer = buffer.removeCellAtIndex(i) return setProperties(newBitmap = 0, newBuffer, owner) } @@ -219,7 +214,7 @@ internal class TrieNode( private fun collisionRemove(element: E): TrieNode { val index = buffer.indexOf(element) if (index != -1) { - return collisionRemoveElementAtIndex(index) + return collisionRemoveElementAtIndex(index, owner = null) } return this } @@ -228,7 +223,7 @@ internal class TrieNode( val index = buffer.indexOf(element) if (index != -1) { mutator.size-- - return mutableCollisionRemoveElementAtIndex(index, mutator.ownership) + return collisionRemoveElementAtIndex(index, mutator.ownership) } return this } From 60f49315bee3e4a36019683a967f0634b8d3cee9 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 28 Jun 2021 02:25:15 +0300 Subject: [PATCH 088/162] Fix IOOBE in Equals benchmark when size parameter equals 1 --- benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt | 2 +- .../commonMain/src/benchmarks/immutableMap/builder/Equals.kt | 2 +- benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt | 2 +- .../commonMain/src/benchmarks/immutableSet/builder/Equals.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt index d2d874bd..1c3dc746 100644 --- a/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/Equals.kt @@ -30,7 +30,7 @@ open class Equals { val keys = generateKeys(hashCodeType, size * 2) persistentMap = persistentMapPut(implementation, keys.take(size)) sameMap = persistentMapPut(implementation, keys.take(size)) - slightlyDifferentMap = sameMap.put(keys[size + 1], "different value").remove(keys[0]) + slightlyDifferentMap = sameMap.put(keys[size], "different value").remove(keys[0]) veryDifferentMap = persistentMapPut(implementation, keys.drop(size)) } diff --git a/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt index 22156057..c001a15a 100644 --- a/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/builder/Equals.kt @@ -31,7 +31,7 @@ open class Equals { persistentMap = persistentMapBuilderPut(implementation, keys.take(size), 0.0) sameMap = persistentMapBuilderPut(implementation, keys.take(size), 0.0) slightlyDifferentMap = sameMap.build().builder() - slightlyDifferentMap.put(keys[size + 1], "different value") + slightlyDifferentMap.put(keys[size], "different value") slightlyDifferentMap.remove(keys[0]) veryDifferentMap = persistentMapBuilderPut(implementation, keys.drop(size), 0.0) } diff --git a/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt index 7beca385..78db8603 100644 --- a/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/Equals.kt @@ -30,7 +30,7 @@ open class Equals { val keys = generateKeys(hashCodeType, size * 2) persistentSet = persistentSetAdd(implementation, keys.take(size)) sameSet = persistentSetAdd(implementation, keys.take(size)) - slightlyDifferentSet = sameSet.add(keys[size + 1]).remove(keys[0]) + slightlyDifferentSet = sameSet.add(keys[size]).remove(keys[0]) veryDifferentSet = persistentSetAdd(implementation, keys.drop(size)) } diff --git a/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt index 8304b4cd..302b1578 100644 --- a/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt +++ b/benchmarks/commonMain/src/benchmarks/immutableSet/builder/Equals.kt @@ -31,7 +31,7 @@ open class Equals { persistentSet = persistentSetBuilderAdd(implementation, keys.take(size), 0.0) sameSet = persistentSetBuilderAdd(implementation, keys.take(size), 0.0) slightlyDifferentSet = sameSet.build().builder() - slightlyDifferentSet.add(keys[size + 1]) + slightlyDifferentSet.add(keys[size]) slightlyDifferentSet.remove(keys[0]) veryDifferentSet = persistentSetBuilderAdd(implementation, keys.drop(size), 0.0) } From 5fa1aade56eda694e43872a8fe087cb46ea81734 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 19 Jul 2021 22:16:36 +0300 Subject: [PATCH 089/162] Increase BenchmarkAll executionTimeoutMin to 1440 --- .teamcity/Benchmarks.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index 245d1031..fc206fa3 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -43,6 +43,10 @@ fun Project.benchmarkAll(buildVersion: BuildType) = BuildType { buildNumberPattern = buildVersion.depParamRefs.buildNumber.ref commonConfigure() + + failureConditions { + executionTimeoutMin = 1440 + } }.also { buildType(it) } fun Project.benchmark(target: String, platform: Platform, buildVersion: BuildType) = buildType("${target}Benchmark", platform) { From f5f852b3159c0b7358f3cd64975bcd5e76154f2b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 19 Jul 2021 20:55:44 +0300 Subject: [PATCH 090/162] Add PersistentMap.putAll benchmark --- .../src/benchmarks/immutableMap/PutAll.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 benchmarks/commonMain/src/benchmarks/immutableMap/PutAll.kt diff --git a/benchmarks/commonMain/src/benchmarks/immutableMap/PutAll.kt b/benchmarks/commonMain/src/benchmarks/immutableMap/PutAll.kt new file mode 100644 index 00000000..be432bef --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableMap/PutAll.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2016-2021 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableMap + +import benchmarks.* +import kotlinx.collections.immutable.PersistentMap +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentMapOf + +@State(Scope.Benchmark) +open class PutAll { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000) + var size: Int = 0 + + @Param(HASH_IMPL, ORDERED_IMPL) + var implementation = "" + + @Param(ASCENDING_HASH_CODE, RANDOM_HASH_CODE, COLLISION_HASH_CODE) + var hashCodeType = "" + + private var lhs = persistentMapOf() + private var lhsSmall = persistentMapOf() + private var rhs = persistentMapOf() + private var rhsSmall = persistentMapOf() + + @Setup + fun prepare() { + val keys = generateKeys(hashCodeType, 2 * size) + lhs = persistentMapPut(implementation, keys.take(size)) + lhsSmall = persistentMapPut(implementation, keys.take((size / 1000) + 1)) + rhs = persistentMapPut(implementation, keys.takeLast(size)) + rhsSmall = persistentMapPut(implementation, keys.takeLast((size / 1000) + 1)) + } + + @Benchmark + fun putAllEqualSize(): PersistentMap { + return lhs.putAll(rhs) + } + + @Benchmark + fun putAllSmallIntoLarge(): PersistentMap { + return lhs.putAll(rhsSmall) + } + + @Benchmark + fun putAllLargeIntoSmall(): PersistentMap { + return lhsSmall.putAll(rhs) + } +} \ No newline at end of file From 06a24235b66121041a030f66b957cb82901f10e3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 16 Jul 2021 00:23:13 +0300 Subject: [PATCH 091/162] PeristentHashMapBuilder.putAll() with another persistent hash map can produce incorrect results #114 When putting all entries of a cell (1) into another cell (2), if (1) is an entry and (2) is a node, for optimization reasons the entry is put into the node. This leads to saving the old value of the entry if the node already contains the key. --- .../implementations/immutableMap/TrieNode.kt | 127 +++++++++--------- .../src/contract/map/ImmutableMapTest.kt | 10 ++ 2 files changed, 76 insertions(+), 61 deletions(-) diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 8b257850..9fa6898b 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -460,38 +460,72 @@ internal class TrieNode( } } - private fun mutablePutAllFromOtherNodeCell(other: TrieNode, - positionMask: Int, - shift: Int, - intersectionCounter: DeltaCounter, - mutator: PersistentHashMapBuilder): TrieNode { - return when { - other.hasNodeAt(positionMask) -> { - mutablePutAll( - other.nodeAtIndex(other.nodeIndex(positionMask)), - shift + LOG_MAX_BRANCHING_FACTOR, - intersectionCounter, - mutator - ) + /** + * Updates the cell of this node at [positionMask] with entries from the cell of [otherNode] at [positionMask]. + */ + private fun mutablePutAllFromOtherNodeCell( + otherNode: TrieNode, + positionMask: Int, + shift: Int, + intersectionCounter: DeltaCounter, + mutator: PersistentHashMapBuilder + ): TrieNode = when { + this.hasNodeAt(positionMask) -> { + val targetNode = this.nodeAtIndex(nodeIndex(positionMask)) + when { + otherNode.hasNodeAt(positionMask) -> { + val otherTargetNode = otherNode.nodeAtIndex(otherNode.nodeIndex(positionMask)) + targetNode.mutablePutAll(otherTargetNode, shift + LOG_MAX_BRANCHING_FACTOR, intersectionCounter, mutator) + } + otherNode.hasEntryAt(positionMask) -> { + val keyIndex = otherNode.entryKeyIndex(positionMask) + val key = otherNode.keyAtIndex(keyIndex) + val value = otherNode.valueAtKeyIndex(keyIndex) + val oldSize = mutator.size + targetNode.mutablePut(key.hashCode(), key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator).also { + if (mutator.size == oldSize) intersectionCounter.count++ + } + } + else -> targetNode } - other.hasEntryAt(positionMask) -> { - val keyIndex = other.entryKeyIndex(positionMask) - val key = other.keyAtIndex(keyIndex) - val value = other.valueAtKeyIndex(keyIndex) - val oldSize = mutator.size - val newNode = mutablePut( - key.hashCode(), - key, - value, - shift + LOG_MAX_BRANCHING_FACTOR, - mutator - ) - if (mutator.size == oldSize) { - intersectionCounter.count++ + } + + otherNode.hasNodeAt(positionMask) -> { + val otherTargetNode = otherNode.nodeAtIndex(otherNode.nodeIndex(positionMask)) + when { + this.hasEntryAt(positionMask) -> { + // if otherTargetNode already has a value associated with the key, do not put this entry + val keyIndex = this.entryKeyIndex(positionMask) + val key = this.keyAtIndex(keyIndex) + if (otherTargetNode.containsKey(key.hashCode(), key, shift + LOG_MAX_BRANCHING_FACTOR)) { + intersectionCounter.count++ + otherTargetNode + } else { + val value = this.valueAtKeyIndex(keyIndex) + otherTargetNode.mutablePut(key.hashCode(), key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) + } } - newNode + else -> otherTargetNode } - else -> this + } + + else -> { // two entries, and they are not equal by key. See (**) in mutablePutAll + val thisKeyIndex = this.entryKeyIndex(positionMask) + val thisKey = this.keyAtIndex(thisKeyIndex) + val thisValue = this.valueAtKeyIndex(thisKeyIndex) + val otherKeyIndex = otherNode.entryKeyIndex(positionMask) + val otherKey = otherNode.keyAtIndex(otherKeyIndex) + val otherValue = otherNode.valueAtKeyIndex(otherKeyIndex) + makeNode( + thisKey.hashCode(), + thisKey, + thisValue, + otherKey.hashCode(), + otherKey, + otherValue, + shift + LOG_MAX_BRANCHING_FACTOR, + mutator.ownership + ) } } @@ -575,7 +609,7 @@ internal class TrieNode( // but not in the new data nodes var newDataMap = dataMap xor otherNode.dataMap and newNodeMap.inv() // (**) now, this is tricky: we have a number of entry-entry pairs and we don't know yet whether - // they result in an entry (if they are equal) or a new node (if they are not) + // they result in an entry (if keys are equal) or a new node (if they are not) // but we want to keep it to single allocation, so we check and mark equal ones here (dataMap and otherNode.dataMap).forEachOneBit { positionMask, _ -> val leftKey = this.keyAtIndex(this.entryKeyIndex(positionMask)) @@ -586,7 +620,7 @@ internal class TrieNode( else newNodeMap = newNodeMap or positionMask // we can use this later to skip calling equals() again } - assert(newNodeMap and newDataMap == 0) + check(newNodeMap and newDataMap == 0) val mutableNode = when { this.ownedBy == mutator.ownership && this.dataMap == newDataMap && this.nodeMap == newNodeMap -> this else -> { @@ -596,36 +630,7 @@ internal class TrieNode( } newNodeMap.forEachOneBit { positionMask, index -> val newNodeIndex = mutableNode.buffer.size - 1 - index - mutableNode.buffer[newNodeIndex] = when { - hasNodeAt(positionMask) -> { - val before = nodeAtIndex(nodeIndex(positionMask)) - before.mutablePutAllFromOtherNodeCell(otherNode, positionMask, shift, intersectionCounter, mutator) - } - - otherNode.hasNodeAt(positionMask) -> { - val before = otherNode.nodeAtIndex(otherNode.nodeIndex(positionMask)) - before.mutablePutAllFromOtherNodeCell(this, positionMask, shift, intersectionCounter, mutator) - } - - else -> { // two entries, and they are not equal by key (see ** above) - val thisKeyIndex = this.entryKeyIndex(positionMask) - val thisKey = this.keyAtIndex(thisKeyIndex) - val thisValue = this.valueAtKeyIndex(thisKeyIndex) - val otherKeyIndex = otherNode.entryKeyIndex(positionMask) - val otherKey = otherNode.keyAtIndex(otherKeyIndex) - val otherValue = otherNode.valueAtKeyIndex(otherKeyIndex) - makeNode( - thisKey.hashCode(), - thisKey, - thisValue, - otherKey.hashCode(), - otherKey, - otherValue, - shift + LOG_MAX_BRANCHING_FACTOR, - mutator.ownership - ) - } - } + mutableNode.buffer[newNodeIndex] = mutablePutAllFromOtherNodeCell(otherNode, positionMask, shift, intersectionCounter, mutator) } newDataMap.forEachOneBit { positionMask, index -> val newKeyIndex = index * ENTRY_SIZE diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 1f1e90a4..b593d3b1 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -87,6 +87,16 @@ class ImmutableHashMapTest : ImmutableMapTest() { assertTrue(map1.containsKey(32)) } + + @Test + fun regressionGithubIssue114() { + // https://github.com/Kotlin/kotlinx.collections.immutable/issues/114 + val p = persistentMapOf(99 to 1) + val e = Array(101) { it }.map { it to it } + val c = persistentMapOf(*e.toTypedArray()) + val n = p.builder().apply { putAll(c) }.build() + assertEquals(99, n[99]) + } } class ImmutableOrderedMapTest : ImmutableMapTest() { From 028701502ede33d9dffafde7f67e798ed3fedc16 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 03:17:31 +0300 Subject: [PATCH 092/162] Upgrade kotlinx-benchmark version to 0.4.1 --- benchmarks/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 7fadb46b..84676c84 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("org.jetbrains.kotlinx.benchmark") version "0.3.0" + id("org.jetbrains.kotlinx.benchmark") version "0.4.1" } @@ -42,7 +42,7 @@ kotlin { commonMain { dependencies { api("org.jetbrains.kotlin:kotlin-stdlib-common") - api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.3.0") + api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.1") api(project(":kotlinx-collections-immutable")) } } From ce1b32db76b08f95e62a07f6e99e3e3ae216a7f8 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 03:48:20 +0300 Subject: [PATCH 093/162] Upgrade Kotlin version to 1.6.0 --- benchmarks/runner/src/csvPrinter.kt | 4 ++-- build.gradle.kts | 2 +- core/commonMain/src/extensions.kt | 4 ++++ .../immutableMap/PersistentHashMapBuilderContentViews.kt | 2 ++ core/commonTest/src/contract/list/ImmutableListTest.kt | 5 ++--- core/commonTest/src/contract/map/ImmutableMapTest.kt | 4 ++-- .../commonTest/src/implementations/list/TrieIteratorTest.kt | 6 ++++-- .../src/implementations/map/HashMapTrieNodeTest.kt | 6 ++++++ core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt | 3 ++- .../commonTest/src/stress/list/PersistentListBuilderTest.kt | 4 ++-- core/commonTest/src/stress/list/PersistentListTest.kt | 2 +- .../src/stress/map/PersistentHashMapBuilderTest.kt | 2 +- core/commonTest/src/stress/map/PersistentHashMapTest.kt | 2 +- .../src/stress/set/PersistentHashSetBuilderTest.kt | 2 +- core/commonTest/src/stress/set/PersistentHashSetTest.kt | 2 +- core/jsMain/src/internal/commonFunctions.kt | 2 +- core/nativeMain/src/internal/commonFunctions.kt | 2 +- 17 files changed, 34 insertions(+), 20 deletions(-) diff --git a/benchmarks/runner/src/csvPrinter.kt b/benchmarks/runner/src/csvPrinter.kt index 9d2c378e..ff100d07 100644 --- a/benchmarks/runner/src/csvPrinter.kt +++ b/benchmarks/runner/src/csvPrinter.kt @@ -15,11 +15,11 @@ fun printCsvResults(benchmarkResults: BenchmarkResults, outputPath: String) { File(outputPath).parentFile?.mkdirs() val fileWriter = FileWriter(outputPath) - fileWriter.appendln(csvHeader) + fileWriter.appendLine(csvHeader) benchmarkResults.runResults.forEach { res -> val paramsValuesString = benchmarkResults.paramsNames.joinToString(",") { res.paramValue(it) } val csvRow = "${res.benchmark},$paramsValuesString,${res.score.formatted()},${res.scoreError.formatted()},${res.allocRate.formatted()}" - fileWriter.appendln(csvRow) + fileWriter.appendLine(csvRow) } fileWriter.flush() diff --git a/build.gradle.kts b/build.gradle.kts index fe9aba73..ac258da8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0") } } diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index f6d75641..a6dd72de 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -48,6 +48,7 @@ inline fun PersistentList.mutate(mutator: (MutableList) -> Unit): Pers * @return a new persistent map with the provided modifications applied; * or this instance if no modifications were made in the result of this operation. */ +@Suppress("UNCHECKED_CAST") inline fun PersistentMap.mutate(mutator: (MutableMap) -> Unit): PersistentMap = (this as PersistentMap).builder().apply(mutator).build() @@ -321,6 +322,7 @@ infix fun PersistentCollection.intersect(elements: Iterable): Persiste * @return a new persistent map with an entry from the specified key-value [pair] added; * or this instance if no modifications were made in the result of this operation. */ +@Suppress("UNCHECKED_CAST") inline operator fun PersistentMap.plus(pair: Pair): PersistentMap = (this as PersistentMap).put(pair.first, pair.second) @@ -369,6 +371,7 @@ inline operator fun PersistentMap.plus(map: Map): Per * @return a new persistent map with keys and values from the specified [map] associated; * or this instance if no modifications were made in the result of this operation. */ +@Suppress("UNCHECKED_CAST") public fun PersistentMap.putAll(map: Map): PersistentMap = (this as PersistentMap).putAll(map) @@ -406,6 +409,7 @@ public fun PersistentMap.putAll(pairs: Sequence>): P * @return a new persistent map with the specified [key] and its corresponding value removed; * or this instance if it contains no mapping for the key. */ +@Suppress("UNCHECKED_CAST") public operator fun PersistentMap.minus(key: K): PersistentMap = (this as PersistentMap).remove(key) diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt index d4dd6e4d..f84911f6 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentViews.kt @@ -11,6 +11,7 @@ import kotlinx.collections.immutable.internal.MapImplementation internal abstract class AbstractMapBuilderEntries, K, V> : AbstractMutableSet() { final override fun contains(element: E): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. + @Suppress("USELESS_CAST") if ((element as? Any?) !is Map.Entry<*, *>) return false return containsEntry(element) } @@ -18,6 +19,7 @@ internal abstract class AbstractMapBuilderEntries, K, V> : A final override fun remove(element: E): Boolean { // TODO: Eliminate this check after KT-30016 gets fixed. + @Suppress("USELESS_CAST") if ((element as? Any?) !is Map.Entry<*, *>) return false return removeEntry(element) } diff --git a/core/commonTest/src/contract/list/ImmutableListTest.kt b/core/commonTest/src/contract/list/ImmutableListTest.kt index 4c959f35..9bfa4f51 100644 --- a/core/commonTest/src/contract/list/ImmutableListTest.kt +++ b/core/commonTest/src/contract/list/ImmutableListTest.kt @@ -84,7 +84,7 @@ class ImmutableListTest { var list = "abcxaxab12".toImmutableList().toPersistentList() for (i in list.indices) { - list = list.set(i, list[i] as Char + i) + list = list.set(i, list[i] + i) } assertEquals("ace{e}gi9;", list.joinToString("")) @@ -120,7 +120,6 @@ class ImmutableListTest { @Test fun subList() { val list = "abcxaxyz12".toImmutableList() val subList = list.subList(2, 5) // 2, 3, 4 - assertTrue(subList is ImmutableList) compareLists(listOf('c', 'x', 'a'), subList) assertFailsWith { list.subList(-1, 2) } @@ -142,7 +141,7 @@ class ImmutableListTest { testMutation { add(0, 'K') } testMutation { addAll("kotlin".toList()) } testMutation { addAll(0, "kotlin".toList()) } - testMutation { this[1] = this[1] as Char + 2 } + testMutation { this[1] = this[1] + 2 } testMutation { removeAt(lastIndex) } testMutation { remove('x') } testMutation { removeAll(listOf('x')) } diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index b593d3b1..4b30d193 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -80,7 +80,7 @@ class ImmutableHashMapTest : ImmutableMapTest() { // https://github.com/Kotlin/kotlinx.collections.immutable/issues/109 val map0 = immutableMapOf().put(0, 0).put(1, 1).put(32, 32) val map1 = map0.mutate { it.remove(0) } - val map2 = map1.mutate { + map1.mutate { it.remove(1) it.remove(0) } @@ -251,7 +251,7 @@ abstract class ImmutableMapTest { @Test fun builder() { val builder = immutableMapOf().builder() - "abcxaxyz12".associateTo(builder) { it to it.toInt() } + "abcxaxyz12".associateTo(builder) { it to it.code } val map = builder.build() assertEquals>(map, builder) assertSame(map, builder.build(), "Building the same list without modifications") diff --git a/core/commonTest/src/implementations/list/TrieIteratorTest.kt b/core/commonTest/src/implementations/list/TrieIteratorTest.kt index c2506b2f..8dc1d4eb 100644 --- a/core/commonTest/src/implementations/list/TrieIteratorTest.kt +++ b/core/commonTest/src/implementations/list/TrieIteratorTest.kt @@ -40,8 +40,10 @@ class TrieIteratorTest { leaves = newLeaves } - assert(leaves.size == 1) - return leaves[0] as Array + check(leaves.size == 1) + + @Suppress("UNCHECKED_CAST") + return leaves.single() as Array } @Test diff --git a/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt index 6e8407ec..f3e4ef5d 100644 --- a/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt +++ b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt @@ -16,6 +16,7 @@ class HashMapTrieNodeTest { private fun testEmptyMap(map: PersistentHashMap) { map.node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(0, dataMap) assertEquals(0, nodeMap) assertTrue(node.buffer.isEmpty()) @@ -33,6 +34,7 @@ class HashMapTrieNodeTest { map.node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(1 shl 0b01101, dataMap) assertEquals(0b0, nodeMap) assertTrue(arrayOf(wrapper1, 1) contentEquals node.buffer) @@ -74,6 +76,7 @@ class HashMapTrieNodeTest { map.remove(wrapper1).node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(0b10, dataMap) assertEquals(0b0, nodeMap) assertTrue(arrayOf(wrapper33, 33) contentEquals node.buffer) @@ -183,6 +186,7 @@ class HashMapTrieNodeTest { map.remove(wrapper1).remove(wrapper1057).node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(0b10, dataMap) assertEquals(0b0, nodeMap) assertTrue(arrayOf(wrapper33, 33) contentEquals node.buffer) @@ -228,6 +232,7 @@ class HashMapTrieNodeTest { map.remove(wrapper1).node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(0b10, dataMap) assertEquals(0b0, nodeMap) assertTrue(arrayOf(wrapper2, 2) contentEquals node.buffer) @@ -351,6 +356,7 @@ class HashMapTrieNodeTest { map.remove(wrapper3).remove(wrapper1).node.accept { node: TrieNode, shift: Int, hash: Int, dataMap: Int, nodeMap: Int -> assertEquals(0, shift) + assertEquals(0, hash) assertEquals(0b10, dataMap) assertEquals(0b0, nodeMap) assertTrue(arrayOf(wrapper2, 2) contentEquals node.buffer) diff --git a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt index e2e10cd2..936b3195 100644 --- a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt +++ b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt @@ -8,8 +8,9 @@ package tests.stress import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.time.* +import kotlin.time.Duration.Companion.seconds -@UseExperimental(ExperimentalTime::class) +@OptIn(ExperimentalTime::class) abstract class ExecutionTimeMeasuringTest { private var clockMark: TimeMark? = null diff --git a/core/commonTest/src/stress/list/PersistentListBuilderTest.kt b/core/commonTest/src/stress/list/PersistentListBuilderTest.kt index b6a2f377..19ab0aef 100644 --- a/core/commonTest/src/stress/list/PersistentListBuilderTest.kt +++ b/core/commonTest/src/stress/list/PersistentListBuilderTest.kt @@ -19,7 +19,7 @@ import kotlin.random.nextInt import kotlin.test.* import kotlin.time.ExperimentalTime -@UseExperimental(ExperimentalTime::class) +@OptIn(ExperimentalTime::class) class PersistentListBuilderTest : ExecutionTimeMeasuringTest() { @Test @@ -622,7 +622,7 @@ class PersistentListBuilderTest : ExecutionTimeMeasuringTest() { vectorGen.add( builders.map { it.build() } ) expected.add(lists) - val maxSize = builders.maxBy { it.size }?.size + val maxSize = builders.maxOf { it.size } println("Largest persistent list builder size: $maxSize") } diff --git a/core/commonTest/src/stress/list/PersistentListTest.kt b/core/commonTest/src/stress/list/PersistentListTest.kt index d6e3f38a..fa17d416 100644 --- a/core/commonTest/src/stress/list/PersistentListTest.kt +++ b/core/commonTest/src/stress/list/PersistentListTest.kt @@ -444,7 +444,7 @@ class PersistentListTest : ExecutionTimeMeasuringTest() { assertEquals>(lists, vectors) - val maxSize = vectors.maxBy { it.size }?.size + val maxSize = vectors.maxOf { it.size } println("Largest persistent list size: $maxSize") lists.forEachIndexed { index, list -> diff --git a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt index 1fed738e..deb20e4b 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt @@ -479,7 +479,7 @@ class PersistentHashMapBuilderTest : ExecutionTimeMeasuringTest() { mapGen.add( builders.map { it.build() } ) expected.add(maps) - val maxSize = builders.maxBy { it.size }?.size + val maxSize = builders.maxOf { it.size } println("Largest persistent map builder size: $maxSize") } diff --git a/core/commonTest/src/stress/map/PersistentHashMapTest.kt b/core/commonTest/src/stress/map/PersistentHashMapTest.kt index a738f2bd..6eb0c08a 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapTest.kt @@ -334,7 +334,7 @@ class PersistentHashMapTest : ExecutionTimeMeasuringTest() { assertEquals>(mutableMaps, immutableMaps) - val maxSize = immutableMaps.maxBy { it.size }?.size + val maxSize = immutableMaps.maxOf { it.size } println("Largest persistent map size: $maxSize") mutableMaps.forEachIndexed { index, mutableMap -> diff --git a/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt b/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt index c34d8987..3978b88d 100644 --- a/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt +++ b/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt @@ -300,7 +300,7 @@ class PersistentHashSetBuilderTest : ExecutionTimeMeasuringTest() { setGen.add( builders.map { it.build() } ) expected.add(sets) - val maxSize = builders.maxBy { it.size }?.size + val maxSize = builders.maxOf { it.size } println("Largest persistent set builder size: $maxSize") } diff --git a/core/commonTest/src/stress/set/PersistentHashSetTest.kt b/core/commonTest/src/stress/set/PersistentHashSetTest.kt index 0f9bc1fa..e37a31d5 100644 --- a/core/commonTest/src/stress/set/PersistentHashSetTest.kt +++ b/core/commonTest/src/stress/set/PersistentHashSetTest.kt @@ -271,7 +271,7 @@ class PersistentHashSetTest : ExecutionTimeMeasuringTest() { assertEquals>(mutableSets, immutableSets) - val maxSize = immutableSets.maxBy { it.size }?.size + val maxSize = immutableSets.maxOf { it.size } println("Largest persistent set size: $maxSize") mutableSets.forEachIndexed { index, mutableSet -> diff --git a/core/jsMain/src/internal/commonFunctions.kt b/core/jsMain/src/internal/commonFunctions.kt index 3ffbd2d3..a8fd2d7e 100644 --- a/core/jsMain/src/internal/commonFunctions.kt +++ b/core/jsMain/src/internal/commonFunctions.kt @@ -13,4 +13,4 @@ internal actual fun assert(condition: Boolean) { internal actual var AbstractMutableList<*>.modCount: Int get() = 0 - set(value) {} \ No newline at end of file + set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file diff --git a/core/nativeMain/src/internal/commonFunctions.kt b/core/nativeMain/src/internal/commonFunctions.kt index 9a02e102..fb430a68 100644 --- a/core/nativeMain/src/internal/commonFunctions.kt +++ b/core/nativeMain/src/internal/commonFunctions.kt @@ -10,4 +10,4 @@ internal actual fun assert(condition: Boolean) = kotlin.assert(condition) internal actual var AbstractMutableList<*>.modCount: Int get() = 0 - set(value) {} \ No newline at end of file + set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file From 7b88baf5e507f227d08e1fdebc5efd873651dd3f Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 04:47:52 +0300 Subject: [PATCH 094/162] @Suppress("EXTENSION_SHADOWED_BY_MEMBER") --- core/commonTest/src/testUtils.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/core/commonTest/src/testUtils.kt b/core/commonTest/src/testUtils.kt index 9d529918..bc59e9bc 100644 --- a/core/commonTest/src/testUtils.kt +++ b/core/commonTest/src/testUtils.kt @@ -11,6 +11,7 @@ import kotlin.native.concurrent.ThreadLocal internal fun Char.isUpperCase(): Boolean = this in 'A'..'Z' internal fun Char.isDigit(): Boolean = this in '0'..'9' +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") internal fun MutableMap.remove(key: K, value: V): Boolean = if (key in this && this[key] == value) { remove(key) From a2d5fcdae58e9e109ffb18bb3ba7452b3cd7e9ec Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 04:49:45 +0300 Subject: [PATCH 095/162] Upgrade jvmTarget to 1.8 --- .teamcity/additionalConfiguration.kt | 3 --- README.md | 3 --- benchmarks/build.gradle.kts | 5 +---- build.gradle.kts | 2 -- core/build.gradle.kts | 6 +----- 5 files changed, 2 insertions(+), 17 deletions(-) diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index f25bae31..f041037e 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -6,8 +6,5 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.Project fun Project.additionalConfiguration() { - params { - param("env.JDK_6", "%env.JDK_16%") - } subProject(benchmarksProject(knownBuilds.buildVersion)) } \ No newline at end of file diff --git a/README.md b/README.md index 339912d6..ae8a8b93 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,6 @@ Add dependencies (you can also add other modules that you need): ## Building from source -> :information_source: To build this project you will need to have a path to JDK 6 specified with the `JDK_6` environment variable or gradle property. -> For the local development purposes any JDK newer than that can be used instead. - You can build and install artifacts to maven local with: gradlew build install diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 84676c84..72d54d5b 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -9,8 +9,6 @@ plugins { evaluationDependsOn(":kotlinx-collections-immutable") -val JDK_6: String by project - kotlin { infra { target("macosX64") @@ -21,8 +19,7 @@ kotlin { jvm { compilations.all { kotlinOptions { - jvmTarget = "1.6" - jdkHome = JDK_6 + jvmTarget = "1.8" } } } diff --git a/build.gradle.kts b/build.gradle.kts index ac258da8..ffef2003 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,8 +20,6 @@ infra { } } -val JDK_6 by ext(System.getenv("JDK_6") ?: findProperty("JDK_6") as String? ?: error("Specify path to JDK 6 in JDK_6 environment variable or Gradle property")) - allprojects { repositories { mavenCentral() diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6ae9bea2..587c6550 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -13,8 +13,6 @@ mavenPublicationsPom { description.set("Kotlin Immutable Collections multiplatform library") } -val JDK_6: String by project - kotlin { infra { target("macosX64") @@ -28,8 +26,7 @@ kotlin { jvm { compilations.all { kotlinOptions { - jvmTarget = "1.6" - jdkHome = JDK_6 + jvmTarget = "1.8" } } } @@ -111,6 +108,5 @@ kotlin { tasks { named("jvmTest", Test::class) { maxHeapSize = "1024m" - executable = "$JDK_6/bin/java" } } \ No newline at end of file From 00f2fa5e9b2116a372d270c3ba93947185f48c05 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 04:51:09 +0300 Subject: [PATCH 096/162] Enable allWarningsAsErrors --- build.gradle.kts | 4 ++++ core/build.gradle.kts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ffef2003..cc7753a8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,4 +24,8 @@ allprojects { repositories { mavenCentral() } + + tasks.withType> { + kotlinOptions.allWarningsAsErrors = true + } } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 587c6550..573cc405 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -53,7 +53,7 @@ kotlin { resources.setSrcDirs(listOf("$name/resources")) languageSettings.apply { // progressiveMode = true - useExperimentalAnnotation("kotlin.Experimental") + optIn("kotlin.RequiresOptIn") } } From 3eb42d3b53c3654df14842672dcc1f42f1860eb4 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 27 Dec 2021 22:28:39 +0300 Subject: [PATCH 097/162] Disable allWarningsAsErrors for now because of KT-46257 --- build.gradle.kts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cc7753a8..936b4e9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,8 @@ allprojects { mavenCentral() } - tasks.withType> { - kotlinOptions.allWarningsAsErrors = true - } + // TODO: enable after https://youtrack.jetbrains.com/issue/KT-46257 gets fixed +// tasks.withType> { +// kotlinOptions.allWarningsAsErrors = true +// } } \ No newline at end of file From 7fe606a5c7569476700bde33d3e55cf5938eb0ec Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 16 Dec 2021 16:25:28 +0300 Subject: [PATCH 098/162] Add other Darwin K/N targets --- core/build.gradle.kts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 573cc405..66f149e8 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,12 +15,25 @@ mavenPublicationsPom { kotlin { infra { - target("macosX64") - target("iosX64") - target("iosArm64") - target("iosArm32") target("linuxX64") target("mingwX64") + + common("darwin") { + target("macosX64") + target("macosArm64") + target("iosX64") + target("iosArm64") + target("iosArm32") + target("iosSimulatorArm64") + target("watchosArm32") + target("watchosArm64") + target("watchosX86") + target("watchosX64") + target("watchosSimulatorArm64") + target("tvosArm64") + target("tvosX64") + target("tvosSimulatorArm64") + } } jvm { @@ -97,11 +110,11 @@ kotlin { } val nativeMain by getting { - dependencies { - - } + dependsOn(commonMain.get()) + } + val nativeTest by getting { + dependsOn(commonTest.get()) } - } } From 8b72b9b0a46f48ca25736123e0aa1691b812fdcc Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 11 Jan 2022 21:11:43 +0300 Subject: [PATCH 099/162] Update CHANGELOG.md for 0.3.5 release Co-authored-by: ilya-g --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90002f12..6318d3a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## 0.3.5 + +- Upgrade Kotlin version up to 1.6.0 +- Raise the JVM bytecode target to 1.8. Now the library cannot be used on JDK 1.6 and 1.7. +- Add other Apple K/N targets +- Implement faster equals function for sets and maps +- Fix PersistentHashMapBuilder.putAll() #[114](https://github.com/Kotlin/kotlinx.collections.immutable/issues/114) + ## 0.3.4 - Upgrade Kotlin version up to 1.4.30 From 6dcd3935aa5ccef2fb19a8ee1503d2a6f29bc44c Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 11 Jan 2022 21:12:46 +0300 Subject: [PATCH 100/162] Update README.md for 0.3.5 release --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ae8a8b93..1f1bfc85 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Immutable collection interfaces and implementation prototypes for Kotlin. -This is a multiplatform library providing implementations for `jvm`, `js`, `mingwX64`, `linuxX64`, `macosX64`, `iosX64`, `iosArm64`, `iosArm32` Kotlin targets. +This is a multiplatform library providing implementations for `jvm`, `js` (both Legacy and IR), `mingwX64`, `linuxX64` and Apple Kotlin/Native targets. For further details see the [proposal](proposal.md). @@ -117,7 +117,7 @@ collection.mutate { some_actions_on(it) } The library is published to Maven Central repository. -The library depends on the Kotlin Standard Library of the version at least `1.4.30`. +The library depends on the Kotlin Standard Library of the version at least `1.6.0`. ### Gradle @@ -136,7 +136,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.4") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") } } } @@ -152,7 +152,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.4 + 0.3.5 ``` From a475e635d3b5a66734cc825595369e908a698904 Mon Sep 17 00:00:00 2001 From: Egor Tolstoy Date: Wed, 25 May 2022 12:14:08 +0300 Subject: [PATCH 101/162] Update README.md (#127) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f1bfc85..773058a2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Immutable Collections Library for Kotlin -[![JetBrains incubator project](https://jb.gg/badges/incubator.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) +[![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html) +[![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.jetbrains.kotlinx%22%20AND%20a:%22kotlinx-collections-immutable%22) From d7b83a13fed459c032dab1b4665eda20a04c740f Mon Sep 17 00:00:00 2001 From: Ji Sungbin Date: Sat, 16 Jul 2022 17:27:12 +0900 Subject: [PATCH 102/162] Fix wrong method `toImmutable()` -> `toImmutableList()` --- proposal.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposal.md b/proposal.md index 07ab71e5..80876b12 100644 --- a/proposal.md +++ b/proposal.md @@ -48,7 +48,7 @@ For persistent collection interfaces there shall be provided the following imple class Query(parameters: List) { // creates an immutable list or does nothing if it's already immutable - val parameters = parameters.toImmutable() + val parameters = parameters.toImmutableList() } ``` From 25f88e84fb3f7388098f76738a790f72250ff67a Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 17:09:07 +0300 Subject: [PATCH 103/162] Upgrade Kotlin version to 1.8.21 --- benchmarks/build.gradle.kts | 4 ++-- build.gradle.kts | 2 +- core/build.gradle.kts | 16 ++-------------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 72d54d5b..9fa92af0 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -24,7 +24,7 @@ kotlin { } } - js { + js(IR) { nodejs { } @@ -38,7 +38,7 @@ kotlin { sourceSets { commonMain { dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib-common") + api("org.jetbrains.kotlin:kotlin-stdlib") api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.1") api(project(":kotlinx-collections-immutable")) } diff --git a/build.gradle.kts b/build.gradle.kts index 936b4e9b..873917b0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21") } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 66f149e8..98950ed1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -73,40 +73,28 @@ kotlin { sourceSets { commonMain { dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib-common") + api("org.jetbrains.kotlin:kotlin-stdlib") } } commonTest { dependencies { - api("org.jetbrains.kotlin:kotlin-test-common") - api("org.jetbrains.kotlin:kotlin-test-annotations-common") + api("org.jetbrains.kotlin:kotlin-test") } } val jvmMain by getting { - dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib") - - } } val jvmTest by getting { dependencies { - api("org.jetbrains.kotlin:kotlin-test-junit") implementation("com.google.guava:guava-testlib:18.0") } } val jsMain by getting { - dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib-js") - } } val jsTest by getting { - dependencies { - api("org.jetbrains.kotlin:kotlin-test-js") - } } val nativeMain by getting { From f334c1782b93ab930bbde5c8b09db578bff393b3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 17:25:20 +0300 Subject: [PATCH 104/162] Upgrade Gradle version to 7.4.2 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da9702f9..aa991fce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From d152f6678aeb4a06348ad071814b8aad2d7ac868 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 22:11:46 +0300 Subject: [PATCH 105/162] Upgrade kotlinx-benchmark version to 0.4.8 --- benchmarks/build.gradle.kts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 9fa92af0..1f1167b0 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("org.jetbrains.kotlinx.benchmark") version "0.4.1" + id("org.jetbrains.kotlinx.benchmark") version "0.4.8" } @@ -39,7 +39,7 @@ kotlin { commonMain { dependencies { api("org.jetbrains.kotlin:kotlin-stdlib") - api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.1") + api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.8") api(project(":kotlinx-collections-immutable")) } } @@ -93,7 +93,6 @@ benchmark { jmhVersion = "1.21" } register("js") - register("native") register("macosX64") register("linuxX64") register("mingwX64") From 19de3c620083349c08e50fe435b88684999f6f8f Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 22:06:33 +0300 Subject: [PATCH 106/162] Update the list of supported native targets According to https://kotlinlang.org/docs/native-target-support.html --- README.md | 3 ++- core/build.gradle.kts | 42 +++++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 773058a2..7cdd977f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ Immutable collection interfaces and implementation prototypes for Kotlin. -This is a multiplatform library providing implementations for `jvm`, `js` (both Legacy and IR), `mingwX64`, `linuxX64` and Apple Kotlin/Native targets. +This is a multiplatform library providing implementations for `jvm`, `js` (both Legacy and IR), +and all [targets supported by the Kotlin/Native compiler](https://kotlinlang.org/docs/native-target-support.html). For further details see the [proposal](proposal.md). diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 98950ed1..70f435fc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,25 +15,33 @@ mavenPublicationsPom { kotlin { infra { + // According to https://kotlinlang.org/docs/native-target-support.html + + // Tier 1 target("linuxX64") - target("mingwX64") + target("macosX64") + target("macosArm64") + target("iosSimulatorArm64") + target("iosX64") - common("darwin") { - target("macosX64") - target("macosArm64") - target("iosX64") - target("iosArm64") - target("iosArm32") - target("iosSimulatorArm64") - target("watchosArm32") - target("watchosArm64") - target("watchosX86") - target("watchosX64") - target("watchosSimulatorArm64") - target("tvosArm64") - target("tvosX64") - target("tvosSimulatorArm64") - } + // Tier 2 + target("linuxArm64") + target("watchosSimulatorArm64") + target("watchosX64") + target("watchosArm32") + target("watchosArm64") + target("tvosSimulatorArm64") + target("tvosX64") + target("tvosArm64") + target("iosArm64") + + // Tier 3 + target("androidNativeArm32") + target("androidNativeArm64") + target("androidNativeX86") + target("androidNativeX64") + target("mingwX64") + target("watchosDeviceArm64") } jvm { From 8bd9f331fb65da6e4de329d4b353b919e030a399 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 22:29:51 +0300 Subject: [PATCH 107/162] Remove deprecated kotlin.mpp.enableCompatibilityMetadataVariant/enableGranularSourceSetsMetadata The properties are deprecated and will be removed in Kotlin 1.9.20 --- gradle.properties | 3 --- 1 file changed, 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 733103e3..de5eed92 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,9 +4,6 @@ versionSuffix=SNAPSHOT org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -kotlin.mpp.enableGranularSourceSetsMetadata=true -kotlin.mpp.enableCompatibilityMetadataVariant=true - kotlin.native.distribution.type=prebuilt # Workaround for Bintray treating .sha512 files as artifacts From bf6627bbf01ee01907c22f335df1dbc757c59dff Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 22:32:43 +0300 Subject: [PATCH 108/162] Remove the workaround for Bintray treating .sha512 files as artifacts --- gradle.properties | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index de5eed92..beebd835 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,4 @@ versionSuffix=SNAPSHOT org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -kotlin.native.distribution.type=prebuilt - -# Workaround for Bintray treating .sha512 files as artifacts -# https://github.com/gradle/gradle/issues/11412 -systemProp.org.gradle.internal.publish.checksums.insecure=true \ No newline at end of file +kotlin.native.distribution.type=prebuilt \ No newline at end of file From d3fbf1914833836b26ea6fe4e1af4332bec9802d Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 5 Jul 2023 22:39:33 +0300 Subject: [PATCH 109/162] Enable allWarningsAsErrors for all KotlinCompile tasks --- build.gradle.kts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 873917b0..59261fce 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,8 +25,7 @@ allprojects { mavenCentral() } - // TODO: enable after https://youtrack.jetbrains.com/issue/KT-46257 gets fixed -// tasks.withType> { -// kotlinOptions.allWarningsAsErrors = true -// } + tasks.withType> { + kotlinOptions.allWarningsAsErrors = true + } } \ No newline at end of file From 42d740d7b0b08bf1089599bba6ce9bd508d96f76 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Sat, 8 Jul 2023 18:03:44 +0300 Subject: [PATCH 110/162] Remove explicit dependency on kotlin-stdlib --- benchmarks/build.gradle.kts | 5 ++--- core/build.gradle.kts | 15 +++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 1f1167b0..9a4c8be6 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -38,9 +38,8 @@ kotlin { sourceSets { commonMain { dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib") - api("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.8") - api(project(":kotlinx-collections-immutable")) + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.8") + implementation(project(":kotlinx-collections-immutable")) } } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 70f435fc..08cebf5c 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -79,15 +79,11 @@ kotlin { } sourceSets { - commonMain { - dependencies { - api("org.jetbrains.kotlin:kotlin-stdlib") - } + val commonMain by getting { } - - commonTest { + val commonTest by getting { dependencies { - api("org.jetbrains.kotlin:kotlin-test") + implementation(kotlin("test")) } } @@ -101,15 +97,14 @@ kotlin { val jsMain by getting { } - val jsTest by getting { } val nativeMain by getting { - dependsOn(commonMain.get()) + dependsOn(commonMain) } val nativeTest by getting { - dependsOn(commonTest.get()) + dependsOn(commonTest) } } } From 21c83ee17ff463aae6a275d1b5f059e659e8687e Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 14 Aug 2023 17:19:13 +0300 Subject: [PATCH 111/162] Upgrade Kotlin version to 1.9.0 --- build.gradle.kts | 2 +- core/nativeMain/src/internal/commonFunctions.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 59261fce..b70adb42 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") } } diff --git a/core/nativeMain/src/internal/commonFunctions.kt b/core/nativeMain/src/internal/commonFunctions.kt index fb430a68..3b9cc7c8 100644 --- a/core/nativeMain/src/internal/commonFunctions.kt +++ b/core/nativeMain/src/internal/commonFunctions.kt @@ -5,6 +5,7 @@ package kotlinx.collections.immutable.internal +@OptIn(kotlin.experimental.ExperimentalNativeApi::class) internal actual fun assert(condition: Boolean) = kotlin.assert(condition) From ea2787dc277d5704a72f9de4af0981537ea2babf Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 14 Aug 2023 17:19:30 +0300 Subject: [PATCH 112/162] Upgrade kotlinx-benchmark version to 0.4.9 --- benchmarks/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 9a4c8be6..cdac79f1 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("org.jetbrains.kotlinx.benchmark") version "0.4.8" + id("org.jetbrains.kotlinx.benchmark") version "0.4.9" } @@ -38,7 +38,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.8") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.9") implementation(project(":kotlinx-collections-immutable")) } } From ea71b024e246226bd35c1287a017c61feffc2679 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Tue, 3 Oct 2023 18:27:39 +0300 Subject: [PATCH 113/162] Remove the legacy js target --- README.md | 2 +- benchmarks/build.gradle.kts | 2 +- core/build.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7cdd977f..638a84db 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Immutable collection interfaces and implementation prototypes for Kotlin. -This is a multiplatform library providing implementations for `jvm`, `js` (both Legacy and IR), +This is a multiplatform library providing implementations for `jvm`, `js` ([IR](https://kotlinlang.org/docs/js-ir-compiler.html)), and all [targets supported by the Kotlin/Native compiler](https://kotlinlang.org/docs/native-target-support.html). For further details see the [proposal](proposal.md). diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index cdac79f1..9ee8bbce 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -24,7 +24,7 @@ kotlin { } } - js(IR) { + js { nodejs { } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 08cebf5c..64a6eb2c 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -52,7 +52,7 @@ kotlin { } } - js(BOTH) { + js { nodejs { testTask { useMocha { From f1e6f814ad9da62d8ce4c8a3f7e49d8729fbbc93 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 5 Oct 2023 11:30:11 +0300 Subject: [PATCH 114/162] Remove kotlin.native.distribution.type=prebuilt K/N distribution includes prebuilt libraries by default. --- gradle.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index beebd835..3f827c82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,5 +3,3 @@ version=0.4 versionSuffix=SNAPSHOT org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 - -kotlin.native.distribution.type=prebuilt \ No newline at end of file From 8c1a56755c579066071afb7a7f2e4c66cbb224b6 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 5 Oct 2023 11:40:11 +0300 Subject: [PATCH 115/162] Upgrade infra plugin version to 0.4.0-dev-80 --- .teamcity/Benchmarks.kt | 4 ++-- .teamcity/additionalConfiguration.kt | 2 +- .teamcity/pom.xml | 4 ++-- .teamcity/settings.kts | 16 ++++++---------- .teamcity/utils.kt | 4 +--- build.gradle.kts | 9 ++++----- core/build.gradle.kts | 2 -- 7 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.teamcity/Benchmarks.kt b/.teamcity/Benchmarks.kt index fc206fa3..d9e018c9 100644 --- a/.teamcity/Benchmarks.kt +++ b/.teamcity/Benchmarks.kt @@ -3,8 +3,8 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2019_2.* -import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle +import jetbrains.buildServer.configs.kotlin.* +import jetbrains.buildServer.configs.kotlin.buildSteps.gradle import java.lang.IllegalArgumentException fun benchmarksProject(buildVersion: BuildType) = Project { diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index f041037e..6fca4acb 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2019_2.Project +import jetbrains.buildServer.configs.kotlin.Project fun Project.additionalConfiguration() { subProject(benchmarksProject(knownBuilds.buildVersion)) diff --git a/.teamcity/pom.xml b/.teamcity/pom.xml index 565421cd..5cae0cd4 100644 --- a/.teamcity/pom.xml +++ b/.teamcity/pom.xml @@ -77,13 +77,13 @@ org.jetbrains.teamcity - configs-dsl-kotlin + configs-dsl-kotlin-latest ${teamcity.dsl.version} compile org.jetbrains.teamcity - configs-dsl-kotlin-plugins + configs-dsl-kotlin-plugins-latest 1.0-SNAPSHOT pom compile diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts index f4610a64..f875d4f1 100644 --- a/.teamcity/settings.kts +++ b/.teamcity/settings.kts @@ -1,6 +1,6 @@ -import jetbrains.buildServer.configs.kotlin.v2019_2.* -import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.* -import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.* +import jetbrains.buildServer.configs.kotlin.* +import jetbrains.buildServer.configs.kotlin.buildSteps.* +import jetbrains.buildServer.configs.kotlin.triggers.* /* The settings script is an entry point for defining a TeamCity @@ -24,7 +24,7 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View 'Debug' option is available in the context menu for the task. */ -version = "2020.1" +version = "2023.05" project { // Disable editing of project and build settings from the UI to avoid issues with TeamCity @@ -142,8 +142,6 @@ fun Project.deployVersion() = BuildType { params { // enable editing of this configuration to set up things param("teamcity.ui.settings.readOnly", "false") - param("bintray-user", bintrayUserName) - password("bintray-key", bintrayToken) param(versionSuffixParameter, "dev-%build.counter%") param("reverse.dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.system.libs.repo.description", libraryStagingRepoDescription) param("env.libs.repository.id", "%dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.env.libs.repository.id%") @@ -158,7 +156,7 @@ fun Project.deployVersion() = BuildType { gradle { name = "Verify Gradle Configuration" tasks = "clean publishPrepareVersion" - gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter% -PbintrayApiKey=%bintray-key% -PbintrayUser=%bintray-user%" + gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter%" buildFile = "" jdkHome = "%env.$jdk%" } @@ -188,8 +186,6 @@ fun Project.deploy(platform: Platform, configureBuild: BuildType) = buildType("D params { param(versionSuffixParameter, "${configureBuild.depParamRefs[versionSuffixParameter]}") param(releaseVersionParameter, "${configureBuild.depParamRefs[releaseVersionParameter]}") - param("bintray-user", bintrayUserName) - password("bintray-key", bintrayToken) param("env.libs.repository.id", "%dep.$BUILD_CREATE_STAGING_REPO_ABSOLUTE_ID.env.libs.repository.id%") } @@ -202,7 +198,7 @@ fun Project.deploy(platform: Platform, configureBuild: BuildType) = buildType("D name = "Deploy ${platform.buildTypeName()} Binaries" jdkHome = "%env.$jdk%" jvmArgs = "-Xmx1g" - gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter% -PbintrayApiKey=%bintray-key% -PbintrayUser=%bintray-user%" + gradleParams = "--info --stacktrace -P$versionSuffixParameter=%$versionSuffixParameter% -P$releaseVersionParameter=%$releaseVersionParameter%" tasks = "clean publish" buildFile = "" gradleWrapperPath = "" diff --git a/.teamcity/utils.kt b/.teamcity/utils.kt index 6fa0973f..27595e3e 100644 --- a/.teamcity/utils.kt +++ b/.teamcity/utils.kt @@ -3,14 +3,12 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -import jetbrains.buildServer.configs.kotlin.v2019_2.* +import jetbrains.buildServer.configs.kotlin.* const val versionSuffixParameter = "versionSuffix" const val teamcitySuffixParameter = "teamcitySuffix" const val releaseVersionParameter = "releaseVersion" -const val bintrayUserName = "%env.BINTRAY_USER%" -const val bintrayToken = "%env.BINTRAY_API_KEY%" const val libraryStagingRepoDescription = "Kotlin-Immutable-Collections" val platforms = Platform.values() diff --git a/build.gradle.kts b/build.gradle.kts index b70adb42..f8e31d6f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,18 +5,17 @@ buildscript { } plugins { - id("kotlinx.team.infra") version "0.3.0-dev-64" + id("kotlinx.team.infra") version "0.4.0-dev-80" } infra { - teamcity { - libraryStagingRepoDescription = project.name - } publishing { include(":kotlinx-collections-immutable") libraryRepoUrl = "https://github.com/Kotlin/kotlinx.collections.immutable" - sonatype {} + sonatype { + libraryStagingRepoDescription = project.name + } } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 64a6eb2c..31eb6ed8 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -101,10 +101,8 @@ kotlin { } val nativeMain by getting { - dependsOn(commonMain) } val nativeTest by getting { - dependsOn(commonTest) } } } From 07ad9f70f62dc8047d42716094a50ebfb3d2129e Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 6 Oct 2023 13:15:39 +0300 Subject: [PATCH 116/162] Update CHANGELOG.md for 0.3.6 release --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6318d3a0..cb6fcbf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 0.3.6 + +- Upgrade Kotlin version up to 1.9.0 +- Support all targets currently supported by the K/N compiler +- Drop support for the Legacy js target + ## 0.3.5 - Upgrade Kotlin version up to 1.6.0 From d977f5509c441a9f39132202930be015dbf0f8b6 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 6 Oct 2023 13:17:18 +0300 Subject: [PATCH 117/162] Update README.md for 0.3.6 release --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 638a84db..c046cea6 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ collection.mutate { some_actions_on(it) } The library is published to Maven Central repository. -The library depends on the Kotlin Standard Library of the version at least `1.6.0`. +The library depends on the Kotlin Standard Library of the version at least `1.9.0`. ### Gradle @@ -138,7 +138,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6") } } } @@ -154,7 +154,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.5 + 0.3.6 ``` From c2aec0409ebe099ba558bc05278b77d3d5de87da Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Wed, 15 Nov 2023 20:38:13 +0100 Subject: [PATCH 118/162] Implement Wasm targets --- benchmarks/build.gradle.kts | 2 + build.gradle.kts | 12 +- core/build.gradle.kts | 106 +++++++++++++----- .../src/contract/map/ImmutableMapTest.kt | 13 ++- core/commonTest/src/testUtils.kt | 3 +- core/wasmMain/src/internal/commonFunctions.kt | 16 +++ core/wasmTest/src/testUtilsJs.kt | 26 +++++ 7 files changed, 142 insertions(+), 36 deletions(-) create mode 100644 core/wasmMain/src/internal/commonFunctions.kt create mode 100644 core/wasmTest/src/testUtilsJs.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 9ee8bbce..13be2d98 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -30,6 +30,8 @@ kotlin { } } + //TODO: Add wasm benchmarks as soon as wasmJs/wasmWasi will be published + sourceSets.all { kotlin.setSrcDirs(listOf("$name/src")) resources.setSrcDirs(listOf("$name/resources")) diff --git a/build.gradle.kts b/build.gradle.kts index f8e31d6f..d08a9b59 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,8 @@ +import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile + buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") } } @@ -26,5 +28,13 @@ allprojects { tasks.withType> { kotlinOptions.allWarningsAsErrors = true + kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + if (this is KotlinJsCompile) { + kotlinOptions.freeCompilerArgs += "-Xwasm-enable-array-range-checks" + } } +} + +tasks.withType().configureEach { + args.add("--ignore-engines") } \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 31eb6ed8..ec3a3c8d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -14,35 +14,34 @@ mavenPublicationsPom { } kotlin { - infra { - // According to https://kotlinlang.org/docs/native-target-support.html - - // Tier 1 - target("linuxX64") - target("macosX64") - target("macosArm64") - target("iosSimulatorArm64") - target("iosX64") - - // Tier 2 - target("linuxArm64") - target("watchosSimulatorArm64") - target("watchosX64") - target("watchosArm32") - target("watchosArm64") - target("tvosSimulatorArm64") - target("tvosX64") - target("tvosArm64") - target("iosArm64") - - // Tier 3 - target("androidNativeArm32") - target("androidNativeArm64") - target("androidNativeX86") - target("androidNativeX64") - target("mingwX64") - target("watchosDeviceArm64") - } + applyDefaultHierarchyTemplate() + + // According to https://kotlinlang.org/docs/native-target-support.html + // Tier 1 + linuxX64() + macosX64() + macosArm64() + iosSimulatorArm64() + iosX64() + + // Tier 2 + linuxArm64() + watchosSimulatorArm64() + watchosX64() + watchosArm32() + watchosArm64() + tvosSimulatorArm64() + tvosX64() + tvosArm64() + iosArm64() + + // Tier 3 + androidNativeArm32() + androidNativeArm64() + androidNativeX86() + androidNativeX64() + mingwX64() + watchosDeviceArm64() jvm { compilations.all { @@ -69,6 +68,26 @@ kotlin { } } + wasmJs { + nodejs { + testTask { + useMocha { + timeout = "30000" + } + } + } + } + + wasmWasi { + nodejs { + testTask { + useMocha { + timeout = "30000" + } + } + } + } + sourceSets.all { kotlin.setSrcDirs(listOf("$name/src")) resources.setSrcDirs(listOf("$name/resources")) @@ -100,6 +119,25 @@ kotlin { val jsTest by getting { } + val wasmMain by creating { + } + val wasmTest by creating { + } + + val wasmJsMain by getting { + dependsOn(wasmMain) + } + val wasmJsTest by getting { + dependsOn(wasmTest) + } + + val wasmWasiMain by getting { + dependsOn(wasmMain) + } + val wasmWasiTest by getting { + dependsOn(wasmTest) + } + val nativeMain by getting { } val nativeTest by getting { @@ -111,4 +149,14 @@ tasks { named("jvmTest", Test::class) { maxHeapSize = "1024m" } +} + +with(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.apply(rootProject)) { + nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2" + nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary" +} + +// Drop this when node js version become stable +tasks.withType().configureEach { + args.add("--ignore-engines") } \ No newline at end of file diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 4b30d193..a1b9a03b 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -283,14 +283,17 @@ abstract class ImmutableMapTest { } @Test fun noOperation() { - immutableMapOf().toPersistentMap().testNoOperation({ clear() }, { clear() }) + immutableMapOf().toPersistentMap().testNoOperation({ clear() }, { clear() }) - val map = immutableMapOf("x" to 1, null to "x").toPersistentMap() + val instance = Any() + val instance2 = Any() + + val map = immutableMapOf("x" to instance, null to "x").toPersistentMap() with(map) { testNoOperation({ remove("y") }, { remove("y") }) - testNoOperation({ remove("x", 2) }, { remove("x", 2) }) - testNoOperation({ put("x", 1) }, { put("x", 1) }) // does not hold - testNoOperation({ putAll(this) }, { putAll(this) }) // does not hold + testNoOperation({ remove("x", instance2) }, { remove("x", instance2) }) + testNoOperation({ put("x", instance) }, { put("x", instance) }) + testNoOperation({ putAll(this) }, { putAll(this) }) testNoOperation({ putAll(emptyMap()) }, { putAll(emptyMap()) }) } } diff --git a/core/commonTest/src/testUtils.kt b/core/commonTest/src/testUtils.kt index bc59e9bc..46f31e28 100644 --- a/core/commonTest/src/testUtils.kt +++ b/core/commonTest/src/testUtils.kt @@ -25,7 +25,8 @@ public expect fun assertTypeEquals(expected: Any?, actual: Any?) public enum class TestPlatform { JVM, JS, - Native + Native, + Wasm, } public expect val currentPlatform: TestPlatform diff --git a/core/wasmMain/src/internal/commonFunctions.kt b/core/wasmMain/src/internal/commonFunctions.kt new file mode 100644 index 00000000..a779f323 --- /dev/null +++ b/core/wasmMain/src/internal/commonFunctions.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.collections.immutable.internal + +internal actual fun assert(condition: Boolean) { + if (!condition) { + throw AssertionError("Assertion failed") + } +} + +internal actual var AbstractMutableList<*>.modCount: Int + get() = 0 + set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file diff --git a/core/wasmTest/src/testUtilsJs.kt b/core/wasmTest/src/testUtilsJs.kt new file mode 100644 index 00000000..c0ec59d6 --- /dev/null +++ b/core/wasmTest/src/testUtilsJs.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests + +import kotlin.test.assertTrue + +public actual fun assertTypeEquals(expected: Any?, actual: Any?) { + if (expected != null && actual != null) { + assertTrue(expected::class.isInstance(actual) || actual::class.isInstance(expected), + "Expected: $expected, Actual: $actual") + } else { + assertTrue(expected == null && actual == null) + } +} + +public actual val currentPlatform: TestPlatform get() = TestPlatform.Wasm + +actual object NForAlgorithmComplexity { + actual val O_N: Int = 500_000 + actual val O_NlogN: Int = 100_000 + actual val O_NN: Int = 5_000 + actual val O_NNlogN: Int = 1_000 +} \ No newline at end of file From 998929a1c5b108e630463ed6f2f18e868da0ad37 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Thu, 23 Nov 2023 14:51:20 +0100 Subject: [PATCH 119/162] Improve ImmutableMapTest.noOperation test --- .../src/contract/map/ImmutableMapTest.kt | 50 +++++++++++++++---- core/commonTest/src/stress/ObjectWrapper.kt | 4 ++ 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index a1b9a03b..43c895e3 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -10,7 +10,9 @@ import tests.contract.collectionBehavior import tests.contract.compare import tests.contract.mapBehavior import tests.contract.setBehavior +import tests.remove import tests.stress.IntWrapper +import tests.stress.ObjectWrapper import kotlin.test.* class ImmutableHashMapTest : ImmutableMapTest() { @@ -285,17 +287,35 @@ abstract class ImmutableMapTest { @Test fun noOperation() { immutableMapOf().toPersistentMap().testNoOperation({ clear() }, { clear() }) - val instance = Any() - val instance2 = Any() - - val map = immutableMapOf("x" to instance, null to "x").toPersistentMap() - with(map) { - testNoOperation({ remove("y") }, { remove("y") }) - testNoOperation({ remove("x", instance2) }, { remove("x", instance2) }) - testNoOperation({ put("x", instance) }, { put("x", instance) }) - testNoOperation({ putAll(this) }, { putAll(this) }) - testNoOperation({ putAll(emptyMap()) }, { putAll(emptyMap()) }) - } + val key = ObjectWrapper("x", "x".hashCode()) + val equalKey = ObjectWrapper("x", "x".hashCode()) // equalKey == key && equalKey !== key + val notEqualKey = ObjectWrapper("y", "x".hashCode()) // notEqualKey != key && notEqualKey.hashCode == key.hashCode + val value = ObjectWrapper(1, 1) + val equalValue = ObjectWrapper(1, 1) // equalValue == value && equalValue !== value + val notEqualValue = ObjectWrapper(2, 2) // notEqualValue != value + + /* + To avoid changes to a persistent map: + * remove(key): + no key in map is equal (==) to the given key + * remove(key, value): + the above, or the value for the found key is not equal (!=) to the given value + * put(key, value): + map has a key equal (==) to the given key, and the value for the found key is the same (===) as the given value + */ + val map = immutableMapOf(key to value, null to "x").toPersistentMap() + + map.testNoOperation({ remove(notEqualKey) }, { remove(notEqualKey) }) + map.testNotNoOperation({ remove(equalKey) }, { remove(equalKey) }) + + map.testNoOperation({ remove(key, notEqualValue) }, { remove(key, notEqualValue) }) + map.testNotNoOperation({ remove(key, equalValue) }, { remove(key, equalValue) }) + + map.testNoOperation({ put(equalKey, value) }, { put(equalKey, value) }) + map.testNotNoOperation({ put(equalKey, equalValue) }, { put(equalKey, equalValue) }) + + map.testNoOperation({ putAll(this) }, { putAll(this) }) + map.testNoOperation({ putAll(emptyMap()) }, { putAll(emptyMap()) }) } fun PersistentMap.testNoOperation(persistent: PersistentMap.() -> PersistentMap, mutating: MutableMap.() -> Unit) { @@ -306,6 +326,14 @@ abstract class ImmutableMapTest { assertSame(this, buildResult) } + fun PersistentMap.testNotNoOperation(persistent: PersistentMap.() -> PersistentMap, mutating: MutableMap.() -> Unit) { + val result = this.persistent() + val buildResult = this.mutate(mutating) + // Ensure mutating operations do not return the same instance + assertNotSame(this, result) + assertNotSame(this, buildResult) + } + @Test fun covariantTyping() { diff --git a/core/commonTest/src/stress/ObjectWrapper.kt b/core/commonTest/src/stress/ObjectWrapper.kt index 609ee92f..c217de6a 100644 --- a/core/commonTest/src/stress/ObjectWrapper.kt +++ b/core/commonTest/src/stress/ObjectWrapper.kt @@ -27,6 +27,10 @@ class ObjectWrapper>( override fun compareTo(other: ObjectWrapper): Int { return obj.compareTo(other.obj) } + + override fun toString(): String { + return "ObjectWrapper($obj, hashCode = $hashCode)" + } } typealias IntWrapper = ObjectWrapper \ No newline at end of file From f04cb64a7444765da090f29750773a4bbe611210 Mon Sep 17 00:00:00 2001 From: Fabian Andera Date: Sun, 10 Dec 2023 16:05:36 +0100 Subject: [PATCH 120/162] Add extension functions to convert Array to persistent collections #159 --- core/commonMain/src/extensions.kt | 34 ++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index a6dd72de..b8ee2318 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -579,6 +579,11 @@ fun Iterable.toImmutableList(): ImmutableList = this as? ImmutableList ?: this.toPersistentList() +/** + * Returns an immutable list containing all elements of this array. + */ +fun Array.toImmutableList(): ImmutableList = toPersistentList() + /** * Returns an immutable list containing all elements of this sequence. */ @@ -590,9 +595,6 @@ fun Sequence.toImmutableList(): ImmutableList = toPersistentList() fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() -// fun Array.toImmutableList(): ImmutableList = immutableListOf() + this.asList() - - /** * Returns a persistent list containing all elements of this collection. * @@ -604,6 +606,11 @@ fun Iterable.toPersistentList(): PersistentList = ?: (this as? PersistentList.Builder)?.build() ?: persistentListOf() + this +/** + * Returns a persistent list containing all elements of this array. + */ +fun Array.toPersistentList(): PersistentList = persistentListOf() + this + /** * Returns a persistent list containing all elements of this sequence. */ @@ -628,6 +635,13 @@ fun Iterable.toImmutableSet(): ImmutableSet = ?: (this as? PersistentSet.Builder)?.build() ?: persistentSetOf() + this +/** + * Returns an immutable set of all elements of this array. + * + * Elements of the returned set are iterated in the same order as in this array. + */ +fun Array.toImmutableSet(): ImmutableSet = toPersistentSet() + /** * Returns an immutable set of all elements of this sequence. * @@ -656,6 +670,13 @@ fun Iterable.toPersistentSet(): PersistentSet = ?: (this as? PersistentOrderedSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf() + this +/** + * Returns a persistent set of all elements of this array. + * + * Elements of the returned set are iterated in the same order as in this array. + */ +fun Array.toPersistentSet(): PersistentSet = persistentSetOf() + this + /** * Returns a persistent set of all elements of this sequence. * @@ -685,6 +706,13 @@ fun Iterable.toPersistentHashSet(): PersistentSet ?: (this as? PersistentHashSetBuilder)?.build() ?: PersistentHashSet.emptyOf() + this +/** + * Returns a persistent set of all elements of this array. + * + * Order of the elements in the returned set is unspecified. + */ +fun Array.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this + /** * Returns a persistent set of all elements of this sequence. * From 1ddfc2cfa56f5c09d0ea019a6d25ff12eecafeeb Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 19 Dec 2023 21:14:34 +0800 Subject: [PATCH 121/162] Update badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c046cea6..53c2cdf2 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![Kotlin Alpha](https://kotl.in/badges/alpha.svg)](https://kotlinlang.org/docs/components-stability.html) [![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) -[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) +[![GitHub license](https://img.shields.io/github/license/kotlin/kotlinx.collections.immutable)](LICENSE.txt) [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) -[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22org.jetbrains.kotlinx%22%20AND%20a:%22kotlinx-collections-immutable%22) +[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-collections-immutable) [![IR](https://img.shields.io/badge/Kotlin%2FJS-IR%20supported-yellow)](https://kotl.in/jsirsupported) Immutable collection interfaces and implementation prototypes for Kotlin. From e4600f8b792b250cc5e1f07c5f5ff719a84d03af Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Dec 2023 14:06:17 +0800 Subject: [PATCH 122/162] Replace MaxPermSize with MaxMetaspaceSize for JDK 17 I can't run this project on JDK 17 cause `MaxPermSize` has been removed from that, we can replace it to support running on newer JDKs. https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#removed-java-options --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3f827c82..d2050515 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ group=org.jetbrains.kotlinx version=0.4 versionSuffix=SNAPSHOT -org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 From c73ce02cf1214f14ec177cc6c2da15677ec98012 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 5 Jan 2024 11:56:41 +0200 Subject: [PATCH 123/162] Don't allocate temporary buffer in SmallPersistentVector.removeAll (#164) --------- Co-authored-by: mcpiroman --- .../immutableList/RemoveAllPredicate.kt | 94 +++++++++++++++++++ .../immutableList/SmallPersistentVector.kt | 26 ++--- 2 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 benchmarks/commonMain/src/benchmarks/immutableList/RemoveAllPredicate.kt diff --git a/benchmarks/commonMain/src/benchmarks/immutableList/RemoveAllPredicate.kt b/benchmarks/commonMain/src/benchmarks/immutableList/RemoveAllPredicate.kt new file mode 100644 index 00000000..46f3aae2 --- /dev/null +++ b/benchmarks/commonMain/src/benchmarks/immutableList/RemoveAllPredicate.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2016-2023 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package benchmarks.immutableList + +import benchmarks.* +import kotlinx.collections.immutable.PersistentList +import kotlinx.benchmark.* +import kotlinx.collections.immutable.persistentListOf +import kotlin.random.Random + +@State(Scope.Benchmark) +open class RemoveAllPredicate { + @Param(BM_1, BM_10, BM_100, BM_1000, BM_10000, BM_100000, BM_1000000, BM_10000000) + var size: Int = 0 + + private var persistentList = persistentListOf() + private val truePredicate: (String) -> Boolean = { true } + private val falsePredicate: (String) -> Boolean = { false } + private var randomHalfElementsPredicate: (String) -> Boolean = truePredicate + private var randomTenElementsPredicate: (String) -> Boolean = truePredicate + private var randomOneElementPredicate: (String) -> Boolean = truePredicate + private var tailElementsPredicate: (String) -> Boolean = truePredicate + + @Setup + fun prepare() { + val randomHalfElements = randomIndexes(size / 2).map { it.toString() }.toHashSet() + randomHalfElementsPredicate = { it in randomHalfElements } + + val randomTenElements = randomIndexes(10).map { it.toString() }.toHashSet() + randomTenElementsPredicate = { it in randomTenElements } + + val randomOneElement = Random.nextInt(size).toString() + randomOneElementPredicate = { it == randomOneElement } + + val tailElements = List(tailSize()) { (size - 1 - it).toString() }.toHashSet() + tailElementsPredicate = { it in tailElements } + + val allElements = List(size) { it.toString() } + persistentList = persistentListOf().addAll(allElements) + } + + // The benchmarks measure (time and memory spent in `removeAll` operation) / size + // + // Expected time: nearly constant + // Expected memory: nearly constant + + /** Removes all elements. */ + @Benchmark + fun removeAll_All(): PersistentList { + return persistentList.removeAll(truePredicate) + } + + /** Removes no elements. */ + @Benchmark + fun removeAll_Non(): PersistentList { + return persistentList.removeAll(falsePredicate) + } + + /** Removes half of the elements randomly selected. */ + @Benchmark + fun removeAll_RandomHalf(): PersistentList { + return persistentList.removeAll(randomHalfElementsPredicate) + } + + /** Removes 10 random elements. */ + @Benchmark + fun removeAll_RandomTen(): PersistentList { + return persistentList.removeAll(randomTenElementsPredicate) + } + + /** Removes a random element. */ + @Benchmark + fun removeAll_RandomOne(): PersistentList { + return persistentList.removeAll(randomOneElementPredicate) + } + + /** Removes last [tailSize] elements. */ + @Benchmark + fun removeAll_Tail(): PersistentList { + return persistentList.removeAll(tailElementsPredicate) + } + + private fun randomIndexes(count: Int): List { + return List(count) { Random.nextInt(size) } + } + + private fun tailSize(): Int { + val bufferSize = 32 + return (size and (bufferSize - 1)).let { if (it == 0) bufferSize else it } + } +} diff --git a/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt b/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt index d34ea1f8..a8f72696 100644 --- a/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt +++ b/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt @@ -49,30 +49,32 @@ internal class SmallPersistentVector(private val buffer: Array) : Immut } override fun removeAll(predicate: (E) -> Boolean): PersistentList { - var newBuffer = buffer var newSize = size - - var anyRemoved = false + var removeMask = 0 for (index in 0 until size) { @Suppress("UNCHECKED_CAST") val element = buffer[index] as E if (predicate(element)) { - if (!anyRemoved) { - newBuffer = buffer.copyOf() - newSize = index - - anyRemoved = true - } - } else if (anyRemoved) { - newBuffer[newSize++] = element + newSize-- + removeMask = removeMask or (1 shl index) } } + return when (newSize) { size -> this 0 -> EMPTY - else -> SmallPersistentVector(newBuffer.copyOfRange(0, newSize)) + else -> { + val newBuffer = buffer.copyOf(newSize) + var newIndex = removeMask.countTrailingZeroBits() + for (index in newIndex + 1 until size) { + if ((removeMask ushr index) and 1 == 0) { + newBuffer[newIndex++] = buffer[index] + } + } + SmallPersistentVector(newBuffer) + } } } From 3ea58c0a9ad89670dcb5943b64c929ab8e1ae5f8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 5 Jan 2024 21:56:14 +0800 Subject: [PATCH 124/162] Introduce binary compatibility validator --- build.gradle.kts | 8 + core/api/kotlinx-collections-immutable.api | 287 +++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 core/api/kotlinx-collections-immutable.api diff --git a/build.gradle.kts b/build.gradle.kts index d08a9b59..9c3b4719 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,7 @@ buildscript { plugins { id("kotlinx.team.infra") version "0.4.0-dev-80" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.2" } infra { @@ -21,6 +22,13 @@ infra { } } +apiValidation { + ignoredProjects += listOf( + "benchmarks", + "runner", + ) +} + allprojects { repositories { mavenCentral() diff --git a/core/api/kotlinx-collections-immutable.api b/core/api/kotlinx-collections-immutable.api new file mode 100644 index 00000000..09231a36 --- /dev/null +++ b/core/api/kotlinx-collections-immutable.api @@ -0,0 +1,287 @@ +public final class kotlinx/collections/immutable/ExtensionsKt { + public static final fun immutableHashMapOf ([Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun immutableHashSetOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun immutableListOf ()Lkotlinx/collections/immutable/PersistentList; + public static final fun immutableListOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun immutableMapOf ([Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun immutableSetOf ()Lkotlinx/collections/immutable/PersistentSet; + public static final fun immutableSetOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun intersect (Lkotlinx/collections/immutable/PersistentCollection;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun intersect (Lkotlinx/collections/immutable/PersistentSet;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun minus (Lkotlinx/collections/immutable/PersistentCollection;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun minus (Lkotlinx/collections/immutable/PersistentCollection;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun minus (Lkotlinx/collections/immutable/PersistentCollection;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun minus (Lkotlinx/collections/immutable/PersistentCollection;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun minus (Lkotlinx/collections/immutable/PersistentList;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentList; + public static final fun minus (Lkotlinx/collections/immutable/PersistentList;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun minus (Lkotlinx/collections/immutable/PersistentList;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentList; + public static final fun minus (Lkotlinx/collections/immutable/PersistentList;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun minus (Lkotlinx/collections/immutable/PersistentMap;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun minus (Lkotlinx/collections/immutable/PersistentMap;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun minus (Lkotlinx/collections/immutable/PersistentMap;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun minus (Lkotlinx/collections/immutable/PersistentMap;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun minus (Lkotlinx/collections/immutable/PersistentSet;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun minus (Lkotlinx/collections/immutable/PersistentSet;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun minus (Lkotlinx/collections/immutable/PersistentSet;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun minus (Lkotlinx/collections/immutable/PersistentSet;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun mutate (Lkotlinx/collections/immutable/PersistentList;Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentList; + public static final fun mutate (Lkotlinx/collections/immutable/PersistentMap;Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun mutate (Lkotlinx/collections/immutable/PersistentSet;Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun persistentHashMapOf ()Lkotlinx/collections/immutable/PersistentMap; + public static final fun persistentHashMapOf ([Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun persistentHashSetOf ()Lkotlinx/collections/immutable/PersistentSet; + public static final fun persistentHashSetOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun persistentListOf ()Lkotlinx/collections/immutable/PersistentList; + public static final fun persistentListOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun persistentMapOf ()Lkotlinx/collections/immutable/PersistentMap; + public static final fun persistentMapOf ([Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun persistentSetOf ()Lkotlinx/collections/immutable/PersistentSet; + public static final fun persistentSetOf ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun plus (Lkotlinx/collections/immutable/PersistentCollection;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun plus (Lkotlinx/collections/immutable/PersistentCollection;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun plus (Lkotlinx/collections/immutable/PersistentCollection;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun plus (Lkotlinx/collections/immutable/PersistentCollection;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public static final fun plus (Lkotlinx/collections/immutable/PersistentList;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentList; + public static final fun plus (Lkotlinx/collections/immutable/PersistentList;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun plus (Lkotlinx/collections/immutable/PersistentList;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentList; + public static final fun plus (Lkotlinx/collections/immutable/PersistentList;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun plus (Lkotlinx/collections/immutable/PersistentMap;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun plus (Lkotlinx/collections/immutable/PersistentMap;Ljava/util/Map;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun plus (Lkotlinx/collections/immutable/PersistentMap;Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun plus (Lkotlinx/collections/immutable/PersistentMap;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun plus (Lkotlinx/collections/immutable/PersistentMap;[Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun plus (Lkotlinx/collections/immutable/PersistentSet;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun plus (Lkotlinx/collections/immutable/PersistentSet;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun plus (Lkotlinx/collections/immutable/PersistentSet;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun plus (Lkotlinx/collections/immutable/PersistentSet;[Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun putAll (Lkotlinx/collections/immutable/PersistentMap;Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun putAll (Lkotlinx/collections/immutable/PersistentMap;Ljava/util/Map;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun putAll (Lkotlinx/collections/immutable/PersistentMap;Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun putAll (Lkotlinx/collections/immutable/PersistentMap;[Lkotlin/Pair;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun toImmutableList (Ljava/lang/CharSequence;)Lkotlinx/collections/immutable/ImmutableList; + public static final fun toImmutableList (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/ImmutableList; + public static final fun toImmutableList (Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/ImmutableList; + public static final fun toImmutableList ([Ljava/lang/Object;)Lkotlinx/collections/immutable/ImmutableList; + public static final fun toImmutableMap (Ljava/util/Map;)Lkotlinx/collections/immutable/ImmutableMap; + public static final fun toImmutableSet (Ljava/lang/CharSequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toImmutableSet (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/ImmutableSet; + public static final fun toImmutableSet (Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/ImmutableSet; + public static final fun toImmutableSet ([Ljava/lang/Object;)Lkotlinx/collections/immutable/ImmutableSet; + public static final fun toPersistentHashMap (Ljava/util/Map;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun toPersistentHashSet (Ljava/lang/CharSequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentHashSet (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentHashSet (Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentHashSet ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentList (Ljava/lang/CharSequence;)Lkotlinx/collections/immutable/PersistentList; + public static final fun toPersistentList (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentList; + public static final fun toPersistentList (Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentList; + public static final fun toPersistentList ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public static final fun toPersistentMap (Ljava/util/Map;)Lkotlinx/collections/immutable/PersistentMap; + public static final fun toPersistentSet (Ljava/lang/CharSequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentSet (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentSet (Lkotlin/sequences/Sequence;)Lkotlinx/collections/immutable/PersistentSet; + public static final fun toPersistentSet ([Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; +} + +public abstract interface class kotlinx/collections/immutable/ImmutableCollection : java/util/Collection, kotlin/jvm/internal/markers/KMappedMarker { +} + +public abstract interface class kotlinx/collections/immutable/ImmutableList : java/util/List, kotlin/jvm/internal/markers/KMappedMarker, kotlinx/collections/immutable/ImmutableCollection { + public abstract fun subList (II)Lkotlinx/collections/immutable/ImmutableList; +} + +public final class kotlinx/collections/immutable/ImmutableList$DefaultImpls { + public static fun subList (Lkotlinx/collections/immutable/ImmutableList;II)Lkotlinx/collections/immutable/ImmutableList; +} + +public abstract interface class kotlinx/collections/immutable/ImmutableMap : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { + public abstract fun getEntries ()Lkotlinx/collections/immutable/ImmutableSet; + public abstract fun getKeys ()Lkotlinx/collections/immutable/ImmutableSet; + public abstract fun getValues ()Lkotlinx/collections/immutable/ImmutableCollection; +} + +public abstract interface class kotlinx/collections/immutable/ImmutableSet : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker, kotlinx/collections/immutable/ImmutableCollection { +} + +public abstract interface class kotlinx/collections/immutable/PersistentCollection : kotlinx/collections/immutable/ImmutableCollection { + public abstract fun add (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun addAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun builder ()Lkotlinx/collections/immutable/PersistentCollection$Builder; + public abstract fun clear ()Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun removeAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun removeAll (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentCollection; + public abstract fun retainAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; +} + +public abstract interface class kotlinx/collections/immutable/PersistentCollection$Builder : java/util/Collection, kotlin/jvm/internal/markers/KMutableCollection { + public abstract fun build ()Lkotlinx/collections/immutable/PersistentCollection; +} + +public abstract interface class kotlinx/collections/immutable/PersistentList : kotlinx/collections/immutable/ImmutableList, kotlinx/collections/immutable/PersistentCollection { + public abstract fun add (ILjava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun add (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun addAll (ILjava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun addAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun builder ()Lkotlinx/collections/immutable/PersistentList$Builder; + public abstract fun clear ()Lkotlinx/collections/immutable/PersistentList; + public abstract fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun removeAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun removeAll (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun removeAt (I)Lkotlinx/collections/immutable/PersistentList; + public abstract fun retainAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public abstract fun set (ILjava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; +} + +public abstract interface class kotlinx/collections/immutable/PersistentList$Builder : java/util/List, kotlin/jvm/internal/markers/KMutableList, kotlinx/collections/immutable/PersistentCollection$Builder { + public abstract fun build ()Lkotlinx/collections/immutable/PersistentList; +} + +public final class kotlinx/collections/immutable/PersistentList$DefaultImpls { + public static fun subList (Lkotlinx/collections/immutable/PersistentList;II)Lkotlinx/collections/immutable/ImmutableList; +} + +public abstract interface class kotlinx/collections/immutable/PersistentMap : kotlinx/collections/immutable/ImmutableMap { + public abstract fun builder ()Lkotlinx/collections/immutable/PersistentMap$Builder; + public abstract fun clear ()Lkotlinx/collections/immutable/PersistentMap; + public abstract fun put (Ljava/lang/Object;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentMap; + public abstract fun putAll (Ljava/util/Map;)Lkotlinx/collections/immutable/PersistentMap; + public abstract fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentMap; + public abstract fun remove (Ljava/lang/Object;Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentMap; +} + +public abstract interface class kotlinx/collections/immutable/PersistentMap$Builder : java/util/Map, kotlin/jvm/internal/markers/KMutableMap { + public abstract fun build ()Lkotlinx/collections/immutable/PersistentMap; +} + +public abstract interface class kotlinx/collections/immutable/PersistentSet : kotlinx/collections/immutable/ImmutableSet, kotlinx/collections/immutable/PersistentCollection { + public abstract fun add (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public abstract fun addAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentSet; + public abstract fun builder ()Lkotlinx/collections/immutable/PersistentSet$Builder; + public abstract fun clear ()Lkotlinx/collections/immutable/PersistentSet; + public abstract fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentSet; + public abstract fun removeAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentSet; + public abstract fun removeAll (Lkotlin/jvm/functions/Function1;)Lkotlinx/collections/immutable/PersistentSet; + public abstract fun retainAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentSet; +} + +public abstract interface class kotlinx/collections/immutable/PersistentSet$Builder : java/util/Set, kotlin/jvm/internal/markers/KMutableSet, kotlinx/collections/immutable/PersistentCollection$Builder { + public abstract fun build ()Lkotlinx/collections/immutable/PersistentSet; +} + +public class kotlinx/collections/immutable/adapters/ImmutableCollectionAdapter : java/util/Collection, kotlin/jvm/internal/markers/KMappedMarker, kotlinx/collections/immutable/ImmutableCollection { + public fun (Ljava/util/Collection;)V + public fun add (Ljava/lang/Object;)Z + public fun addAll (Ljava/util/Collection;)Z + public fun clear ()V + public fun contains (Ljava/lang/Object;)Z + public fun containsAll (Ljava/util/Collection;)Z + public fun equals (Ljava/lang/Object;)Z + public fun getSize ()I + public fun hashCode ()I + public fun isEmpty ()Z + public fun iterator ()Ljava/util/Iterator; + public fun remove (Ljava/lang/Object;)Z + public fun removeAll (Ljava/util/Collection;)Z + public fun removeIf (Ljava/util/function/Predicate;)Z + public fun retainAll (Ljava/util/Collection;)Z + public final fun size ()I + public fun toArray ()[Ljava/lang/Object; + public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/collections/immutable/adapters/ImmutableListAdapter : java/util/List, kotlin/jvm/internal/markers/KMappedMarker, kotlinx/collections/immutable/ImmutableList { + public fun (Ljava/util/List;)V + public fun add (ILjava/lang/Object;)V + public fun add (Ljava/lang/Object;)Z + public fun addAll (ILjava/util/Collection;)Z + public fun addAll (Ljava/util/Collection;)Z + public fun clear ()V + public fun contains (Ljava/lang/Object;)Z + public fun containsAll (Ljava/util/Collection;)Z + public fun equals (Ljava/lang/Object;)Z + public fun get (I)Ljava/lang/Object; + public fun getSize ()I + public fun hashCode ()I + public fun indexOf (Ljava/lang/Object;)I + public fun isEmpty ()Z + public fun iterator ()Ljava/util/Iterator; + public fun lastIndexOf (Ljava/lang/Object;)I + public fun listIterator ()Ljava/util/ListIterator; + public fun listIterator (I)Ljava/util/ListIterator; + public fun remove (I)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;)Z + public fun removeAll (Ljava/util/Collection;)Z + public fun replaceAll (Ljava/util/function/UnaryOperator;)V + public fun retainAll (Ljava/util/Collection;)Z + public fun set (ILjava/lang/Object;)Ljava/lang/Object; + public final fun size ()I + public fun sort (Ljava/util/Comparator;)V + public synthetic fun subList (II)Ljava/util/List; + public fun subList (II)Lkotlinx/collections/immutable/ImmutableList; + public fun toArray ()[Ljava/lang/Object; + public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + public fun toString ()Ljava/lang/String; +} + +public final class kotlinx/collections/immutable/adapters/ImmutableMapAdapter : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker, kotlinx/collections/immutable/ImmutableMap { + public fun (Ljava/util/Map;)V + public fun clear ()V + public fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; + public fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; + public fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; + public fun containsKey (Ljava/lang/Object;)Z + public fun containsValue (Ljava/lang/Object;)Z + public synthetic fun entrySet ()Ljava/util/Set; + public final fun entrySet ()Lkotlinx/collections/immutable/ImmutableSet; + public fun equals (Ljava/lang/Object;)Z + public fun get (Ljava/lang/Object;)Ljava/lang/Object; + public fun getEntries ()Lkotlinx/collections/immutable/ImmutableSet; + public fun getKeys ()Lkotlinx/collections/immutable/ImmutableSet; + public fun getSize ()I + public fun getValues ()Lkotlinx/collections/immutable/ImmutableCollection; + public fun hashCode ()I + public fun isEmpty ()Z + public synthetic fun keySet ()Ljava/util/Set; + public final fun keySet ()Lkotlinx/collections/immutable/ImmutableSet; + public fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; + public fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun putAll (Ljava/util/Map;)V + public fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z + public fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z + public fun replaceAll (Ljava/util/function/BiFunction;)V + public final fun size ()I + public fun toString ()Ljava/lang/String; + public synthetic fun values ()Ljava/util/Collection; + public final fun values ()Lkotlinx/collections/immutable/ImmutableCollection; +} + +public final class kotlinx/collections/immutable/adapters/ImmutableSetAdapter : kotlinx/collections/immutable/adapters/ImmutableCollectionAdapter, kotlinx/collections/immutable/ImmutableSet { + public fun (Ljava/util/Set;)V +} + +public abstract class kotlinx/collections/immutable/implementations/immutableList/AbstractPersistentList : kotlin/collections/AbstractList, kotlinx/collections/immutable/PersistentList { + public fun ()V + public fun addAll (ILjava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public synthetic fun addAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; + public fun addAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public synthetic fun clear ()Lkotlinx/collections/immutable/PersistentCollection; + public fun clear ()Lkotlinx/collections/immutable/PersistentList; + public fun contains (Ljava/lang/Object;)Z + public fun containsAll (Ljava/util/Collection;)Z + public fun iterator ()Ljava/util/Iterator; + public fun listIterator ()Ljava/util/ListIterator; + public synthetic fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentCollection; + public fun remove (Ljava/lang/Object;)Lkotlinx/collections/immutable/PersistentList; + public synthetic fun removeAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; + public fun removeAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public synthetic fun retainAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentCollection; + public fun retainAll (Ljava/util/Collection;)Lkotlinx/collections/immutable/PersistentList; + public synthetic fun subList (II)Ljava/util/List; + public fun subList (II)Lkotlinx/collections/immutable/ImmutableList; +} + From 5350160b75c3ccc98d3a7fbe6d334bb3599a381d Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 12 Jan 2024 00:27:44 +0800 Subject: [PATCH 125/162] Enable Explicit API mode --- core/build.gradle.kts | 1 + core/commonMain/src/ImmutableCollection.kt | 20 +-- core/commonMain/src/ImmutableList.kt | 10 +- core/commonMain/src/ImmutableMap.kt | 16 +- core/commonMain/src/ImmutableSet.kt | 2 +- core/commonMain/src/extensions.kt | 148 +++++++++--------- .../immutableList/AbstractPersistentList.kt | 2 +- 7 files changed, 100 insertions(+), 99 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index ec3a3c8d..35262988 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,6 +15,7 @@ mavenPublicationsPom { kotlin { applyDefaultHierarchyTemplate() + explicitApi() // According to https://kotlinlang.org/docs/native-target-support.html // Tier 1 diff --git a/core/commonMain/src/ImmutableCollection.kt b/core/commonMain/src/ImmutableCollection.kt index dbd38b55..d36e838a 100644 --- a/core/commonMain/src/ImmutableCollection.kt +++ b/core/commonMain/src/ImmutableCollection.kt @@ -31,7 +31,7 @@ public interface PersistentCollection : ImmutableCollection { * @returns a new persistent collection with the specified [element] added; * or this instance if this collection does not support duplicates and it already contains the element. */ - fun add(element: @UnsafeVariance E): PersistentCollection + public fun add(element: @UnsafeVariance E): PersistentCollection /** * Returns the result of adding all elements of the specified [elements] collection to this collection. @@ -39,7 +39,7 @@ public interface PersistentCollection : ImmutableCollection { * @return a new persistent collection with elements of the specified [elements] collection added; * or this instance if no modifications were made in the result of this operation. */ - fun addAll(elements: Collection<@UnsafeVariance E>): PersistentCollection + public fun addAll(elements: Collection<@UnsafeVariance E>): PersistentCollection /** * Returns the result of removing a single appearance of the specified [element] from this collection. @@ -47,7 +47,7 @@ public interface PersistentCollection : ImmutableCollection { * @return a new persistent collection with a single appearance of the specified [element] removed; * or this instance if there is no such element in this collection. */ - fun remove(element: @UnsafeVariance E): PersistentCollection + public fun remove(element: @UnsafeVariance E): PersistentCollection /** * Returns the result of removing all elements in this collection that are also @@ -57,7 +57,7 @@ public interface PersistentCollection : ImmutableCollection { * contained in the specified [elements] collection removed; * or this instance if no modifications were made in the result of this operation. */ - fun removeAll(elements: Collection<@UnsafeVariance E>): PersistentCollection + public fun removeAll(elements: Collection<@UnsafeVariance E>): PersistentCollection /** * Returns the result of removing all elements in this collection that match the specified [predicate]. @@ -65,7 +65,7 @@ public interface PersistentCollection : ImmutableCollection { * @return a new persistent collection with elements matching the specified [predicate] removed; * or this instance if no elements match the predicate. */ - fun removeAll(predicate: (E) -> Boolean): PersistentCollection + public fun removeAll(predicate: (E) -> Boolean): PersistentCollection /** * Returns all elements in this collection that are also @@ -75,12 +75,12 @@ public interface PersistentCollection : ImmutableCollection { * contained in the specified [elements] collection; * or this instance if no modifications were made in the result of this operation. */ - fun retainAll(elements: Collection<@UnsafeVariance E>): PersistentCollection + public fun retainAll(elements: Collection<@UnsafeVariance E>): PersistentCollection /** * Returns an empty persistent collection. */ - fun clear(): PersistentCollection + public fun clear(): PersistentCollection /** * A generic builder of the persistent collection. Builder exposes its modification operations through the [MutableCollection] interface. @@ -98,7 +98,7 @@ public interface PersistentCollection : ImmutableCollection { * * When [build] is called the builder forgets about all owned nodes it had created. */ - interface Builder: MutableCollection { + public interface Builder: MutableCollection { /** * Returns a persistent collection with the same contents as this builder. * @@ -108,7 +108,7 @@ public interface PersistentCollection : ImmutableCollection { * - on the first call it returns the same persistent collection instance this builder was obtained from. * - on subsequent calls it returns the same previously returned persistent collection instance. */ - fun build(): PersistentCollection + public fun build(): PersistentCollection } /** @@ -116,5 +116,5 @@ public interface PersistentCollection : ImmutableCollection { * * The builder can be used to efficiently perform multiple modification operations. */ - fun builder(): Builder<@UnsafeVariance E> + public fun builder(): Builder<@UnsafeVariance E> } diff --git a/core/commonMain/src/ImmutableList.kt b/core/commonMain/src/ImmutableList.kt index ed126d4b..e42ddbc0 100644 --- a/core/commonMain/src/ImmutableList.kt +++ b/core/commonMain/src/ImmutableList.kt @@ -125,28 +125,28 @@ public interface PersistentList : ImmutableList, PersistentCollection< * * @throws IndexOutOfBoundsException if [index] is out of bounds of this list. */ - fun addAll(index: Int, c: Collection<@UnsafeVariance E>): PersistentList // = builder().apply { addAll(index, c.toList()) }.build() + public fun addAll(index: Int, c: Collection<@UnsafeVariance E>): PersistentList // = builder().apply { addAll(index, c.toList()) }.build() /** * Returns a new persistent list with the element at the specified [index] replaced with the specified [element]. * * @throws IndexOutOfBoundsException if [index] is out of bounds of this list. */ - fun set(index: Int, element: @UnsafeVariance E): PersistentList + public fun set(index: Int, element: @UnsafeVariance E): PersistentList /** * Returns a new persistent list with the specified [element] inserted at the specified [index]. * * @throws IndexOutOfBoundsException if [index] is out of bounds of this list. */ - fun add(index: Int, element: @UnsafeVariance E): PersistentList + public fun add(index: Int, element: @UnsafeVariance E): PersistentList /** * Returns a new persistent list with the element at the specified [index] removed. * * @throws IndexOutOfBoundsException if [index] is out of bounds of this list. */ - fun removeAt(index: Int): PersistentList + public fun removeAt(index: Int): PersistentList /** * A generic builder of the persistent list. Builder exposes its modification operations through the [MutableList] interface. @@ -164,7 +164,7 @@ public interface PersistentList : ImmutableList, PersistentCollection< * * When [build] is called the builder forgets about all owned nodes it had created. */ - interface Builder: MutableList, PersistentCollection.Builder { + public interface Builder: MutableList, PersistentCollection.Builder { override fun build(): PersistentList } diff --git a/core/commonMain/src/ImmutableMap.kt b/core/commonMain/src/ImmutableMap.kt index 016fa297..4b9abc59 100644 --- a/core/commonMain/src/ImmutableMap.kt +++ b/core/commonMain/src/ImmutableMap.kt @@ -47,7 +47,7 @@ public interface PersistentMap : ImmutableMap { * @return a new persistent map with the specified [value] associated with the specified [key]; * or this instance if no modifications were made in the result of this operation. */ - fun put(key: K, value: @UnsafeVariance V): PersistentMap + public fun put(key: K, value: @UnsafeVariance V): PersistentMap /** * Returns the result of removing the specified [key] and its corresponding value from this map. @@ -55,7 +55,7 @@ public interface PersistentMap : ImmutableMap { * @return a new persistent map with the specified [key] and its corresponding value removed; * or this instance if it contains no mapping for the key. */ - fun remove(key: K): PersistentMap + public fun remove(key: K): PersistentMap /** * Returns the result of removing the entry that maps the specified [key] to the specified [value]. @@ -63,7 +63,7 @@ public interface PersistentMap : ImmutableMap { * @return a new persistent map with the entry for the specified [key] and [value] removed; * or this instance if it contains no entry with the specified key and value. */ - fun remove(key: K, value: @UnsafeVariance V): PersistentMap + public fun remove(key: K, value: @UnsafeVariance V): PersistentMap /** * Returns the result of merging the specified [m] map with this map. @@ -74,12 +74,12 @@ public interface PersistentMap : ImmutableMap { * @return a new persistent map with keys and values from the specified map [m] associated; * or this instance if no modifications were made in the result of this operation. */ - fun putAll(m: Map): PersistentMap // m: Iterable> or Map or Iterable> + public fun putAll(m: Map): PersistentMap // m: Iterable> or Map or Iterable> /** * Returns an empty persistent map. */ - fun clear(): PersistentMap + public fun clear(): PersistentMap /** * A generic builder of the persistent map. Builder exposes its modification operations through the [MutableMap] interface. @@ -97,7 +97,7 @@ public interface PersistentMap : ImmutableMap { * * When [build] is called the builder forgets about all owned nodes it had created. */ - interface Builder: MutableMap { + public interface Builder: MutableMap { /** * Returns a persistent map with the same contents as this builder. * @@ -107,7 +107,7 @@ public interface PersistentMap : ImmutableMap { * - on the first call it returns the same persistent map instance this builder was obtained from. * - on subsequent calls it returns the same previously returned persistent map instance. */ - fun build(): PersistentMap + public fun build(): PersistentMap } /** @@ -115,5 +115,5 @@ public interface PersistentMap : ImmutableMap { * * The builder can be used to efficiently perform multiple modification operations. */ - fun builder(): Builder + public fun builder(): Builder } diff --git a/core/commonMain/src/ImmutableSet.kt b/core/commonMain/src/ImmutableSet.kt index 7a66609c..b53d1c2b 100644 --- a/core/commonMain/src/ImmutableSet.kt +++ b/core/commonMain/src/ImmutableSet.kt @@ -100,7 +100,7 @@ public interface PersistentSet : ImmutableSet, PersistentCollection * * When [build] is called the builder forgets about all owned nodes it had created. */ - interface Builder: MutableSet, PersistentCollection.Builder { + public interface Builder: MutableSet, PersistentCollection.Builder { override fun build(): PersistentSet } diff --git a/core/commonMain/src/extensions.kt b/core/commonMain/src/extensions.kt index b8ee2318..57d289fa 100644 --- a/core/commonMain/src/extensions.kt +++ b/core/commonMain/src/extensions.kt @@ -28,7 +28,7 @@ import kotlinx.collections.immutable.implementations.persistentOrderedSet.Persis * @return a new persistent set with the provided modifications applied; * or this instance if no modifications were made in the result of this operation. */ -inline fun PersistentSet.mutate(mutator: (MutableSet) -> Unit): PersistentSet = builder().apply(mutator).build() +public inline fun PersistentSet.mutate(mutator: (MutableSet) -> Unit): PersistentSet = builder().apply(mutator).build() /** * Returns the result of applying the provided modifications on this list. @@ -38,7 +38,7 @@ inline fun PersistentSet.mutate(mutator: (MutableSet) -> Unit): Persis * @return a new persistent list with the provided modifications applied; * or this instance if no modifications were made in the result of this operation. */ -inline fun PersistentList.mutate(mutator: (MutableList) -> Unit): PersistentList = builder().apply(mutator).build() +public inline fun PersistentList.mutate(mutator: (MutableList) -> Unit): PersistentList = builder().apply(mutator).build() /** * Returns the result of applying the provided modifications on this map. @@ -49,7 +49,7 @@ inline fun PersistentList.mutate(mutator: (MutableList) -> Unit): Pers * or this instance if no modifications were made in the result of this operation. */ @Suppress("UNCHECKED_CAST") -inline fun PersistentMap.mutate(mutator: (MutableMap) -> Unit): PersistentMap = +public inline fun PersistentMap.mutate(mutator: (MutableMap) -> Unit): PersistentMap = (this as PersistentMap).builder().apply(mutator).build() @@ -59,7 +59,7 @@ inline fun PersistentMap.mutate(mutator: (MutableMap) -> * @returns a new persistent collection with the specified [element] added; * or this instance if this collection does not support duplicates and it already contains the element. */ -inline operator fun PersistentCollection.plus(element: E): PersistentCollection = add(element) +public inline operator fun PersistentCollection.plus(element: E): PersistentCollection = add(element) /** * Returns the result of removing a single appearance of the specified [element] from this collection. @@ -67,7 +67,7 @@ inline operator fun PersistentCollection.plus(element: E): PersistentColl * @return a new persistent collection with a single appearance of the specified [element] removed; * or this instance if there is no such element in this collection. */ -inline operator fun PersistentCollection.minus(element: E): PersistentCollection = remove(element) +public inline operator fun PersistentCollection.minus(element: E): PersistentCollection = remove(element) /** @@ -76,7 +76,7 @@ inline operator fun PersistentCollection.minus(element: E): PersistentCol * @return a new persistent collection with elements of the specified [elements] collection added; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.plus(elements: Iterable): PersistentCollection +public operator fun PersistentCollection.plus(elements: Iterable): PersistentCollection = if (elements is Collection) addAll(elements) else builder().also { it.addAll(elements) }.build() /** @@ -85,7 +85,7 @@ operator fun PersistentCollection.plus(elements: Iterable): Persistent * @return a new persistent collection with elements of the specified [elements] array added; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.plus(elements: Array): PersistentCollection +public operator fun PersistentCollection.plus(elements: Array): PersistentCollection = builder().also { it.addAll(elements) }.build() /** @@ -94,7 +94,7 @@ operator fun PersistentCollection.plus(elements: Array): Persisten * @return a new persistent collection with elements of the specified [elements] sequence added; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.plus(elements: Sequence): PersistentCollection +public operator fun PersistentCollection.plus(elements: Sequence): PersistentCollection = builder().also { it.addAll(elements) }.build() @@ -106,7 +106,7 @@ operator fun PersistentCollection.plus(elements: Sequence): Persistent * contained in the specified [elements] collection removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.minus(elements: Iterable): PersistentCollection +public operator fun PersistentCollection.minus(elements: Iterable): PersistentCollection = if (elements is Collection) removeAll(elements) else builder().also { it.removeAll(elements) }.build() /** @@ -117,7 +117,7 @@ operator fun PersistentCollection.minus(elements: Iterable): Persisten * contained in the specified [elements] array removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.minus(elements: Array): PersistentCollection +public operator fun PersistentCollection.minus(elements: Array): PersistentCollection = builder().also { it.removeAll(elements) }.build() /** @@ -128,14 +128,14 @@ operator fun PersistentCollection.minus(elements: Array): Persiste * contained in the specified [elements] sequence removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentCollection.minus(elements: Sequence): PersistentCollection +public operator fun PersistentCollection.minus(elements: Sequence): PersistentCollection = builder().also { it.removeAll(elements) }.build() /** * Returns a new persistent list with the specified [element] appended. */ -inline operator fun PersistentList.plus(element: E): PersistentList = add(element) +public inline operator fun PersistentList.plus(element: E): PersistentList = add(element) /** * Returns the result of removing the first appearance of the specified [element] from this list. @@ -143,7 +143,7 @@ inline operator fun PersistentList.plus(element: E): PersistentList = * @return a new persistent list with the first appearance of the specified [element] removed; * or this instance if there is no such element in this list. */ -inline operator fun PersistentList.minus(element: E): PersistentList = remove(element) +public inline operator fun PersistentList.minus(element: E): PersistentList = remove(element) /** @@ -154,7 +154,7 @@ inline operator fun PersistentList.minus(element: E): PersistentList = * @return a new persistent list with elements of the specified [elements] collection appended; * or this instance if the specified collection is empty. */ -operator fun PersistentList.plus(elements: Iterable): PersistentList +public operator fun PersistentList.plus(elements: Iterable): PersistentList = if (elements is Collection) addAll(elements) else mutate { it.addAll(elements) } /** @@ -165,7 +165,7 @@ operator fun PersistentList.plus(elements: Iterable): PersistentList PersistentList.plus(elements: Array): PersistentList +public operator fun PersistentList.plus(elements: Array): PersistentList = mutate { it.addAll(elements) } /** @@ -176,7 +176,7 @@ operator fun PersistentList.plus(elements: Array): PersistentList< * @return a new persistent list with elements of the specified [elements] sequence appended; * or this instance if the specified sequence is empty. */ -operator fun PersistentList.plus(elements: Sequence): PersistentList +public operator fun PersistentList.plus(elements: Sequence): PersistentList = mutate { it.addAll(elements) } @@ -188,7 +188,7 @@ operator fun PersistentList.plus(elements: Sequence): PersistentList PersistentList.minus(elements: Iterable): PersistentList +public operator fun PersistentList.minus(elements: Iterable): PersistentList = if (elements is Collection) removeAll(elements) else mutate { it.removeAll(elements) } /** @@ -199,7 +199,7 @@ operator fun PersistentList.minus(elements: Iterable): PersistentList< * contained in the specified [elements] array removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentList.minus(elements: Array): PersistentList +public operator fun PersistentList.minus(elements: Array): PersistentList = mutate { it.removeAll(elements) } /** @@ -210,7 +210,7 @@ operator fun PersistentList.minus(elements: Array): PersistentList * contained in the specified [elements] sequence removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentList.minus(elements: Sequence): PersistentList +public operator fun PersistentList.minus(elements: Sequence): PersistentList = mutate { it.removeAll(elements) } @@ -220,7 +220,7 @@ operator fun PersistentList.minus(elements: Sequence): PersistentList< * @return a new persistent set with the specified [element] added; * or this instance if it already contains the element. */ -inline operator fun PersistentSet.plus(element: E): PersistentSet = add(element) +public inline operator fun PersistentSet.plus(element: E): PersistentSet = add(element) /** * Returns the result of removing the specified [element] from this set. @@ -228,7 +228,7 @@ inline operator fun PersistentSet.plus(element: E): PersistentSet = ad * @return a new persistent set with the specified [element] removed; * or this instance if there is no such element in this set. */ -inline operator fun PersistentSet.minus(element: E): PersistentSet = remove(element) +public inline operator fun PersistentSet.minus(element: E): PersistentSet = remove(element) /** @@ -237,7 +237,7 @@ inline operator fun PersistentSet.minus(element: E): PersistentSet = r * @return a new persistent set with elements of the specified [elements] collection added; * or this instance if it already contains every element of the specified collection. */ -operator fun PersistentSet.plus(elements: Iterable): PersistentSet +public operator fun PersistentSet.plus(elements: Iterable): PersistentSet = if (elements is Collection) addAll(elements) else mutate { it.addAll(elements) } /** @@ -246,7 +246,7 @@ operator fun PersistentSet.plus(elements: Iterable): PersistentSet * @return a new persistent set with elements of the specified [elements] array added; * or this instance if it already contains every element of the specified array. */ -operator fun PersistentSet.plus(elements: Array): PersistentSet +public operator fun PersistentSet.plus(elements: Array): PersistentSet = mutate { it.addAll(elements) } /** @@ -255,7 +255,7 @@ operator fun PersistentSet.plus(elements: Array): PersistentSet * @return a new persistent set with elements of the specified [elements] sequence added; * or this instance if it already contains every element of the specified sequence. */ -operator fun PersistentSet.plus(elements: Sequence): PersistentSet +public operator fun PersistentSet.plus(elements: Sequence): PersistentSet = mutate { it.addAll(elements) } @@ -267,7 +267,7 @@ operator fun PersistentSet.plus(elements: Sequence): PersistentSet * contained in the specified [elements] collection removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentSet.minus(elements: Iterable): PersistentSet +public operator fun PersistentSet.minus(elements: Iterable): PersistentSet = if (elements is Collection) removeAll(elements) else mutate { it.removeAll(elements) } /** @@ -278,7 +278,7 @@ operator fun PersistentSet.minus(elements: Iterable): PersistentSet * contained in the specified [elements] array removed; * or this instance if no modifications were made in the result of this operation. */ -operator fun PersistentSet.minus(elements: Array): PersistentSet +public operator fun PersistentSet.minus(elements: Array): PersistentSet = mutate { it.removeAll(elements) } /** @@ -289,7 +289,7 @@ operator fun PersistentSet.minus(elements: Array): PersistentSet PersistentSet.minus(elements: Sequence): PersistentSet +public operator fun PersistentSet.minus(elements: Sequence): PersistentSet = mutate { it.removeAll(elements) } /** @@ -300,7 +300,7 @@ operator fun PersistentSet.minus(elements: Sequence): PersistentSet * contained in the specified [elements] collection; * or this instance if no modifications were made in the result of this operation. */ -infix fun PersistentSet.intersect(elements: Iterable): PersistentSet +public infix fun PersistentSet.intersect(elements: Iterable): PersistentSet = if (elements is Collection) retainAll(elements) else mutate { it.retainAll(elements) } /** @@ -310,7 +310,7 @@ infix fun PersistentSet.intersect(elements: Iterable): PersistentSet PersistentCollection.intersect(elements: Iterable): PersistentSet +public infix fun PersistentCollection.intersect(elements: Iterable): PersistentSet = this.toPersistentSet().intersect(elements) /** @@ -323,7 +323,7 @@ infix fun PersistentCollection.intersect(elements: Iterable): Persiste * or this instance if no modifications were made in the result of this operation. */ @Suppress("UNCHECKED_CAST") -inline operator fun PersistentMap.plus(pair: Pair): PersistentMap +public inline operator fun PersistentMap.plus(pair: Pair): PersistentMap = (this as PersistentMap).put(pair.first, pair.second) /** @@ -332,7 +332,7 @@ inline operator fun PersistentMap.plus(pair: Pair): Persi * @return a new persistent map with entries from the specified key-value pairs added; * or this instance if no modifications were made in the result of this operation. */ -inline operator fun PersistentMap.plus(pairs: Iterable>): PersistentMap = putAll(pairs) +public inline operator fun PersistentMap.plus(pairs: Iterable>): PersistentMap = putAll(pairs) /** * Returns the result of replacing or adding entries to this map from the specified key-value pairs. @@ -340,7 +340,7 @@ inline operator fun PersistentMap.plus(pairs: Iterable PersistentMap.plus(pairs: Array>): PersistentMap = putAll(pairs) +public inline operator fun PersistentMap.plus(pairs: Array>): PersistentMap = putAll(pairs) /** * Returns the result of replacing or adding entries to this map from the specified key-value pairs. @@ -348,7 +348,7 @@ inline operator fun PersistentMap.plus(pairs: Array PersistentMap.plus(pairs: Sequence>): PersistentMap = putAll(pairs) +public inline operator fun PersistentMap.plus(pairs: Sequence>): PersistentMap = putAll(pairs) /** * Returns the result of merging the specified [map] with this map. @@ -359,7 +359,7 @@ inline operator fun PersistentMap.plus(pairs: Sequence PersistentMap.plus(map: Map): PersistentMap = putAll(map) +public inline operator fun PersistentMap.plus(map: Map): PersistentMap = putAll(map) /** @@ -444,12 +444,12 @@ public operator fun PersistentMap.minus(keys: Sequence): Per /** * Returns a new persistent list of the specified elements. */ -fun persistentListOf(vararg elements: E): PersistentList = persistentVectorOf().addAll(elements.asList()) +public fun persistentListOf(vararg elements: E): PersistentList = persistentVectorOf().addAll(elements.asList()) /** * Returns an empty persistent list. */ -fun persistentListOf(): PersistentList = persistentVectorOf() +public fun persistentListOf(): PersistentList = persistentVectorOf() /** @@ -457,12 +457,12 @@ fun persistentListOf(): PersistentList = persistentVectorOf() * * Elements of the returned set are iterated in the order they were specified. */ -fun persistentSetOf(vararg elements: E): PersistentSet = PersistentOrderedSet.emptyOf().addAll(elements.asList()) +public fun persistentSetOf(vararg elements: E): PersistentSet = PersistentOrderedSet.emptyOf().addAll(elements.asList()) /** * Returns an empty persistent set. */ -fun persistentSetOf(): PersistentSet = PersistentOrderedSet.emptyOf() +public fun persistentSetOf(): PersistentSet = PersistentOrderedSet.emptyOf() /** @@ -470,12 +470,12 @@ fun persistentSetOf(): PersistentSet = PersistentOrderedSet.emptyOf() * * Order of the elements in the returned set is unspecified. */ -fun persistentHashSetOf(vararg elements: E): PersistentSet = PersistentHashSet.emptyOf().addAll(elements.asList()) +public fun persistentHashSetOf(vararg elements: E): PersistentSet = PersistentHashSet.emptyOf().addAll(elements.asList()) /** * Returns an empty persistent set. */ -fun persistentHashSetOf(): PersistentSet = PersistentHashSet.emptyOf() +public fun persistentHashSetOf(): PersistentSet = PersistentHashSet.emptyOf() /** @@ -486,12 +486,12 @@ fun persistentHashSetOf(): PersistentSet = PersistentHashSet.emptyOf() * * Entries of the map are iterated in the order they were specified. */ -fun persistentMapOf(vararg pairs: Pair): PersistentMap = PersistentOrderedMap.emptyOf().mutate { it += pairs } +public fun persistentMapOf(vararg pairs: Pair): PersistentMap = PersistentOrderedMap.emptyOf().mutate { it += pairs } /** * Returns an empty persistent map. */ -fun persistentMapOf(): PersistentMap = PersistentOrderedMap.emptyOf() +public fun persistentMapOf(): PersistentMap = PersistentOrderedMap.emptyOf() /** @@ -502,25 +502,25 @@ fun persistentMapOf(): PersistentMap = PersistentOrderedMap.emptyOf * * Order of the entries in the returned map is unspecified. */ -fun persistentHashMapOf(vararg pairs: Pair): PersistentMap = PersistentHashMap.emptyOf().mutate { it += pairs } +public fun persistentHashMapOf(vararg pairs: Pair): PersistentMap = PersistentHashMap.emptyOf().mutate { it += pairs } /** * Returns an empty persistent map. */ -fun persistentHashMapOf(): PersistentMap = PersistentHashMap.emptyOf() +public fun persistentHashMapOf(): PersistentMap = PersistentHashMap.emptyOf() /** * Returns a new persistent list of the specified elements. */ @Deprecated("Use persistentListOf instead.", ReplaceWith("persistentListOf(*elements)")) -fun immutableListOf(vararg elements: E): PersistentList = persistentListOf(*elements) +public fun immutableListOf(vararg elements: E): PersistentList = persistentListOf(*elements) /** * Returns an empty persistent list. */ @Deprecated("Use persistentListOf instead.", ReplaceWith("persistentListOf()")) -fun immutableListOf(): PersistentList = persistentListOf() +public fun immutableListOf(): PersistentList = persistentListOf() /** @@ -529,13 +529,13 @@ fun immutableListOf(): PersistentList = persistentListOf() * Elements of the returned set are iterated in the order they were specified. */ @Deprecated("Use persistentSetOf instead.", ReplaceWith("persistentSetOf(*elements)")) -fun immutableSetOf(vararg elements: E): PersistentSet = persistentSetOf(*elements) +public fun immutableSetOf(vararg elements: E): PersistentSet = persistentSetOf(*elements) /** * Returns an empty persistent set. */ @Deprecated("Use persistentSetOf instead.", ReplaceWith("persistentSetOf()")) -fun immutableSetOf(): PersistentSet = persistentSetOf() +public fun immutableSetOf(): PersistentSet = persistentSetOf() /** @@ -544,7 +544,7 @@ fun immutableSetOf(): PersistentSet = persistentSetOf() * Order of the elements in the returned set is unspecified. */ @Deprecated("Use persistentHashSetOf instead.", ReplaceWith("persistentHashSetOf(*elements)")) -fun immutableHashSetOf(vararg elements: E): PersistentSet = persistentHashSetOf(*elements) +public fun immutableHashSetOf(vararg elements: E): PersistentSet = persistentHashSetOf(*elements) /** @@ -556,7 +556,7 @@ fun immutableHashSetOf(vararg elements: E): PersistentSet = persistentHas * Entries of the map are iterated in the order they were specified. */ @Deprecated("Use persistentMapOf instead.", ReplaceWith("persistentMapOf(*pairs)")) -fun immutableMapOf(vararg pairs: Pair): PersistentMap = persistentMapOf(*pairs) +public fun immutableMapOf(vararg pairs: Pair): PersistentMap = persistentMapOf(*pairs) /** * Returns a new persistent map with the specified contents, given as a list of pairs @@ -567,7 +567,7 @@ fun immutableMapOf(vararg pairs: Pair): PersistentMap = persi * Order of the entries in the returned map is unspecified. */ @Deprecated("Use persistentHashMapOf instead.", ReplaceWith("persistentHashMapOf(*pairs)")) -fun immutableHashMapOf(vararg pairs: Pair): PersistentMap = persistentHashMapOf(*pairs) +public fun immutableHashMapOf(vararg pairs: Pair): PersistentMap = persistentHashMapOf(*pairs) /** @@ -575,24 +575,24 @@ fun immutableHashMapOf(vararg pairs: Pair): PersistentMap = p * * If the receiver is already an immutable list, returns it as is. */ -fun Iterable.toImmutableList(): ImmutableList = +public fun Iterable.toImmutableList(): ImmutableList = this as? ImmutableList ?: this.toPersistentList() /** * Returns an immutable list containing all elements of this array. */ -fun Array.toImmutableList(): ImmutableList = toPersistentList() +public fun Array.toImmutableList(): ImmutableList = toPersistentList() /** * Returns an immutable list containing all elements of this sequence. */ -fun Sequence.toImmutableList(): ImmutableList = toPersistentList() +public fun Sequence.toImmutableList(): ImmutableList = toPersistentList() /** * Returns an immutable list containing all characters. */ -fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() +public fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() /** @@ -601,7 +601,7 @@ fun CharSequence.toImmutableList(): ImmutableList = toPersistentList() * If the receiver is already a persistent list, returns it as is. * If the receiver is a persistent list builder, calls `build` on it and returns the result. */ -fun Iterable.toPersistentList(): PersistentList = +public fun Iterable.toPersistentList(): PersistentList = this as? PersistentList ?: (this as? PersistentList.Builder)?.build() ?: persistentListOf() + this @@ -609,17 +609,17 @@ fun Iterable.toPersistentList(): PersistentList = /** * Returns a persistent list containing all elements of this array. */ -fun Array.toPersistentList(): PersistentList = persistentListOf() + this +public fun Array.toPersistentList(): PersistentList = persistentListOf() + this /** * Returns a persistent list containing all elements of this sequence. */ -fun Sequence.toPersistentList(): PersistentList = persistentListOf() + this +public fun Sequence.toPersistentList(): PersistentList = persistentListOf() + this /** * Returns a persistent list containing all characters. */ -fun CharSequence.toPersistentList(): PersistentList = +public fun CharSequence.toPersistentList(): PersistentList = persistentListOf().mutate { this.toCollection(it) } @@ -630,7 +630,7 @@ fun CharSequence.toPersistentList(): PersistentList = * * Elements of the returned set are iterated in the same order as in this collection. */ -fun Iterable.toImmutableSet(): ImmutableSet = +public fun Iterable.toImmutableSet(): ImmutableSet = this as? ImmutableSet ?: (this as? PersistentSet.Builder)?.build() ?: persistentSetOf() + this @@ -640,21 +640,21 @@ fun Iterable.toImmutableSet(): ImmutableSet = * * Elements of the returned set are iterated in the same order as in this array. */ -fun Array.toImmutableSet(): ImmutableSet = toPersistentSet() +public fun Array.toImmutableSet(): ImmutableSet = toPersistentSet() /** * Returns an immutable set of all elements of this sequence. * * Elements of the returned set are iterated in the same order as in this sequence. */ -fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() +public fun Sequence.toImmutableSet(): ImmutableSet = toPersistentSet() /** * Returns an immutable set of all characters. * * Elements of the returned set are iterated in the same order as in this char sequence. */ -fun CharSequence.toImmutableSet(): PersistentSet = toPersistentSet() +public fun CharSequence.toImmutableSet(): PersistentSet = toPersistentSet() /** @@ -665,7 +665,7 @@ fun CharSequence.toImmutableSet(): PersistentSet = toPersistentSet() * * Elements of the returned set are iterated in the same order as in this collection. */ -fun Iterable.toPersistentSet(): PersistentSet = +public fun Iterable.toPersistentSet(): PersistentSet = this as? PersistentOrderedSet ?: (this as? PersistentOrderedSetBuilder)?.build() ?: PersistentOrderedSet.emptyOf() + this @@ -675,21 +675,21 @@ fun Iterable.toPersistentSet(): PersistentSet = * * Elements of the returned set are iterated in the same order as in this array. */ -fun Array.toPersistentSet(): PersistentSet = persistentSetOf() + this +public fun Array.toPersistentSet(): PersistentSet = persistentSetOf() + this /** * Returns a persistent set of all elements of this sequence. * * Elements of the returned set are iterated in the same order as in this sequence. */ -fun Sequence.toPersistentSet(): PersistentSet = persistentSetOf() + this +public fun Sequence.toPersistentSet(): PersistentSet = persistentSetOf() + this /** * Returns a persistent set of all characters. * * Elements of the returned set are iterated in the same order as in this char sequence. */ -fun CharSequence.toPersistentSet(): PersistentSet = +public fun CharSequence.toPersistentSet(): PersistentSet = persistentSetOf().mutate { this.toCollection(it) } @@ -701,7 +701,7 @@ fun CharSequence.toPersistentSet(): PersistentSet = * * Order of the elements in the returned set is unspecified. */ -fun Iterable.toPersistentHashSet(): PersistentSet +public fun Iterable.toPersistentHashSet(): PersistentSet = this as? PersistentHashSet ?: (this as? PersistentHashSetBuilder)?.build() ?: PersistentHashSet.emptyOf() + this @@ -711,21 +711,21 @@ fun Iterable.toPersistentHashSet(): PersistentSet * * Order of the elements in the returned set is unspecified. */ -fun Array.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this +public fun Array.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this /** * Returns a persistent set of all elements of this sequence. * * Order of the elements in the returned set is unspecified. */ -fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this +public fun Sequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf() + this /** * Returns a persistent set of all characters. * * Order of the elements in the returned set is unspecified. */ -fun CharSequence.toPersistentHashSet(): PersistentSet = +public fun CharSequence.toPersistentHashSet(): PersistentSet = persistentHashSetOf().mutate { this.toCollection(it) } @@ -736,7 +736,7 @@ fun CharSequence.toPersistentHashSet(): PersistentSet = * * Entries of the returned map are iterated in the same order as in this map. */ -fun Map.toImmutableMap(): ImmutableMap +public fun Map.toImmutableMap(): ImmutableMap = this as? ImmutableMap ?: (this as? PersistentMap.Builder)?.build() ?: persistentMapOf().putAll(this) @@ -749,7 +749,7 @@ fun Map.toImmutableMap(): ImmutableMap * * Entries of the returned map are iterated in the same order as in this map. */ -fun Map.toPersistentMap(): PersistentMap +public fun Map.toPersistentMap(): PersistentMap = this as? PersistentOrderedMap ?: (this as? PersistentOrderedMapBuilder)?.build() ?: PersistentOrderedMap.emptyOf().putAll(this) @@ -762,7 +762,7 @@ fun Map.toPersistentMap(): PersistentMap * * Order of the entries in the returned map is unspecified. */ -fun Map.toPersistentHashMap(): PersistentMap +public fun Map.toPersistentHashMap(): PersistentMap = this as? PersistentHashMap ?: (this as? PersistentHashMapBuilder)?.build() ?: PersistentHashMap.emptyOf().putAll(this) diff --git a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt index 37af4bce..e018fe4e 100644 --- a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt +++ b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate -abstract class AbstractPersistentList : PersistentList, AbstractList() { +public abstract class AbstractPersistentList : PersistentList, AbstractList() { override fun subList(fromIndex: Int, toIndex: Int): ImmutableList { return super.subList(fromIndex, toIndex) } From a4b9fb70166891d133723ac633358cb6e208ee69 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 12 Jan 2024 00:33:22 +0800 Subject: [PATCH 126/162] Remove redundant public modifiers in test source sets --- .../src/contract/CollectionBehaviors.kt | 16 ++++++++-------- core/commonTest/src/contract/ComparisonDSL.kt | 16 ++++++++-------- core/commonTest/src/testUtils.kt | 8 ++++---- core/jsTest/src/testUtilsJs.kt | 4 ++-- core/jvmTest/src/testUtilsJvm.kt | 4 ++-- core/nativeTest/src/testUtilsNative.kt | 4 ++-- core/wasmTest/src/testUtilsJs.kt | 4 ++-- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/core/commonTest/src/contract/CollectionBehaviors.kt b/core/commonTest/src/contract/CollectionBehaviors.kt index 83de7d89..96c37ddf 100644 --- a/core/commonTest/src/contract/CollectionBehaviors.kt +++ b/core/commonTest/src/contract/CollectionBehaviors.kt @@ -5,7 +5,7 @@ package tests.contract -public fun CompareContext>.listBehavior() { +fun CompareContext>.listBehavior() { equalityBehavior() collectionBehavior() compareProperty( { listIterator() }, { listIteratorBehavior() }) @@ -28,7 +28,7 @@ public fun CompareContext>.listBehavior() { propertyEquals { subList(0, size) } } -public fun CompareContext>.listIteratorBehavior() { +fun CompareContext>.listIteratorBehavior() { listIteratorProperties() while (expected.hasNext()) { @@ -44,14 +44,14 @@ public fun CompareContext>.listIteratorBehavior() { propertyFails { previous() } } -public fun CompareContext>.listIteratorProperties() { +fun CompareContext>.listIteratorProperties() { propertyEquals { hasNext() } propertyEquals { hasPrevious() } propertyEquals { nextIndex() } propertyEquals { previousIndex() } } -public fun CompareContext>.iteratorBehavior() { +fun CompareContext>.iteratorBehavior() { propertyEquals { hasNext() } while (expected.hasNext()) { @@ -61,7 +61,7 @@ public fun CompareContext>.iteratorBehavior() { propertyFails { next() } } -public fun CompareContext>.setBehavior(objectName: String = "", ordered: Boolean) { +fun CompareContext>.setBehavior(objectName: String = "", ordered: Boolean) { equalityBehavior(objectName, ordered) collectionBehavior(objectName, ordered) @@ -72,7 +72,7 @@ public fun CompareContext>.setBehavior(objectName: String = "", order -public fun CompareContext>.mapBehavior(ordered: Boolean) { +fun CompareContext>.mapBehavior(ordered: Boolean) { propertyEquals { size } propertyEquals { isEmpty() } equalityBehavior(ordered = ordered) @@ -92,7 +92,7 @@ public fun CompareContext>.mapBehavior(ordered: Boolean) { } -public fun CompareContext.equalityBehavior(objectName: String = "", ordered: Boolean = true) { +fun CompareContext.equalityBehavior(objectName: String = "", ordered: Boolean = true) { val prefix = objectName + if (objectName.isNotEmpty()) "." else "" equals(objectName) propertyEquals(prefix + "hashCode") { hashCode() } @@ -101,7 +101,7 @@ public fun CompareContext.equalityBehavior(objectName: String = "", order } -public fun CompareContext>.collectionBehavior(objectName: String = "", ordered: Boolean = true) { +fun CompareContext>.collectionBehavior(objectName: String = "", ordered: Boolean = true) { val prefix = objectName + if (objectName.isNotEmpty()) "." else "" propertyEquals (prefix + "size") { size } propertyEquals (prefix + "isEmpty") { isEmpty() } diff --git a/core/commonTest/src/contract/ComparisonDSL.kt b/core/commonTest/src/contract/ComparisonDSL.kt index 25186ef4..99eb0146 100644 --- a/core/commonTest/src/contract/ComparisonDSL.kt +++ b/core/commonTest/src/contract/ComparisonDSL.kt @@ -8,22 +8,22 @@ package tests.contract import tests.assertTypeEquals import kotlin.test.* -public fun compare(expected: T, actual: T, block: CompareContext.() -> Unit) { +fun compare(expected: T, actual: T, block: CompareContext.() -> Unit) { CompareContext(expected, actual).block() } -public class CompareContext(public val expected: T, public val actual: T) { +class CompareContext(val expected: T, val actual: T) { - public fun equals(message: String = "") { + fun equals(message: String = "") { assertEquals(expected, actual, message) } - public fun

propertyEquals(message: String = "", getter: T.() -> P) { + fun

propertyEquals(message: String = "", getter: T.() -> P) { assertEquals(expected.getter(), actual.getter(), message) } - public fun propertyFails(getter: T.() -> Unit) { assertFailEquals({expected.getter()}, {actual.getter()}) } - public inline fun propertyFailsWith(noinline getter: T.() -> Unit) = propertyFailsWith({ it is E }, getter) - public fun propertyFailsWith(exceptionPredicate: (Throwable) -> Boolean, getter: T.() -> Unit) { assertFailEquals({expected.getter()}, {actual.getter()}, exceptionPredicate) } - public fun

compareProperty(getter: T.() -> P, block: CompareContext

.() -> Unit) { + fun propertyFails(getter: T.() -> Unit) { assertFailEquals({expected.getter()}, {actual.getter()}) } + inline fun propertyFailsWith(noinline getter: T.() -> Unit) = propertyFailsWith({ it is E }, getter) + fun propertyFailsWith(exceptionPredicate: (Throwable) -> Boolean, getter: T.() -> Unit) { assertFailEquals({expected.getter()}, {actual.getter()}, exceptionPredicate) } + fun

compareProperty(getter: T.() -> P, block: CompareContext

.() -> Unit) { compare(expected.getter(), actual.getter(), block) } diff --git a/core/commonTest/src/testUtils.kt b/core/commonTest/src/testUtils.kt index 46f31e28..7683b974 100644 --- a/core/commonTest/src/testUtils.kt +++ b/core/commonTest/src/testUtils.kt @@ -20,17 +20,17 @@ internal fun MutableMap.remove(key: K, value: V): Boolean = false } -public expect fun assertTypeEquals(expected: Any?, actual: Any?) +expect fun assertTypeEquals(expected: Any?, actual: Any?) -public enum class TestPlatform { +enum class TestPlatform { JVM, JS, Native, Wasm, } -public expect val currentPlatform: TestPlatform +expect val currentPlatform: TestPlatform -public inline fun testOn(platform: TestPlatform, action: () -> Unit) { +inline fun testOn(platform: TestPlatform, action: () -> Unit) { if (platform == currentPlatform) action() } diff --git a/core/jsTest/src/testUtilsJs.kt b/core/jsTest/src/testUtilsJs.kt index 3aa86c2a..d1117311 100644 --- a/core/jsTest/src/testUtilsJs.kt +++ b/core/jsTest/src/testUtilsJs.kt @@ -7,11 +7,11 @@ package tests import kotlin.test.assertEquals -public actual fun assertTypeEquals(expected: Any?, actual: Any?) { +actual fun assertTypeEquals(expected: Any?, actual: Any?) { assertEquals(expected?.let { it::class.js }, actual?.let { it::class.js }) } -public actual val currentPlatform: TestPlatform get() = TestPlatform.JS +actual val currentPlatform: TestPlatform get() = TestPlatform.JS actual object NForAlgorithmComplexity { actual val O_N: Int = 500_000 diff --git a/core/jvmTest/src/testUtilsJvm.kt b/core/jvmTest/src/testUtilsJvm.kt index 7292c0d8..0a79869f 100644 --- a/core/jvmTest/src/testUtilsJvm.kt +++ b/core/jvmTest/src/testUtilsJvm.kt @@ -7,11 +7,11 @@ package tests import kotlin.test.assertEquals -public actual fun assertTypeEquals(expected: Any?, actual: Any?) { +actual fun assertTypeEquals(expected: Any?, actual: Any?) { assertEquals(expected?.javaClass, actual?.javaClass) } -public actual val currentPlatform: TestPlatform get() = TestPlatform.JVM +actual val currentPlatform: TestPlatform get() = TestPlatform.JVM actual object NForAlgorithmComplexity { actual val O_N: Int = 1_000_000 diff --git a/core/nativeTest/src/testUtilsNative.kt b/core/nativeTest/src/testUtilsNative.kt index 04157b50..a64d8959 100644 --- a/core/nativeTest/src/testUtilsNative.kt +++ b/core/nativeTest/src/testUtilsNative.kt @@ -7,7 +7,7 @@ package tests import kotlin.test.assertTrue -public actual fun assertTypeEquals(expected: Any?, actual: Any?) { +actual fun assertTypeEquals(expected: Any?, actual: Any?) { if (expected != null && actual != null) { assertTrue(expected::class.isInstance(actual) || actual::class.isInstance(expected), "Expected: $expected, Actual: $actual") @@ -16,7 +16,7 @@ public actual fun assertTypeEquals(expected: Any?, actual: Any?) { } } -public actual val currentPlatform: TestPlatform get() = TestPlatform.Native +actual val currentPlatform: TestPlatform get() = TestPlatform.Native actual object NForAlgorithmComplexity { actual val O_N: Int = 100_000 diff --git a/core/wasmTest/src/testUtilsJs.kt b/core/wasmTest/src/testUtilsJs.kt index c0ec59d6..ea9d1397 100644 --- a/core/wasmTest/src/testUtilsJs.kt +++ b/core/wasmTest/src/testUtilsJs.kt @@ -7,7 +7,7 @@ package tests import kotlin.test.assertTrue -public actual fun assertTypeEquals(expected: Any?, actual: Any?) { +actual fun assertTypeEquals(expected: Any?, actual: Any?) { if (expected != null && actual != null) { assertTrue(expected::class.isInstance(actual) || actual::class.isInstance(expected), "Expected: $expected, Actual: $actual") @@ -16,7 +16,7 @@ public actual fun assertTypeEquals(expected: Any?, actual: Any?) { } } -public actual val currentPlatform: TestPlatform get() = TestPlatform.Wasm +actual val currentPlatform: TestPlatform get() = TestPlatform.Wasm actual object NForAlgorithmComplexity { actual val O_N: Int = 500_000 From 89e63fba66d39ff05ca4d39b94233ef706c4ac25 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Sat, 16 Dec 2023 08:38:19 +0200 Subject: [PATCH 127/162] Update README.md and CHANGELOG.md for 0.3.7 release --- CHANGELOG.md | 5 +++++ README.md | 9 ++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb6fcbf1..a863a9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 0.3.7 + +- Upgrade Kotlin version up to 1.9.21 +- Support wasmJs and wasmWasi targets + ## 0.3.6 - Upgrade Kotlin version up to 1.9.0 diff --git a/README.md b/README.md index 53c2cdf2..96ec1a42 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,10 @@ [![GitHub license](https://img.shields.io/github/license/kotlin/kotlinx.collections.immutable)](LICENSE.txt) [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-collections-immutable) -[![IR](https://img.shields.io/badge/Kotlin%2FJS-IR%20supported-yellow)](https://kotl.in/jsirsupported) Immutable collection interfaces and implementation prototypes for Kotlin. -This is a multiplatform library providing implementations for `jvm`, `js` ([IR](https://kotlinlang.org/docs/js-ir-compiler.html)), +This is a multiplatform library providing implementations for `jvm`, `js`, `wasmJs`, `wasmWasi` and all [targets supported by the Kotlin/Native compiler](https://kotlinlang.org/docs/native-target-support.html). For further details see the [proposal](proposal.md). @@ -119,7 +118,7 @@ collection.mutate { some_actions_on(it) } The library is published to Maven Central repository. -The library depends on the Kotlin Standard Library of the version at least `1.9.0`. +The library depends on the Kotlin Standard Library of the version at least `1.9.21`. ### Gradle @@ -138,7 +137,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7") } } } @@ -154,7 +153,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.6 + 0.3.7 ``` From 79ef892c292dcb6b8842b45733601de7938a7156 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 15 Dec 2023 16:11:23 +0200 Subject: [PATCH 128/162] Upgrade kotlinx-benchmark version to 0.4.10 --- benchmarks/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 13be2d98..99aa30bc 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.jvm.tasks.Jar plugins { id("kotlin-multiplatform") - id("org.jetbrains.kotlinx.benchmark") version "0.4.9" + id("org.jetbrains.kotlinx.benchmark") version "0.4.10" } @@ -40,7 +40,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.9") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10") implementation(project(":kotlinx-collections-immutable")) } } From 7fb0d74ea87d1f52e8d30d39d4df066a57f51e50 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Sat, 16 Dec 2023 08:29:21 +0200 Subject: [PATCH 129/162] Add wasmJs target in benchmarks --- benchmarks/build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 99aa30bc..da19e723 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -30,7 +30,11 @@ kotlin { } } - //TODO: Add wasm benchmarks as soon as wasmJs/wasmWasi will be published + wasmJs { + nodejs() + } + + //TODO: Add wasmWasi benchmarks as soon as kx-benchmark supports the target sourceSets.all { kotlin.setSrcDirs(listOf("$name/src")) @@ -94,6 +98,7 @@ benchmark { jmhVersion = "1.21" } register("js") + register("wasmJs") register("macosX64") register("linuxX64") register("mingwX64") From 7353626698c05eefb10f0b572c363051b05e592d Mon Sep 17 00:00:00 2001 From: Igor Yakovlev Date: Mon, 19 Feb 2024 18:16:13 +0100 Subject: [PATCH 130/162] Wasm invalid gradle configuration fix --- core/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 35262988..e3f2e6f7 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -121,8 +121,10 @@ kotlin { } val wasmMain by creating { + dependsOn(commonMain) } val wasmTest by creating { + dependsOn(commonTest) } val wasmJsMain by getting { From 16bd411980b11d5cb0e10c347642d4bb57d48545 Mon Sep 17 00:00:00 2001 From: Tatiana Bogdanova Date: Mon, 26 Feb 2024 18:32:20 +0100 Subject: [PATCH 131/162] Kotlin K2 migration: add generic type to emptyList See KT-66196. --- core/commonTest/src/contract/list/ImmutableListTest.kt | 2 +- core/commonTest/src/contract/set/ImmutableSetTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/commonTest/src/contract/list/ImmutableListTest.kt b/core/commonTest/src/contract/list/ImmutableListTest.kt index 9bfa4f51..fc14c831 100644 --- a/core/commonTest/src/contract/list/ImmutableListTest.kt +++ b/core/commonTest/src/contract/list/ImmutableListTest.kt @@ -25,7 +25,7 @@ class ImmutableListTest { assertFailsWith { empty1.iterator().next() } - compareLists(emptyList(), empty1) + compareLists(emptyList(), empty1) } diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index 5f1e422d..4e8ef6fd 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -239,7 +239,7 @@ abstract class ImmutableSetTestBase { assertEquals>(setOf(), empty1) assertTrue(empty1 === empty2) - compareSets(emptySet(), empty1) + compareSets(emptySet(), empty1) } @Test fun ofElements() { From a1d250a3a4e761eda4f6f8cbf90e326200ad2e3c Mon Sep 17 00:00:00 2001 From: Matthew Pope Date: Tue, 12 Mar 2024 13:05:58 -0700 Subject: [PATCH 132/162] Avoid creating new PersistentList instance when adding all elements of an empty collection --- .../immutableList/SmallPersistentVector.kt | 2 ++ core/commonTest/src/contract/list/ImmutableListTest.kt | 10 ++++++++++ core/commonTest/src/contract/map/ImmutableMapTest.kt | 6 ++++++ core/commonTest/src/contract/set/ImmutableSetTest.kt | 9 +++++++++ 4 files changed, 27 insertions(+) diff --git a/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt b/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt index a8f72696..5f9c92de 100644 --- a/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt +++ b/core/commonMain/src/implementations/immutableList/SmallPersistentVector.kt @@ -36,6 +36,7 @@ internal class SmallPersistentVector(private val buffer: Array) : Immut } override fun addAll(elements: Collection): PersistentList { + if (elements.isEmpty()) return this if (size + elements.size <= MAX_BUFFER_SIZE) { val newBuffer = buffer.copyOf(size + elements.size) // TODO: investigate performance of elements.toArray + copyInto @@ -80,6 +81,7 @@ internal class SmallPersistentVector(private val buffer: Array) : Immut override fun addAll(index: Int, c: Collection): PersistentList { checkPositionIndex(index, size) + if (c.isEmpty()) return this if (size + c.size <= MAX_BUFFER_SIZE) { val newBuffer = bufferOfSize(size + c.size) buffer.copyInto(newBuffer, endIndex = index) diff --git a/core/commonTest/src/contract/list/ImmutableListTest.kt b/core/commonTest/src/contract/list/ImmutableListTest.kt index fc14c831..76883a04 100644 --- a/core/commonTest/src/contract/list/ImmutableListTest.kt +++ b/core/commonTest/src/contract/list/ImmutableListTest.kt @@ -68,6 +68,13 @@ class ImmutableListTest { compareLists(list, immList) } + @Test fun emptyListToPersistentList() { + val empty = emptyList() + val emptyPersistent = empty.toPersistentList() + + assertSame(emptyPersistent, empty.toPersistentList()) + } + @Test fun addElements() { var list = persistentListOf() list = list.add("x") @@ -194,6 +201,9 @@ class ImmutableListTest { testNoOperation({ remove('d') }, { remove('d') }) testNoOperation({ removeAll(listOf('d', 'e')) }, { removeAll(listOf('d', 'e')) }) testNoOperation({ removeAll { it.isUpperCase() } }, { removeAll { it.isUpperCase() } }) + testNoOperation({ removeAll(emptyList()) }, { removeAll(emptyList())}) + testNoOperation({ addAll(emptyList()) }, { addAll(emptyList())}) + testNoOperation({ addAll(2, emptyList()) }, { addAll(2, emptyList())}) } } diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 43c895e3..599f2c1c 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -191,6 +191,12 @@ abstract class ImmutableMapTest { assertEquals>(map, immMap) // problem } + @Test fun emptyMapToPersistentMap() { + val empty = emptyMap() + val emptyPersistentMap = empty.toPersistentMap() + + assertSame(emptyPersistentMap, empty.toPersistentMap()) + } @Test fun putElements() { var map = immutableMapOf().toPersistentMap() diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index 4e8ef6fd..f017ccae 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -339,13 +339,22 @@ abstract class ImmutableSetTestBase { val set = immutableSetOf("abcxyz12".toList()) with(set) { testNoOperation({ add('a') }, { add('a') }) + testNoOperation({ addAll(emptySet()) }, { addAll(emptySet()) }) testNoOperation({ addAll(listOf('a', 'b')) }, { addAll(listOf('a', 'b')) }) testNoOperation({ remove('d') }, { remove('d') }) testNoOperation({ removeAll(listOf('d', 'e')) }, { removeAll(listOf('d', 'e')) }) testNoOperation({ removeAll { it.isUpperCase() } }, { removeAll { it.isUpperCase() } }) + testNoOperation({ removeAll(emptySet()) }, { removeAll(emptySet()) }) } } + @Test fun emptySetToPersistentSet() { + val empty = emptySet() + val emptyPersistentSet = empty.toPersistentSet() + + assertSame(emptyPersistentSet, empty.toPersistentSet()) + } + fun PersistentSet.testNoOperation(persistent: PersistentSet.() -> PersistentSet, mutating: MutableSet.() -> Unit) { val result = this.persistent() val buildResult = this.mutate(mutating) From 1d18389e32e8afdbf5caa89808fb5af308fb5c8d Mon Sep 17 00:00:00 2001 From: Matthew Pope Date: Wed, 20 Mar 2024 14:17:57 -0700 Subject: [PATCH 133/162] Adds an empty input check to all addAll, removeAll, and retainAll methods --- .../implementations/immutableList/AbstractPersistentList.kt | 6 ++++++ .../immutableList/PersistentVectorBuilder.kt | 1 + .../src/implementations/immutableMap/PersistentHashMap.kt | 1 + .../immutableMap/PersistentHashMapBuilder.kt | 1 + .../src/implementations/immutableSet/PersistentHashSet.kt | 3 +++ .../immutableSet/PersistentHashSetBuilder.kt | 2 ++ .../persistentOrderedMap/PersistentOrderedMap.kt | 1 + .../persistentOrderedSet/PersistentOrderedSet.kt | 3 +++ 8 files changed, 18 insertions(+) diff --git a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt index e018fe4e..525e59b5 100644 --- a/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt +++ b/core/commonMain/src/implementations/immutableList/AbstractPersistentList.kt @@ -8,6 +8,7 @@ package kotlinx.collections.immutable.implementations.immutableList import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate +import kotlinx.collections.immutable.internal.ListImplementation.checkPositionIndex public abstract class AbstractPersistentList : PersistentList, AbstractList() { override fun subList(fromIndex: Int, toIndex: Int): ImmutableList { @@ -15,10 +16,13 @@ public abstract class AbstractPersistentList : PersistentList, AbstractLis } override fun addAll(elements: Collection): PersistentList { + if (elements.isEmpty()) return this return mutate { it.addAll(elements) } } override fun addAll(index: Int, c: Collection): PersistentList { + checkPositionIndex(index, size) + if (c.isEmpty()) return this return mutate { it.addAll(index, c) } } @@ -31,10 +35,12 @@ public abstract class AbstractPersistentList : PersistentList, AbstractLis } override fun removeAll(elements: Collection): PersistentList { + if (elements.isEmpty()) return this return removeAll { elements.contains(it) } } override fun retainAll(elements: Collection): PersistentList { + if (elements.isEmpty()) return persistentVectorOf() return removeAll { !elements.contains(it) } } diff --git a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt index 6d788ab5..f4f56f71 100644 --- a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt +++ b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt @@ -669,6 +669,7 @@ internal class PersistentVectorBuilder(private var vector: PersistentList, } override fun removeAll(elements: Collection): Boolean { + if (elements.isEmpty()) return false return removeAllWithPredicate { elements.contains(it) } } diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt index 332e5e40..e798d483 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMap.kt @@ -68,6 +68,7 @@ internal class PersistentHashMap(internal val node: TrieNode, } override fun putAll(m: Map): PersistentMap { + if (m.isEmpty()) return this return this.mutate { it.putAll(m) } } diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index 983a319f..eac1cac9 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -66,6 +66,7 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap } override fun putAll(from: Map) { + if (from.isEmpty()) return val map = from as? PersistentHashMap ?: (from as? PersistentHashMapBuilder)?.build() if (map != null) @Suppress("UNCHECKED_CAST") { val intersectionCounter = DeltaCounter() diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt index 8686b1ee..7ac2a461 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSet.kt @@ -21,6 +21,7 @@ internal class PersistentHashSet(internal val node: TrieNode, } override fun addAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return this return this.mutate { it.addAll(elements) } } @@ -31,6 +32,7 @@ internal class PersistentHashSet(internal val node: TrieNode, } override fun removeAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return this return mutate { it.removeAll(elements) } } @@ -39,6 +41,7 @@ internal class PersistentHashSet(internal val node: TrieNode, } override fun retainAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return PersistentHashSet.emptyOf() return mutate { it.retainAll(elements) } } diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt index a9bce437..a5899156 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt @@ -45,6 +45,7 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet } override fun addAll(elements: Collection): Boolean { + if (elements.isEmpty()) return false val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() if (set !== null) { val deltaCounter = DeltaCounter() @@ -81,6 +82,7 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet } override fun removeAll(elements: Collection): Boolean { + if (elements.isEmpty()) return false val set = elements as? PersistentHashSet ?: (elements as? PersistentHashSetBuilder)?.build() if (set !== null) { val counter = DeltaCounter() diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt index 94d25929..5f7df084 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMap.kt @@ -117,6 +117,7 @@ internal class PersistentOrderedMap( } override fun putAll(m: Map): PersistentMap { + if (m.isEmpty()) return this return this.mutate { it.putAll(m) } } diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt index a1f5dc1a..54bc6e72 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSet.kt @@ -53,6 +53,7 @@ internal class PersistentOrderedSet( } override fun addAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return this return this.mutate { it.addAll(elements) } } @@ -78,6 +79,7 @@ internal class PersistentOrderedSet( } override fun removeAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return this return mutate { it.removeAll(elements) } } @@ -86,6 +88,7 @@ internal class PersistentOrderedSet( } override fun retainAll(elements: Collection): PersistentSet { + if (elements.isEmpty()) return PersistentOrderedSet.emptyOf() return mutate { it.retainAll(elements) } } From 3c2248e2667ffcce6892cf81e6d201d25cea610c Mon Sep 17 00:00:00 2001 From: Margarita Bobova <32216159+woainikk@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:49:49 +0200 Subject: [PATCH 134/162] Bump binary-compatibility-validator version to 0.16.2 (#188) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9c3b4719..890e8bb0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ buildscript { plugins { id("kotlinx.team.infra") version "0.4.0-dev-80" - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.2" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.2" } infra { From 38be1b4a9e36989bee4f7b92adeb9328e20f9900 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 21 Aug 2024 09:47:05 +0800 Subject: [PATCH 135/162] Gradle 8.10 --- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 285 ++++++++++++++--------- gradlew.bat | 37 +-- 4 files changed, 200 insertions(+), 126 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fce..9355b415 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From c60eb51560581c8014df7692ab6080d3b85696fc Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 4 Jun 2024 15:38:25 +0800 Subject: [PATCH 136/162] Enable KLib ABI validation https://github.com/Kotlin/binary-compatibility-validator?tab=readme-ov-file#experimental-klib-abi-validation-support --- build.gradle.kts | 7 +- .../kotlinx-collections-immutable.klib.api | 246 ++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 core/api/kotlinx-collections-immutable.klib.api diff --git a/build.gradle.kts b/build.gradle.kts index 890e8bb0..68584e15 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ buildscript { plugins { id("kotlinx.team.infra") version "0.4.0-dev-80" - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.2" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" } infra { @@ -27,6 +27,11 @@ apiValidation { "benchmarks", "runner", ) + + @OptIn(kotlinx.validation.ExperimentalBCVApi::class) + klib { + enabled = true + } } allprojects { diff --git a/core/api/kotlinx-collections-immutable.klib.api b/core/api/kotlinx-collections-immutable.klib.api new file mode 100644 index 00000000..ac692d0d --- /dev/null +++ b/core/api/kotlinx-collections-immutable.klib.api @@ -0,0 +1,246 @@ +// Klib ABI Dump +// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +abstract interface <#A: kotlin/Any?, #B: out kotlin/Any?> kotlinx.collections.immutable/ImmutableMap : kotlin.collections/Map<#A, #B> { // kotlinx.collections.immutable/ImmutableMap|null[0] + abstract val entries // kotlinx.collections.immutable/ImmutableMap.entries|{}entries[0] + abstract fun (): kotlinx.collections.immutable/ImmutableSet> // kotlinx.collections.immutable/ImmutableMap.entries.|(){}[0] + abstract val keys // kotlinx.collections.immutable/ImmutableMap.keys|{}keys[0] + abstract fun (): kotlinx.collections.immutable/ImmutableSet<#A> // kotlinx.collections.immutable/ImmutableMap.keys.|(){}[0] + abstract val values // kotlinx.collections.immutable/ImmutableMap.values|{}values[0] + abstract fun (): kotlinx.collections.immutable/ImmutableCollection<#B> // kotlinx.collections.immutable/ImmutableMap.values.|(){}[0] +} + +abstract interface <#A: kotlin/Any?, #B: out kotlin/Any?> kotlinx.collections.immutable/PersistentMap : kotlinx.collections.immutable/ImmutableMap<#A, #B> { // kotlinx.collections.immutable/PersistentMap|null[0] + abstract fun builder(): kotlinx.collections.immutable/PersistentMap.Builder<#A, #B> // kotlinx.collections.immutable/PersistentMap.builder|builder(){}[0] + abstract fun clear(): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/PersistentMap.clear|clear(){}[0] + abstract fun put(#A, #B): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/PersistentMap.put|put(1:0;1:1){}[0] + abstract fun putAll(kotlin.collections/Map): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/PersistentMap.putAll|putAll(kotlin.collections.Map){}[0] + abstract fun remove(#A): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/PersistentMap.remove|remove(1:0){}[0] + abstract fun remove(#A, #B): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/PersistentMap.remove|remove(1:0;1:1){}[0] + + abstract interface <#A1: kotlin/Any?, #B1: kotlin/Any?> Builder : kotlin.collections/MutableMap<#A1, #B1> { // kotlinx.collections.immutable/PersistentMap.Builder|null[0] + abstract fun build(): kotlinx.collections.immutable/PersistentMap<#A1, #B1> // kotlinx.collections.immutable/PersistentMap.Builder.build|build(){}[0] + } +} + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/ImmutableCollection : kotlin.collections/Collection<#A> // kotlinx.collections.immutable/ImmutableCollection|null[0] + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/ImmutableList : kotlin.collections/List<#A>, kotlinx.collections.immutable/ImmutableCollection<#A> { // kotlinx.collections.immutable/ImmutableList|null[0] + open fun subList(kotlin/Int, kotlin/Int): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable/ImmutableList.subList|subList(kotlin.Int;kotlin.Int){}[0] +} + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/ImmutableSet : kotlin.collections/Set<#A>, kotlinx.collections.immutable/ImmutableCollection<#A> // kotlinx.collections.immutable/ImmutableSet|null[0] + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/PersistentCollection : kotlinx.collections.immutable/ImmutableCollection<#A> { // kotlinx.collections.immutable/PersistentCollection|null[0] + abstract fun add(#A): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.add|add(1:0){}[0] + abstract fun addAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.addAll|addAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun builder(): kotlinx.collections.immutable/PersistentCollection.Builder<#A> // kotlinx.collections.immutable/PersistentCollection.builder|builder(){}[0] + abstract fun clear(): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.clear|clear(){}[0] + abstract fun remove(#A): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.remove|remove(1:0){}[0] + abstract fun removeAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun removeAll(kotlin/Function1<#A, kotlin/Boolean>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.removeAll|removeAll(kotlin.Function1<1:0,kotlin.Boolean>){}[0] + abstract fun retainAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/PersistentCollection.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0] + + abstract interface <#A1: kotlin/Any?> Builder : kotlin.collections/MutableCollection<#A1> { // kotlinx.collections.immutable/PersistentCollection.Builder|null[0] + abstract fun build(): kotlinx.collections.immutable/PersistentCollection<#A1> // kotlinx.collections.immutable/PersistentCollection.Builder.build|build(){}[0] + } +} + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/PersistentList : kotlinx.collections.immutable/ImmutableList<#A>, kotlinx.collections.immutable/PersistentCollection<#A> { // kotlinx.collections.immutable/PersistentList|null[0] + abstract fun add(#A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.add|add(1:0){}[0] + abstract fun add(kotlin/Int, #A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.add|add(kotlin.Int;1:0){}[0] + abstract fun addAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.addAll|addAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun addAll(kotlin/Int, kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.addAll|addAll(kotlin.Int;kotlin.collections.Collection<1:0>){}[0] + abstract fun builder(): kotlinx.collections.immutable/PersistentList.Builder<#A> // kotlinx.collections.immutable/PersistentList.builder|builder(){}[0] + abstract fun clear(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.clear|clear(){}[0] + abstract fun remove(#A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.remove|remove(1:0){}[0] + abstract fun removeAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun removeAll(kotlin/Function1<#A, kotlin/Boolean>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.removeAll|removeAll(kotlin.Function1<1:0,kotlin.Boolean>){}[0] + abstract fun removeAt(kotlin/Int): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.removeAt|removeAt(kotlin.Int){}[0] + abstract fun retainAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun set(kotlin/Int, #A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/PersistentList.set|set(kotlin.Int;1:0){}[0] + + abstract interface <#A1: kotlin/Any?> Builder : kotlin.collections/MutableList<#A1>, kotlinx.collections.immutable/PersistentCollection.Builder<#A1> { // kotlinx.collections.immutable/PersistentList.Builder|null[0] + abstract fun build(): kotlinx.collections.immutable/PersistentList<#A1> // kotlinx.collections.immutable/PersistentList.Builder.build|build(){}[0] + } +} + +abstract interface <#A: out kotlin/Any?> kotlinx.collections.immutable/PersistentSet : kotlinx.collections.immutable/ImmutableSet<#A>, kotlinx.collections.immutable/PersistentCollection<#A> { // kotlinx.collections.immutable/PersistentSet|null[0] + abstract fun add(#A): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.add|add(1:0){}[0] + abstract fun addAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.addAll|addAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun builder(): kotlinx.collections.immutable/PersistentSet.Builder<#A> // kotlinx.collections.immutable/PersistentSet.builder|builder(){}[0] + abstract fun clear(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.clear|clear(){}[0] + abstract fun remove(#A): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.remove|remove(1:0){}[0] + abstract fun removeAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0] + abstract fun removeAll(kotlin/Function1<#A, kotlin/Boolean>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.removeAll|removeAll(kotlin.Function1<1:0,kotlin.Boolean>){}[0] + abstract fun retainAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/PersistentSet.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0] + + abstract interface <#A1: kotlin/Any?> Builder : kotlin.collections/MutableSet<#A1>, kotlinx.collections.immutable/PersistentCollection.Builder<#A1> { // kotlinx.collections.immutable/PersistentSet.Builder|null[0] + abstract fun build(): kotlinx.collections.immutable/PersistentSet<#A1> // kotlinx.collections.immutable/PersistentSet.Builder.build|build(){}[0] + } +} + +abstract class <#A: kotlin/Any?> kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList : kotlin.collections/AbstractList<#A>, kotlinx.collections.immutable/PersistentList<#A> { // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList|null[0] + constructor () // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.|(){}[0] + + open fun addAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.addAll|addAll(kotlin.collections.Collection<1:0>){}[0] + open fun addAll(kotlin/Int, kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.addAll|addAll(kotlin.Int;kotlin.collections.Collection<1:0>){}[0] + open fun clear(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.clear|clear(){}[0] + open fun contains(#A): kotlin/Boolean // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.contains|contains(1:0){}[0] + open fun containsAll(kotlin.collections/Collection<#A>): kotlin/Boolean // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.containsAll|containsAll(kotlin.collections.Collection<1:0>){}[0] + open fun iterator(): kotlin.collections/Iterator<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.iterator|iterator(){}[0] + open fun listIterator(): kotlin.collections/ListIterator<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.listIterator|listIterator(){}[0] + open fun remove(#A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.remove|remove(1:0){}[0] + open fun removeAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.removeAll|removeAll(kotlin.collections.Collection<1:0>){}[0] + open fun retainAll(kotlin.collections/Collection<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.retainAll|retainAll(kotlin.collections.Collection<1:0>){}[0] + open fun subList(kotlin/Int, kotlin/Int): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable.implementations.immutableList/AbstractPersistentList.subList|subList(kotlin.Int;kotlin.Int){}[0] +} + +final class <#A: kotlin/Any?, #B: out kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableMapAdapter : kotlin.collections/Map<#A, #B>, kotlinx.collections.immutable/ImmutableMap<#A, #B> { // kotlinx.collections.immutable.adapters/ImmutableMapAdapter|null[0] + constructor (kotlin.collections/Map<#A, #B>) // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.|(kotlin.collections.Map<1:0,1:1>){}[0] + + final val entries // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.entries|{}entries[0] + final fun (): kotlinx.collections.immutable/ImmutableSet> // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.entries.|(){}[0] + final val keys // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.keys|{}keys[0] + final fun (): kotlinx.collections.immutable/ImmutableSet<#A> // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.keys.|(){}[0] + final val size // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.size|{}size[0] + final fun (): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.size.|(){}[0] + final val values // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.values|{}values[0] + final fun (): kotlinx.collections.immutable/ImmutableCollection<#B> // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.values.|(){}[0] + + final fun containsKey(#A): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.containsKey|containsKey(1:0){}[0] + final fun containsValue(#B): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.containsValue|containsValue(1:1){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.equals|equals(kotlin.Any?){}[0] + final fun get(#A): #B? // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.get|get(1:0){}[0] + final fun hashCode(): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.hashCode|hashCode(){}[0] + final fun isEmpty(): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.isEmpty|isEmpty(){}[0] + final fun toString(): kotlin/String // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.toString|toString(){}[0] +} + +final class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableListAdapter : kotlin.collections/List<#A>, kotlinx.collections.immutable/ImmutableList<#A> { // kotlinx.collections.immutable.adapters/ImmutableListAdapter|null[0] + constructor (kotlin.collections/List<#A>) // kotlinx.collections.immutable.adapters/ImmutableListAdapter.|(kotlin.collections.List<1:0>){}[0] + + final val size // kotlinx.collections.immutable.adapters/ImmutableListAdapter.size|{}size[0] + final fun (): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableListAdapter.size.|(){}[0] + + final fun contains(#A): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableListAdapter.contains|contains(1:0){}[0] + final fun containsAll(kotlin.collections/Collection<#A>): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableListAdapter.containsAll|containsAll(kotlin.collections.Collection<1:0>){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableListAdapter.equals|equals(kotlin.Any?){}[0] + final fun get(kotlin/Int): #A // kotlinx.collections.immutable.adapters/ImmutableListAdapter.get|get(kotlin.Int){}[0] + final fun hashCode(): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableListAdapter.hashCode|hashCode(){}[0] + final fun indexOf(#A): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableListAdapter.indexOf|indexOf(1:0){}[0] + final fun isEmpty(): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableListAdapter.isEmpty|isEmpty(){}[0] + final fun iterator(): kotlin.collections/Iterator<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.iterator|iterator(){}[0] + final fun lastIndexOf(#A): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableListAdapter.lastIndexOf|lastIndexOf(1:0){}[0] + final fun listIterator(): kotlin.collections/ListIterator<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.listIterator|listIterator(){}[0] + final fun listIterator(kotlin/Int): kotlin.collections/ListIterator<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.listIterator|listIterator(kotlin.Int){}[0] + final fun subList(kotlin/Int, kotlin/Int): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.subList|subList(kotlin.Int;kotlin.Int){}[0] + final fun toString(): kotlin/String // kotlinx.collections.immutable.adapters/ImmutableListAdapter.toString|toString(){}[0] +} + +final class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableSetAdapter : kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter<#A>, kotlinx.collections.immutable/ImmutableSet<#A> { // kotlinx.collections.immutable.adapters/ImmutableSetAdapter|null[0] + constructor (kotlin.collections/Set<#A>) // kotlinx.collections.immutable.adapters/ImmutableSetAdapter.|(kotlin.collections.Set<1:0>){}[0] +} + +open class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter : kotlin.collections/Collection<#A>, kotlinx.collections.immutable/ImmutableCollection<#A> { // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter|null[0] + constructor (kotlin.collections/Collection<#A>) // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.|(kotlin.collections.Collection<1:0>){}[0] + + open val size // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.size|{}size[0] + open fun (): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.size.|(){}[0] + + open fun contains(#A): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.contains|contains(1:0){}[0] + open fun containsAll(kotlin.collections/Collection<#A>): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.containsAll|containsAll(kotlin.collections.Collection<1:0>){}[0] + open fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.equals|equals(kotlin.Any?){}[0] + open fun hashCode(): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.hashCode|hashCode(){}[0] + open fun isEmpty(): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.isEmpty|isEmpty(){}[0] + open fun iterator(): kotlin.collections/Iterator<#A> // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.iterator|iterator(){}[0] + open fun toString(): kotlin/String // kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter.toString|toString(){}[0] +} + +final fun (kotlin/CharSequence).kotlinx.collections.immutable/toImmutableList(): kotlinx.collections.immutable/ImmutableList // kotlinx.collections.immutable/toImmutableList|toImmutableList@kotlin.CharSequence(){}[0] +final fun (kotlin/CharSequence).kotlinx.collections.immutable/toImmutableSet(): kotlinx.collections.immutable/PersistentSet // kotlinx.collections.immutable/toImmutableSet|toImmutableSet@kotlin.CharSequence(){}[0] +final fun (kotlin/CharSequence).kotlinx.collections.immutable/toPersistentHashSet(): kotlinx.collections.immutable/PersistentSet // kotlinx.collections.immutable/toPersistentHashSet|toPersistentHashSet@kotlin.CharSequence(){}[0] +final fun (kotlin/CharSequence).kotlinx.collections.immutable/toPersistentList(): kotlinx.collections.immutable/PersistentList // kotlinx.collections.immutable/toPersistentList|toPersistentList@kotlin.CharSequence(){}[0] +final fun (kotlin/CharSequence).kotlinx.collections.immutable/toPersistentSet(): kotlinx.collections.immutable/PersistentSet // kotlinx.collections.immutable/toPersistentSet|toPersistentSet@kotlin.CharSequence(){}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlin.collections/Map<#A, #B>).kotlinx.collections.immutable/toImmutableMap(): kotlinx.collections.immutable/ImmutableMap<#A, #B> // kotlinx.collections.immutable/toImmutableMap|toImmutableMap@kotlin.collections.Map<0:0,0:1>(){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlin.collections/Map<#A, #B>).kotlinx.collections.immutable/toPersistentHashMap(): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/toPersistentHashMap|toPersistentHashMap@kotlin.collections.Map<0:0,0:1>(){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlin.collections/Map<#A, #B>).kotlinx.collections.immutable/toPersistentMap(): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/toPersistentMap|toPersistentMap@kotlin.collections.Map<0:0,0:1>(){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/minus(#A): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentMap(0:0){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/minus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentMap(kotlin.collections.Iterable<0:0>){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/minus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentMap(kotlin.sequences.Sequence<0:0>){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/minus(kotlin/Array): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentMap(kotlin.Array){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/putAll(kotlin.collections/Iterable>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/putAll|putAll@kotlinx.collections.immutable.PersistentMap(kotlin.collections.Iterable>){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/putAll(kotlin.collections/Map): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/putAll|putAll@kotlinx.collections.immutable.PersistentMap(kotlin.collections.Map){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/putAll(kotlin.sequences/Sequence>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/putAll|putAll@kotlinx.collections.immutable.PersistentMap(kotlin.sequences.Sequence>){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/putAll(kotlin/Array>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/putAll|putAll@kotlinx.collections.immutable.PersistentMap(kotlin.Array>){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/immutableHashMapOf(kotlin/Array>...): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/immutableHashMapOf|immutableHashMapOf(kotlin.Array>...){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/immutableMapOf(kotlin/Array>...): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/immutableMapOf|immutableMapOf(kotlin.Array>...){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/persistentHashMapOf(): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/persistentHashMapOf|persistentHashMapOf(){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/persistentHashMapOf(kotlin/Array>...): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/persistentHashMapOf|persistentHashMapOf(kotlin.Array>...){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/persistentMapOf(): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/persistentMapOf|persistentMapOf(){0§;1§}[0] +final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.collections.immutable/persistentMapOf(kotlin/Array>...): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/persistentMapOf|persistentMapOf(kotlin.Array>...){0§;1§}[0] +final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.collections.immutable/toImmutableList(): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable/toImmutableList|toImmutableList@kotlin.collections.Iterable<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.collections.immutable/toImmutableSet(): kotlinx.collections.immutable/ImmutableSet<#A> // kotlinx.collections.immutable/toImmutableSet|toImmutableSet@kotlin.collections.Iterable<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.collections.immutable/toPersistentHashSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentHashSet|toPersistentHashSet@kotlin.collections.Iterable<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.collections.immutable/toPersistentList(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/toPersistentList|toPersistentList@kotlin.collections.Iterable<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).kotlinx.collections.immutable/toPersistentSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentSet|toPersistentSet@kotlin.collections.Iterable<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.collections.immutable/toImmutableList(): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable/toImmutableList|toImmutableList@kotlin.sequences.Sequence<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.collections.immutable/toImmutableSet(): kotlinx.collections.immutable/ImmutableSet<#A> // kotlinx.collections.immutable/toImmutableSet|toImmutableSet@kotlin.sequences.Sequence<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.collections.immutable/toPersistentHashSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentHashSet|toPersistentHashSet@kotlin.sequences.Sequence<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.collections.immutable/toPersistentList(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/toPersistentList|toPersistentList@kotlin.sequences.Sequence<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin.sequences/Sequence<#A>).kotlinx.collections.immutable/toPersistentSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentSet|toPersistentSet@kotlin.sequences.Sequence<0:0>(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin/Array).kotlinx.collections.immutable/toImmutableList(): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable/toImmutableList|toImmutableList@kotlin.Array(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin/Array).kotlinx.collections.immutable/toImmutableSet(): kotlinx.collections.immutable/ImmutableSet<#A> // kotlinx.collections.immutable/toImmutableSet|toImmutableSet@kotlin.Array(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin/Array).kotlinx.collections.immutable/toPersistentHashSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentHashSet|toPersistentHashSet@kotlin.Array(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin/Array).kotlinx.collections.immutable/toPersistentList(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/toPersistentList|toPersistentList@kotlin.Array(){0§}[0] +final fun <#A: kotlin/Any?> (kotlin/Array).kotlinx.collections.immutable/toPersistentSet(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/toPersistentSet|toPersistentSet@kotlin.Array(){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/intersect(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/intersect|intersect@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/minus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/minus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/minus(kotlin/Array): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/plus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/plus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/plus(kotlin/Array): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentCollection<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/minus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/minus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/minus(kotlin/Array): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/plus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/plus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/plus(kotlin/Array): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/intersect(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/intersect|intersect@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/minus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/minus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/minus(kotlin/Array): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/plus(kotlin.collections/Iterable<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.collections.Iterable<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/plus(kotlin.sequences/Sequence<#A>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.sequences.Sequence<0:0>){0§}[0] +final fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/plus(kotlin/Array): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.Array){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/immutableHashSetOf(kotlin/Array...): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/immutableHashSetOf|immutableHashSetOf(kotlin.Array...){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/immutableListOf(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/immutableListOf|immutableListOf(){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/immutableListOf(kotlin/Array...): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/immutableListOf|immutableListOf(kotlin.Array...){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/immutableSetOf(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/immutableSetOf|immutableSetOf(){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/immutableSetOf(kotlin/Array...): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/immutableSetOf|immutableSetOf(kotlin.Array...){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentHashSetOf(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/persistentHashSetOf|persistentHashSetOf(){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentHashSetOf(kotlin/Array...): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/persistentHashSetOf|persistentHashSetOf(kotlin.Array...){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentListOf(): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/persistentListOf|persistentListOf(){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentListOf(kotlin/Array...): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/persistentListOf|persistentListOf(kotlin.Array...){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentSetOf(): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/persistentSetOf|persistentSetOf(){0§}[0] +final fun <#A: kotlin/Any?> kotlinx.collections.immutable/persistentSetOf(kotlin/Array...): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/persistentSetOf|persistentSetOf(kotlin.Array...){0§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/mutate(kotlin/Function1, kotlin/Unit>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/mutate|mutate@kotlinx.collections.immutable.PersistentMap(kotlin.Function1,kotlin.Unit>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/plus(kotlin.collections/Iterable>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentMap(kotlin.collections.Iterable>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/plus(kotlin.collections/Map): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentMap(kotlin.collections.Map){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/plus(kotlin.sequences/Sequence>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentMap(kotlin.sequences.Sequence>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/plus(kotlin/Array>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentMap(kotlin.Array>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlinx.collections.immutable/PersistentMap).kotlinx.collections.immutable/plus(kotlin/Pair<#A, #B>): kotlinx.collections.immutable/PersistentMap<#A, #B> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentMap(kotlin.Pair<0:0,0:1>){0§;1§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/minus(#A): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentCollection<0:0>(0:0){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentCollection<#A>).kotlinx.collections.immutable/plus(#A): kotlinx.collections.immutable/PersistentCollection<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentCollection<0:0>(0:0){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/minus(#A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentList<0:0>(0:0){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/mutate(kotlin/Function1, kotlin/Unit>): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/mutate|mutate@kotlinx.collections.immutable.PersistentList<0:0>(kotlin.Function1,kotlin.Unit>){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentList<#A>).kotlinx.collections.immutable/plus(#A): kotlinx.collections.immutable/PersistentList<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentList<0:0>(0:0){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/minus(#A): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/minus|minus@kotlinx.collections.immutable.PersistentSet<0:0>(0:0){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/mutate(kotlin/Function1, kotlin/Unit>): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/mutate|mutate@kotlinx.collections.immutable.PersistentSet<0:0>(kotlin.Function1,kotlin.Unit>){0§}[0] +final inline fun <#A: kotlin/Any?> (kotlinx.collections.immutable/PersistentSet<#A>).kotlinx.collections.immutable/plus(#A): kotlinx.collections.immutable/PersistentSet<#A> // kotlinx.collections.immutable/plus|plus@kotlinx.collections.immutable.PersistentSet<0:0>(0:0){0§}[0] From fe7b163d00541b7fada2e7e4efbb1f5b04be69fd Mon Sep 17 00:00:00 2001 From: Andrey Zaytsev Date: Wed, 4 Sep 2024 21:15:57 +0200 Subject: [PATCH 137/162] fix memory leak in builders: they should not hold on to the original collection (#193) --- .../immutableList/PersistentVectorBuilder.kt | 40 ++++++++---- .../immutableMap/PersistentHashMapBuilder.kt | 19 ++++-- .../immutableSet/PersistentHashSetBuilder.kt | 19 ++++-- .../PersistentOrderedMapBuilder.kt | 65 +++++++++++-------- .../PersistentOrderedSetBuilder.kt | 23 ++++--- 5 files changed, 104 insertions(+), 62 deletions(-) diff --git a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt index f4f56f71..e2b35b13 100644 --- a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt +++ b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt @@ -12,38 +12,52 @@ import kotlinx.collections.immutable.internal.MutabilityOwnership import kotlinx.collections.immutable.internal.assert import kotlinx.collections.immutable.internal.modCount -internal class PersistentVectorBuilder(private var vector: PersistentList, - private var vectorRoot: Array?, - private var vectorTail: Array, +internal class PersistentVectorBuilder(vector: PersistentList, + vectorRoot: Array?, + vectorTail: Array, internal var rootShift: Int) : AbstractMutableList(), PersistentList.Builder { + + private var builtVector: PersistentList? = vector private var ownership = MutabilityOwnership() + internal var root = vectorRoot - private set + private set (value) { + if (value !== field) { + builtVector = null + field = value + } + } + internal var tail = vectorTail - private set + private set (value) { + if (value !== field) { + builtVector = null + field = value + } + } + override var size = vector.size private set internal fun getModCount() = modCount override fun build(): PersistentList { - vector = if (root === vectorRoot && tail === vectorTail) { - vector - } else { + return builtVector ?: run { + val root = root + val tail = tail ownership = MutabilityOwnership() - vectorRoot = root - vectorTail = tail - if (root == null) { + val newlyBuiltVector: PersistentList = if (root == null) { if (tail.isEmpty()) { persistentVectorOf() } else { SmallPersistentVector(tail.copyOf(size)) } } else { - PersistentVector(root!!, tail, size, rootShift) + PersistentVector(root, tail, size, rootShift) } + builtVector = newlyBuiltVector + newlyBuiltVector } - return vector } private fun rootSize(): Int { diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt index eac1cac9..f0825325 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilder.kt @@ -12,10 +12,18 @@ import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MapImplementation import kotlinx.collections.immutable.internal.MutabilityOwnership -internal class PersistentHashMapBuilder(private var map: PersistentHashMap) : PersistentMap.Builder, AbstractMutableMap() { +internal class PersistentHashMapBuilder(map: PersistentHashMap) : PersistentMap.Builder, AbstractMutableMap() { + internal var builtMap: PersistentHashMap? = map + private set internal var ownership = MutabilityOwnership() private set internal var node = map.node + set(value) { + if (value !== field) { + field = value + builtMap = null + } + } internal var operationResult: V? = null internal var modCount = 0 @@ -27,13 +35,12 @@ internal class PersistentHashMapBuilder(private var map: PersistentHashMap } override fun build(): PersistentHashMap { - map = if (node === map.node) { - map - } else { + return builtMap ?: run { + val newlyBuiltMap = PersistentHashMap(node, size) + builtMap = newlyBuiltMap ownership = MutabilityOwnership() - PersistentHashMap(node, size) + newlyBuiltMap } - return map } override val entries: MutableSet> diff --git a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt index a5899156..08fcf6a2 100644 --- a/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt +++ b/core/commonMain/src/implementations/immutableSet/PersistentHashSetBuilder.kt @@ -9,11 +9,17 @@ import kotlinx.collections.immutable.PersistentSet import kotlinx.collections.immutable.internal.DeltaCounter import kotlinx.collections.immutable.internal.MutabilityOwnership -internal class PersistentHashSetBuilder(private var set: PersistentHashSet) : AbstractMutableSet(), PersistentSet.Builder { +internal class PersistentHashSetBuilder(set: PersistentHashSet) : AbstractMutableSet(), PersistentSet.Builder { + private var builtSet: PersistentHashSet? = set internal var ownership = MutabilityOwnership() private set internal var node = set.node - private set + private set (value) { + if (value !== field) { + builtSet = null + field = value + } + } internal var modCount = 0 private set @@ -25,13 +31,12 @@ internal class PersistentHashSetBuilder(private var set: PersistentHashSet } override fun build(): PersistentHashSet { - set = if (node === set.node) { - set - } else { + return builtSet ?: run { + val newlyBuiltSet = PersistentHashSet(node, size) ownership = MutabilityOwnership() - PersistentHashSet(node, size) + builtSet = newlyBuiltSet + newlyBuiltSet } - return set } override fun contains(element: E): Boolean { diff --git a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt index b39fb9b4..b750a5e2 100644 --- a/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt +++ b/core/commonMain/src/implementations/persistentOrderedMap/PersistentOrderedMapBuilder.kt @@ -12,7 +12,9 @@ import kotlinx.collections.immutable.internal.EndOfChain import kotlinx.collections.immutable.internal.MapImplementation import kotlinx.collections.immutable.internal.assert -internal class PersistentOrderedMapBuilder(private var map: PersistentOrderedMap) : AbstractMutableMap(), PersistentMap.Builder { +internal class PersistentOrderedMapBuilder(map: PersistentOrderedMap) : AbstractMutableMap(), PersistentMap.Builder { + private var builtMap: PersistentOrderedMap? = map + internal var firstKey = map.firstKey private set @@ -23,15 +25,17 @@ internal class PersistentOrderedMapBuilder(private var map: PersistentOrde override val size: Int get() = hashMapBuilder.size override fun build(): PersistentMap { - val newHashMap = hashMapBuilder.build() - map = if (newHashMap === map.hashMap) { + return builtMap?.also { map -> + assert(hashMapBuilder.builtMap != null) assert(firstKey === map.firstKey) assert(lastKey === map.lastKey) - map - } else { - PersistentOrderedMap(firstKey, lastKey, newHashMap) + } ?: run { + assert(hashMapBuilder.builtMap == null) + val newHashMap = hashMapBuilder.build() + val newOrdered = PersistentOrderedMap(firstKey, lastKey, newHashMap) + builtMap = newOrdered + newOrdered } - return map } override val entries: MutableSet> @@ -55,34 +59,36 @@ internal class PersistentOrderedMapBuilder(private var map: PersistentOrde override fun put(key: K, value: @UnsafeVariance V): V? { val links = hashMapBuilder[key] - if (links != null) { + return if (links != null) { if (links.value === value) { - return value + value + } else { + builtMap = null + hashMapBuilder[key] = links.withValue(value) + links.value } - hashMapBuilder[key] = links.withValue(value) - return links.value - } - - if (isEmpty()) { // isEmpty - firstKey = key - lastKey = key - hashMapBuilder[key] = LinkedValue(value) - return null + } else { + builtMap = null + if (isEmpty()) { + firstKey = key + lastKey = key + hashMapBuilder[key] = LinkedValue(value) + } else { + @Suppress("UNCHECKED_CAST") + val lastKey = lastKey as K + val lastLinks = hashMapBuilder[lastKey]!! + assert(!lastLinks.hasNext) + hashMapBuilder[lastKey] = lastLinks.withNext(key) + hashMapBuilder[key] = LinkedValue(value, previous = lastKey) + this.lastKey = key + } + null } - @Suppress("UNCHECKED_CAST") - val lastKey = lastKey as K - val lastLinks = hashMapBuilder[lastKey]!! - assert(!lastLinks.hasNext) - - hashMapBuilder[lastKey] = lastLinks.withNext(key) - hashMapBuilder[key] = LinkedValue(value, previous = lastKey) - this.lastKey = key - return null } override fun remove(key: K): V? { val links = hashMapBuilder.remove(key) ?: return null - + builtMap = null if (links.hasPrevious) { val previousLinks = hashMapBuilder[links.previous]!! // assert(previousLinks.next == key) @@ -115,6 +121,9 @@ internal class PersistentOrderedMapBuilder(private var map: PersistentOrde } override fun clear() { + if (hashMapBuilder.isNotEmpty()) { + builtMap = null + } hashMapBuilder.clear() firstKey = EndOfChain lastKey = EndOfChain diff --git a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt index 352a9576..4f121e6a 100644 --- a/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt +++ b/core/commonMain/src/implementations/persistentOrderedSet/PersistentOrderedSetBuilder.kt @@ -9,7 +9,8 @@ import kotlinx.collections.immutable.PersistentSet import kotlinx.collections.immutable.internal.EndOfChain import kotlinx.collections.immutable.internal.assert -internal class PersistentOrderedSetBuilder(private var set: PersistentOrderedSet) : AbstractMutableSet(), PersistentSet.Builder { +internal class PersistentOrderedSetBuilder(set: PersistentOrderedSet) : AbstractMutableSet(), PersistentSet.Builder { + private var builtSet: PersistentOrderedSet? = set internal var firstElement = set.firstElement private var lastElement = set.lastElement internal val hashMapBuilder = set.hashMap.builder() @@ -18,15 +19,17 @@ internal class PersistentOrderedSetBuilder(private var set: PersistentOrdered get() = hashMapBuilder.size override fun build(): PersistentSet { - val newMap = hashMapBuilder.build() - set = if (newMap === set.hashMap) { + return builtSet?.also { set -> + assert(hashMapBuilder.builtMap != null) assert(firstElement === set.firstElement) assert(lastElement === set.lastElement) - set - } else { - PersistentOrderedSet(firstElement, lastElement, newMap) + } ?: run { + assert(hashMapBuilder.builtMap == null) + val newMap = hashMapBuilder.build() + val newSet = PersistentOrderedSet(firstElement, lastElement, newMap) + builtSet = newSet + newSet } - return set } override fun contains(element: E): Boolean { @@ -37,6 +40,7 @@ internal class PersistentOrderedSetBuilder(private var set: PersistentOrdered if (hashMapBuilder.containsKey(element)) { return false } + builtSet = null if (isEmpty()) { firstElement = element lastElement = element @@ -56,7 +60,7 @@ internal class PersistentOrderedSetBuilder(private var set: PersistentOrdered override fun remove(element: E): Boolean { val links = hashMapBuilder.remove(element) ?: return false - + builtSet = null if (links.hasPrevious) { val previousLinks = hashMapBuilder[links.previous]!! // assert(previousLinks.next == element) @@ -78,6 +82,9 @@ internal class PersistentOrderedSetBuilder(private var set: PersistentOrdered } override fun clear() { + if (hashMapBuilder.isNotEmpty()) { + builtSet = null + } hashMapBuilder.clear() firstElement = EndOfChain lastElement = EndOfChain From e69326fff0d95a689e6f872edeb22bbf466b0058 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 21 Aug 2024 10:01:30 +0800 Subject: [PATCH 138/162] Convert all Gradle build scripts to kts --- benchmarks/runner/build.gradle | 111 ------------------------- benchmarks/runner/build.gradle.kts | 126 +++++++++++++++++++++++++++++ settings.gradle | 14 ---- settings.gradle.kts | 16 ++++ 4 files changed, 142 insertions(+), 125 deletions(-) delete mode 100644 benchmarks/runner/build.gradle create mode 100644 benchmarks/runner/build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/benchmarks/runner/build.gradle b/benchmarks/runner/build.gradle deleted file mode 100644 index 9d7f3728..00000000 --- a/benchmarks/runner/build.gradle +++ /dev/null @@ -1,111 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib" - implementation 'org.openjdk.jmh:jmh-core:1.21' - - runtimeOnly project(path: ':benchmarks', configuration: 'benchmarksJar') - runtimeOnly project(path: ':kotlinx-collections-immutable') -} - -sourceSets.main.kotlin.srcDirs "src" - -// map -task benchmarkHashMap(type: JavaExec, group: "Benchmark") { - main = 'runners.HashMapRunnerKt' -} - -task benchmarkHashMapBuilder(type: JavaExec, group: "Benchmark") { - main = 'runners.HashMapBuilderRunnerKt' -} - -task benchmarkOrderedMap(type: JavaExec, group: "Benchmark") { - main = 'runners.OrderedMapRunnerKt' -} - -task benchmarkOrderedMapBuilder(type: JavaExec, group: "Benchmark") { - main = 'runners.OrderedMapBuilderRunnerKt' -} - -task benchmarkAllMaps(group: "Benchmark") { - dependsOn benchmarkHashMap - dependsOn benchmarkHashMapBuilder - dependsOn benchmarkOrderedMap - dependsOn benchmarkOrderedMapBuilder -} - -// set -task benchmarkHashSet(type: JavaExec, group: "Benchmark") { - main = 'runners.HashSetRunnerKt' -} - -task benchmarkHashSetBuilder(type: JavaExec, group: "Benchmark") { - main = 'runners.HashSetBuilderRunnerKt' -} - -task benchmarkOrderedSet(type: JavaExec, group: "Benchmark") { - main = 'runners.OrderedSetRunnerKt' -} - -task benchmarkOrderedSetBuilder(type: JavaExec, group: "Benchmark") { - main = 'runners.OrderedSetBuilderRunnerKt' -} - -task benchmarkAllSets(group: "Benchmark") { - dependsOn benchmarkHashSet - dependsOn benchmarkHashSetBuilder - dependsOn benchmarkOrderedSet - dependsOn benchmarkOrderedSetBuilder -} - -// list -task benchmarkList(type: JavaExec, group: "Benchmark") { - main = 'runners.ListRunnerKt' -} - -task benchmarkListBuilder(type: JavaExec, group: "Benchmark") { - main = 'runners.ListBuilderRunnerKt' -} - -task benchmarkAllLists(group: "Benchmark") { - dependsOn benchmarkList - dependsOn benchmarkListBuilder -} - -// all -task benchmarkAll(group: "Benchmark") { - dependsOn benchmarkAllMaps - dependsOn benchmarkAllSets - dependsOn benchmarkAllLists -} - - -// configure runner tasks - -def benchmarkParams = [ - 'remote', - 'forks', - 'measurementIterations', - 'measurementTime', - 'warmupIterations', - 'warmupTime', -// 'exclude', -// 'include', - 'size', - 'hashCodeType', - 'immutablePercentage' -] - -tasks.withType(JavaExec) { - if (group == "Benchmark") { - classpath = sourceSets.main.runtimeClasspath - - benchmarkParams.forEach { param -> - if (project.hasProperty(param)) { - systemProperty(param, project.property(param)) - } - } - } -} \ No newline at end of file diff --git a/benchmarks/runner/build.gradle.kts b/benchmarks/runner/build.gradle.kts new file mode 100644 index 00000000..3035e3db --- /dev/null +++ b/benchmarks/runner/build.gradle.kts @@ -0,0 +1,126 @@ +plugins { + id("org.jetbrains.kotlin.jvm") +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib") + implementation("org.openjdk.jmh:jmh-core:1.21") + + runtimeOnly(project(path = ":benchmarks", configuration = "benchmarksJar")) + runtimeOnly(project(":kotlinx-collections-immutable")) +} + +sourceSets.main { + kotlin.srcDirs("src") +} + +// map +val benchmarkHashMap by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.HashMapRunnerKt") +} + +val benchmarkHashMapBuilder by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.HashMapBuilderRunnerKt") +} + +val benchmarkOrderedMap by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.OrderedMapRunnerKt") +} + +val benchmarkOrderedMapBuilder by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.OrderedMapBuilderRunnerKt") +} + +val benchmarkAllMaps by tasks.registering { + group = "Benchmark" + dependsOn(benchmarkHashMap) + dependsOn(benchmarkHashMapBuilder) + dependsOn(benchmarkOrderedMap) + dependsOn(benchmarkOrderedMapBuilder) +} + +// set +val benchmarkHashSet by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.HashSetRunnerKt") +} + +val benchmarkHashSetBuilder by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.HashSetBuilderRunnerKt") +} + +val benchmarkOrderedSet by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.OrderedSetRunnerKt") +} + +val benchmarkOrderedSetBuilder by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.OrderedSetBuilderRunnerKt") +} + +val benchmarkAllSets by tasks.registering { + group = "Benchmark" + dependsOn(benchmarkHashSet) + dependsOn(benchmarkHashSetBuilder) + dependsOn(benchmarkOrderedSet) + dependsOn(benchmarkOrderedSetBuilder) +} + +// list +val benchmarkList by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.ListRunnerKt") +} + +val benchmarkListBuilder by tasks.registering(JavaExec::class) { + group = "Benchmark" + mainClass.set("runners.ListBuilderRunnerKt") +} + +val benchmarkAllLists by tasks.registering { + group = "Benchmark" + dependsOn(benchmarkList) + dependsOn(benchmarkListBuilder) +} + +// all +val benchmarkAll by tasks.registering { + group = "Benchmark" + dependsOn(benchmarkAllMaps) + dependsOn(benchmarkAllSets) + dependsOn(benchmarkAllLists) +} + +// configure runner tasks + +val benchmarkParams = listOf( + "remote", + "forks", + "measurementIterations", + "measurementTime", + "warmupIterations", + "warmupTime", +// "exclude", +// "include", + "size", + "hashCodeType", + "immutablePercentage" +) + +tasks.withType().configureEach { + if (group == "Benchmark") { + classpath = sourceSets.main.get().runtimeClasspath + + benchmarkParams.forEach { param -> + if (project.hasProperty(param)) { + systemProperty(param, requireNotNull(project.property(param))) + } + } + } +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 3708b453..00000000 --- a/settings.gradle +++ /dev/null @@ -1,14 +0,0 @@ -pluginManagement { - repositories { - maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/kotlinx/maven' } - gradlePluginPortal() - } -} - -rootProject.name = 'Kotlin-Immutable-Collections' // TODO: Make readable name when it's not used in js module names - -include ':core' -project(":core").name='kotlinx-collections-immutable' - -include ':benchmarks' -include ':benchmarks:runner' \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..1d4d5c9d --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlinx/maven") + gradlePluginPortal() + } +} + +rootProject.name = "Kotlin-Immutable-Collections" // TODO: Make readable name when it's not used in js module names + +include(":core") +project(":core").name="kotlinx-collections-immutable" + +include( + ":benchmarks", + ":benchmarks:runner" +) From 5a93e2b437d16c5e9b3f40f5dbc15b925a254ef3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 5 Sep 2024 04:13:14 +0800 Subject: [PATCH 139/162] Simple property assignments and migrations --- benchmarks/build.gradle.kts | 2 ++ benchmarks/runner/build.gradle.kts | 20 ++++++++++---------- core/build.gradle.kts | 7 +++++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index da19e723..b489c25f 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,5 +1,6 @@ import kotlinx.benchmark.gradle.JvmBenchmarkTarget import org.gradle.jvm.tasks.Jar +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { id("kotlin-multiplatform") @@ -30,6 +31,7 @@ kotlin { } } + @OptIn(ExperimentalWasmDsl::class) wasmJs { nodejs() } diff --git a/benchmarks/runner/build.gradle.kts b/benchmarks/runner/build.gradle.kts index 3035e3db..254851eb 100644 --- a/benchmarks/runner/build.gradle.kts +++ b/benchmarks/runner/build.gradle.kts @@ -17,22 +17,22 @@ sourceSets.main { // map val benchmarkHashMap by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.HashMapRunnerKt") + mainClass = "runners.HashMapRunnerKt" } val benchmarkHashMapBuilder by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.HashMapBuilderRunnerKt") + mainClass = "runners.HashMapBuilderRunnerKt" } val benchmarkOrderedMap by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.OrderedMapRunnerKt") + mainClass = "runners.OrderedMapRunnerKt" } val benchmarkOrderedMapBuilder by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.OrderedMapBuilderRunnerKt") + mainClass = "runners.OrderedMapBuilderRunnerKt" } val benchmarkAllMaps by tasks.registering { @@ -46,22 +46,22 @@ val benchmarkAllMaps by tasks.registering { // set val benchmarkHashSet by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.HashSetRunnerKt") + mainClass = "runners.HashSetRunnerKt" } val benchmarkHashSetBuilder by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.HashSetBuilderRunnerKt") + mainClass = "runners.HashSetBuilderRunnerKt" } val benchmarkOrderedSet by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.OrderedSetRunnerKt") + mainClass = "runners.OrderedSetRunnerKt" } val benchmarkOrderedSetBuilder by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.OrderedSetBuilderRunnerKt") + mainClass = "runners.OrderedSetBuilderRunnerKt" } val benchmarkAllSets by tasks.registering { @@ -75,12 +75,12 @@ val benchmarkAllSets by tasks.registering { // list val benchmarkList by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.ListRunnerKt") + mainClass = "runners.ListRunnerKt" } val benchmarkListBuilder by tasks.registering(JavaExec::class) { group = "Benchmark" - mainClass.set("runners.ListBuilderRunnerKt") + mainClass = "runners.ListBuilderRunnerKt" } val benchmarkAllLists by tasks.registering { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e3f2e6f7..534c3bf1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,4 +1,5 @@ import kotlinx.team.infra.mavenPublicationsPom +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { id("kotlin-multiplatform") @@ -6,11 +7,11 @@ plugins { } base { - archivesBaseName = "kotlinx-collections-immutable" // doesn't work + archivesName = "kotlinx-collections-immutable" // doesn't work } mavenPublicationsPom { - description.set("Kotlin Immutable Collections multiplatform library") + description = "Kotlin Immutable Collections multiplatform library" } kotlin { @@ -69,6 +70,7 @@ kotlin { } } + @OptIn(ExperimentalWasmDsl::class) wasmJs { nodejs { testTask { @@ -79,6 +81,7 @@ kotlin { } } + @OptIn(ExperimentalWasmDsl::class) wasmWasi { nodejs { testTask { From 766ceef1a41ae0db83aaaf2af41689e89b856141 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 16 Sep 2024 13:35:35 +0300 Subject: [PATCH 140/162] Update README.md and CHANGELOG.md for 0.3.8 release 0.3.8 was released from fe7b163d --- CHANGELOG.md | 7 +++++++ README.md | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a863a9f1..c00d579f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## 0.3.8 + +- Add extension functions to convert Array to persistent collections [#159](https://github.com/Kotlin/kotlinx.collections.immutable/issues/159) +- Don't allocate temporary buffer in SmallPersistentVector.removeAll [#164](https://github.com/Kotlin/kotlinx.collections.immutable/pull/164) +- Avoid creating new PersistentList instance when adding empty collection [#176](https://github.com/Kotlin/kotlinx.collections.immutable/pull/176) +- Fix memory leak in builders [#193](https://github.com/Kotlin/kotlinx.collections.immutable/pull/193) + ## 0.3.7 - Upgrade Kotlin version up to 1.9.21 diff --git a/README.md b/README.md index 96ec1a42..cf7adfb4 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8") } } } @@ -153,7 +153,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.7 + 0.3.8 ``` From 1c453de9ffa69bb19a4da4dc2066d3fb9351e296 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Fri, 6 Sep 2024 16:08:09 +0300 Subject: [PATCH 141/162] Show TeamCity build status GitHub PRs --- .teamcity/additionalConfiguration.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.teamcity/additionalConfiguration.kt b/.teamcity/additionalConfiguration.kt index 6fca4acb..d5043b78 100644 --- a/.teamcity/additionalConfiguration.kt +++ b/.teamcity/additionalConfiguration.kt @@ -4,7 +4,21 @@ */ import jetbrains.buildServer.configs.kotlin.Project +import jetbrains.buildServer.configs.kotlin.DslContext +import jetbrains.buildServer.configs.kotlin.buildFeatures.commitStatusPublisher fun Project.additionalConfiguration() { subProject(benchmarksProject(knownBuilds.buildVersion)) + + knownBuilds.buildAll.features { + commitStatusPublisher { + vcsRootExtId = "${DslContext.settingsRoot.id}" + publisher = github { + githubUrl = "https://api.github.com" + authType = storedToken { + tokenId = "tc_token_id:CID_7db3007c46f7e30124f81ef54591b223:-1:f604c3ec-6391-4e29-a6e4-e59397b4622d" + } + } + } + } } \ No newline at end of file From 06d28543d0d2e1004b835106833ce1f9d1b38408 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Wed, 13 Nov 2024 16:03:13 +0100 Subject: [PATCH 142/162] Do not use mocha for wasm targets It is senseless because for wasm target there is special test framework, and nodejs+wasm can be used only with that special test framework KotlinWasmNode. --- core/build.gradle.kts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 534c3bf1..3330a584 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -73,22 +73,12 @@ kotlin { @OptIn(ExperimentalWasmDsl::class) wasmJs { nodejs { - testTask { - useMocha { - timeout = "30000" - } - } } } @OptIn(ExperimentalWasmDsl::class) wasmWasi { nodejs { - testTask { - useMocha { - timeout = "30000" - } - } } } From abcda7361a650697512bd822fb6b8795c84271c3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 4 Sep 2024 12:20:17 +0300 Subject: [PATCH 143/162] Bump Kotlin version to 2.0.20 --- benchmarks/build.gradle.kts | 2 +- build.gradle.kts | 2 +- core/api/kotlinx-collections-immutable.klib.api | 6 ++++++ core/build.gradle.kts | 2 +- core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt | 1 - .../commonTest/src/stress/list/PersistentListBuilderTest.kt | 2 -- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index b489c25f..2f0f6500 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,6 +1,6 @@ import kotlinx.benchmark.gradle.JvmBenchmarkTarget import org.gradle.jvm.tasks.Jar -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { id("kotlin-multiplatform") diff --git a/build.gradle.kts b/build.gradle.kts index 68584e15..dde28a38 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20") } } diff --git a/core/api/kotlinx-collections-immutable.klib.api b/core/api/kotlinx-collections-immutable.klib.api index ac692d0d..4bd7b493 100644 --- a/core/api/kotlinx-collections-immutable.klib.api +++ b/core/api/kotlinx-collections-immutable.klib.api @@ -120,6 +120,9 @@ final class <#A: kotlin/Any?, #B: out kotlin/Any?> kotlinx.collections.immutable final fun hashCode(): kotlin/Int // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.hashCode|hashCode(){}[0] final fun isEmpty(): kotlin/Boolean // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.isEmpty|isEmpty(){}[0] final fun toString(): kotlin/String // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.toString|toString(){}[0] + + // Targets: [js] + final fun asJsReadonlyMapView(): kotlin.js.collections/JsReadonlyMap<#A, #B> // kotlinx.collections.immutable.adapters/ImmutableMapAdapter.asJsReadonlyMapView|asJsReadonlyMapView(){}[0] } final class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableListAdapter : kotlin.collections/List<#A>, kotlinx.collections.immutable/ImmutableList<#A> { // kotlinx.collections.immutable.adapters/ImmutableListAdapter|null[0] @@ -141,6 +144,9 @@ final class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableLi final fun listIterator(kotlin/Int): kotlin.collections/ListIterator<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.listIterator|listIterator(kotlin.Int){}[0] final fun subList(kotlin/Int, kotlin/Int): kotlinx.collections.immutable/ImmutableList<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.subList|subList(kotlin.Int;kotlin.Int){}[0] final fun toString(): kotlin/String // kotlinx.collections.immutable.adapters/ImmutableListAdapter.toString|toString(){}[0] + + // Targets: [js] + final fun asJsReadonlyArrayView(): kotlin.js.collections/JsReadonlyArray<#A> // kotlinx.collections.immutable.adapters/ImmutableListAdapter.asJsReadonlyArrayView|asJsReadonlyArrayView(){}[0] } final class <#A: kotlin/Any?> kotlinx.collections.immutable.adapters/ImmutableSetAdapter : kotlinx.collections.immutable.adapters/ImmutableCollectionAdapter<#A>, kotlinx.collections.immutable/ImmutableSet<#A> { // kotlinx.collections.immutable.adapters/ImmutableSetAdapter|null[0] diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 3330a584..1d091c6e 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,5 @@ import kotlinx.team.infra.mavenPublicationsPom -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { id("kotlin-multiplatform") diff --git a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt index 936b3195..95187e93 100644 --- a/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt +++ b/core/commonTest/src/stress/ExecutionTimeMeasuringTest.kt @@ -10,7 +10,6 @@ import kotlin.test.BeforeTest import kotlin.time.* import kotlin.time.Duration.Companion.seconds -@OptIn(ExperimentalTime::class) abstract class ExecutionTimeMeasuringTest { private var clockMark: TimeMark? = null diff --git a/core/commonTest/src/stress/list/PersistentListBuilderTest.kt b/core/commonTest/src/stress/list/PersistentListBuilderTest.kt index 19ab0aef..ec587c94 100644 --- a/core/commonTest/src/stress/list/PersistentListBuilderTest.kt +++ b/core/commonTest/src/stress/list/PersistentListBuilderTest.kt @@ -17,9 +17,7 @@ import tests.testOn import kotlin.random.Random import kotlin.random.nextInt import kotlin.test.* -import kotlin.time.ExperimentalTime -@OptIn(ExperimentalTime::class) class PersistentListBuilderTest : ExecutionTimeMeasuringTest() { @Test From 19bf6b2297ca60cb71a0ac1b537cd6f128f8e9c3 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 4 Sep 2024 12:21:13 +0300 Subject: [PATCH 144/162] Adopt build scripts for K2 build infrastructure --- build.gradle.kts | 7 ++++++- gradle.properties | 2 ++ settings.gradle.kts | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index dde28a38..caad5cec 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.20") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${rootProject.properties["kotlin_version"]}") } } @@ -37,6 +37,11 @@ apiValidation { allprojects { repositories { mavenCentral() + + val kotlinRepoUrl = providers.gradleProperty("kotlin_repo_url").orNull + if (kotlinRepoUrl != null) { + maven(kotlinRepoUrl) + } } tasks.withType> { diff --git a/gradle.properties b/gradle.properties index d2050515..a3771d6d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,6 @@ group=org.jetbrains.kotlinx version=0.4 versionSuffix=SNAPSHOT +kotlin_version=2.0.20 + org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 diff --git a/settings.gradle.kts b/settings.gradle.kts index 1d4d5c9d..55339c36 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,6 +2,11 @@ pluginManagement { repositories { maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlinx/maven") gradlePluginPortal() + + val kotlinRepoUrl = providers.gradleProperty("kotlin_repo_url").orNull + if (kotlinRepoUrl != null) { + maven(kotlinRepoUrl) + } } } From 35f458aa4d12cb52b40e775974d322c71b593344 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 26 Feb 2024 18:45:37 +0200 Subject: [PATCH 145/162] Remove the common `var AbstractMutableList<*>.modCount: Int` declaration In Kotlin 2.0 it is provided by stdlib. See KT-57150. --- .../implementations/immutableList/PersistentVectorBuilder.kt | 1 - core/commonMain/src/internal/commonFunctions.kt | 3 --- core/jsMain/src/internal/commonFunctions.kt | 4 ---- core/nativeMain/src/internal/commonFunctions.kt | 5 ----- core/wasmMain/src/internal/commonFunctions.kt | 4 ---- 5 files changed, 17 deletions(-) diff --git a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt index e2b35b13..a67cf52e 100644 --- a/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt +++ b/core/commonMain/src/implementations/immutableList/PersistentVectorBuilder.kt @@ -10,7 +10,6 @@ import kotlinx.collections.immutable.internal.ListImplementation.checkElementInd import kotlinx.collections.immutable.internal.ListImplementation.checkPositionIndex import kotlinx.collections.immutable.internal.MutabilityOwnership import kotlinx.collections.immutable.internal.assert -import kotlinx.collections.immutable.internal.modCount internal class PersistentVectorBuilder(vector: PersistentList, vectorRoot: Array?, diff --git a/core/commonMain/src/internal/commonFunctions.kt b/core/commonMain/src/internal/commonFunctions.kt index f8a50d51..64846611 100644 --- a/core/commonMain/src/internal/commonFunctions.kt +++ b/core/commonMain/src/internal/commonFunctions.kt @@ -6,6 +6,3 @@ package kotlinx.collections.immutable.internal internal expect fun assert(condition: Boolean) - -@Suppress("NO_ACTUAL_FOR_EXPECT") // implemented by protected property in JVM -internal expect var AbstractMutableList<*>.modCount: Int \ No newline at end of file diff --git a/core/jsMain/src/internal/commonFunctions.kt b/core/jsMain/src/internal/commonFunctions.kt index a8fd2d7e..08e3d961 100644 --- a/core/jsMain/src/internal/commonFunctions.kt +++ b/core/jsMain/src/internal/commonFunctions.kt @@ -10,7 +10,3 @@ internal actual fun assert(condition: Boolean) { throw AssertionError("Assertion failed") } } - -internal actual var AbstractMutableList<*>.modCount: Int - get() = 0 - set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file diff --git a/core/nativeMain/src/internal/commonFunctions.kt b/core/nativeMain/src/internal/commonFunctions.kt index 3b9cc7c8..a8ac6f85 100644 --- a/core/nativeMain/src/internal/commonFunctions.kt +++ b/core/nativeMain/src/internal/commonFunctions.kt @@ -7,8 +7,3 @@ package kotlinx.collections.immutable.internal @OptIn(kotlin.experimental.ExperimentalNativeApi::class) internal actual fun assert(condition: Boolean) = kotlin.assert(condition) - - -internal actual var AbstractMutableList<*>.modCount: Int - get() = 0 - set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file diff --git a/core/wasmMain/src/internal/commonFunctions.kt b/core/wasmMain/src/internal/commonFunctions.kt index a779f323..a682c617 100644 --- a/core/wasmMain/src/internal/commonFunctions.kt +++ b/core/wasmMain/src/internal/commonFunctions.kt @@ -10,7 +10,3 @@ internal actual fun assert(condition: Boolean) { throw AssertionError("Assertion failed") } } - -internal actual var AbstractMutableList<*>.modCount: Int - get() = 0 - set(@Suppress("UNUSED_PARAMETER") value) {} \ No newline at end of file From 474612ce6787c5f354a2da0c0b4dc5b6b0c2921b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 4 Sep 2024 12:03:47 +0300 Subject: [PATCH 146/162] Drop the Wasm workaround with using unstable NodeJs version In Kotlin 2.0.20 the workaround is no longer needed are it uses NodeJs 22.0.0+ --- core/build.gradle.kts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1d091c6e..8e74ce6a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -146,13 +146,3 @@ tasks { maxHeapSize = "1024m" } } - -with(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin.apply(rootProject)) { - nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2" - nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary" -} - -// Drop this when node js version become stable -tasks.withType().configureEach { - args.add("--ignore-engines") -} \ No newline at end of file From d30701c25522ae569b1b506686b671e9389cf23b Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 4 Sep 2024 12:17:32 +0300 Subject: [PATCH 147/162] Add macosArm64 target to benchmarks module This will allow running the benchmarks locally in ARM-based MacBooks. --- benchmarks/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 2f0f6500..12d833c2 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -13,6 +13,7 @@ evaluationDependsOn(":kotlinx-collections-immutable") kotlin { infra { target("macosX64") + target("macosArm64") target("linuxX64") target("mingwX64") } @@ -102,6 +103,7 @@ benchmark { register("js") register("wasmJs") register("macosX64") + register("macosArm64") register("linuxX64") register("mingwX64") } From f29c098ebd4f517982e319854dada147eab391ea Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Mon, 25 Nov 2024 16:04:06 +0200 Subject: [PATCH 148/162] Bump kotlinx-benchmark version to 0.4.13 --- benchmarks/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 12d833c2..7d689907 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -4,7 +4,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl plugins { id("kotlin-multiplatform") - id("org.jetbrains.kotlinx.benchmark") version "0.4.10" + id("org.jetbrains.kotlinx.benchmark") version "0.4.13" } @@ -47,7 +47,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.10") + implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.13") implementation(project(":kotlinx-collections-immutable")) } } From 592f05fce02a1ad9e26cc6f3fdb55cdd97910599 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 22 Jan 2025 17:16:15 -0500 Subject: [PATCH 149/162] Migrate to a new DSL from a deprecated one (#206) --- benchmarks/build.gradle.kts | 9 +++++---- build.gradle.kts | 13 +++++++++---- core/build.gradle.kts | 19 +++++++++---------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 7d689907..ea61caa0 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,6 +1,8 @@ import kotlinx.benchmark.gradle.JvmBenchmarkTarget import org.gradle.jvm.tasks.Jar +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("kotlin-multiplatform") @@ -10,6 +12,7 @@ plugins { evaluationDependsOn(":kotlinx-collections-immutable") +@OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { infra { target("macosX64") @@ -19,10 +22,8 @@ kotlin { } jvm { - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - } + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) } } diff --git a/build.gradle.kts b/build.gradle.kts index caad5cec..5d005658 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask buildscript { dependencies { @@ -44,11 +45,15 @@ allprojects { } } - tasks.withType> { - kotlinOptions.allWarningsAsErrors = true - kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + tasks.withType(KotlinCompilationTask::class).configureEach { + compilerOptions { + allWarningsAsErrors = true + freeCompilerArgs.add("-Xexpect-actual-classes") + } if (this is KotlinJsCompile) { - kotlinOptions.freeCompilerArgs += "-Xwasm-enable-array-range-checks" + compilerOptions { + freeCompilerArgs.add("-Xwasm-enable-array-range-checks") + } } } } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8e74ce6a..abd8ad74 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,5 +1,8 @@ import kotlinx.team.infra.mavenPublicationsPom +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JsModuleKind +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("kotlin-multiplatform") @@ -14,6 +17,7 @@ mavenPublicationsPom { description = "Kotlin Immutable Collections multiplatform library" } +@OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { applyDefaultHierarchyTemplate() explicitApi() @@ -46,10 +50,8 @@ kotlin { watchosDeviceArm64() jvm { - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - } + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) } } @@ -61,12 +63,9 @@ kotlin { } } } - compilations.all { - kotlinOptions { - sourceMap = true - moduleKind = "umd" - metaInfo = true - } + compilerOptions { + sourceMap = true + moduleKind.set(JsModuleKind.MODULE_UMD) } } From c57cf324b41e5e63082cc808894c980538d7cae1 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 5 Mar 2025 20:12:27 +0100 Subject: [PATCH 150/162] Enable '-Xjvm-default=disable' explicitly to prevent API dump changes (#210) The default mode is changing from 'disable' to 'enable' in KT-71768. However, core libraries will migrate to the new '-jvm-default=enable' mode explicitly, once they update to Kotlin 2.2, see KT-72051. --- build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5d005658..4a2fe4e0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask buildscript { @@ -54,10 +55,14 @@ allprojects { compilerOptions { freeCompilerArgs.add("-Xwasm-enable-array-range-checks") } + } else if (this is KotlinJvmCompile) { + compilerOptions { + freeCompilerArgs.add("-Xjvm-default=disable") + } } } } tasks.withType().configureEach { args.add("--ignore-engines") -} \ No newline at end of file +} From 4af5b474f21f81911ba13584fc38330883a44318 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 6 Mar 2025 18:48:31 +0100 Subject: [PATCH 151/162] Use another KotlinJvmCompile to change JVM compiler options (#211) Apparently the one in the `dsl` package is deprecated with error since 2.2 (KT-68597): https://github.com/JetBrains/kotlin/blob/35162db3a4162849366a7e1175d74e0fadbb8732/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinCompile.kt#L20 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4a2fe4e0..b205622d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile -import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask buildscript { From 7dbd115737864c48d28a32ef171ea90599cbe771 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Wed, 2 Apr 2025 13:06:22 -0400 Subject: [PATCH 152/162] Configure native targets directly, without using the infra plugin (#215) --- benchmarks/build.gradle.kts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index ea61caa0..3826c92b 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -14,12 +14,10 @@ evaluationDependsOn(":kotlinx-collections-immutable") @OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { - infra { - target("macosX64") - target("macosArm64") - target("linuxX64") - target("mingwX64") - } + macosX64() + macosArm64() + linuxX64() + mingwX64() jvm { compilerOptions { @@ -115,4 +113,4 @@ val benchmarksJar: Configuration by configurations.creating afterEvaluate { val jvmBenchmarkJar by tasks.getting(Jar::class) artifacts.add("benchmarksJar", jvmBenchmarkJar) -} \ No newline at end of file +} From 4524d61dbbdaf794bff005df6ae217ba392ef10b Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 3 Apr 2025 07:57:20 -0400 Subject: [PATCH 153/162] Update kotlinx.team.infra plugin (#216) * Update kotlinx.team.infra plugin Update kotlinx.team.infra to a version that no longer provides native targets and source-set setup functionality that is incompatible with KGP 2.2+. * Workaround KT-61313, which is reproducible since infra plugin 0.4.0-dev-81 --- build.gradle.kts | 2 +- core/build.gradle.kts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index b205622d..bb7de9d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ buildscript { } plugins { - id("kotlinx.team.infra") version "0.4.0-dev-80" + id("kotlinx.team.infra") version "0.4.0-dev-85" id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index abd8ad74..53a602b2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -144,4 +144,15 @@ tasks { named("jvmTest", Test::class) { maxHeapSize = "1024m" } + + // See https://youtrack.jetbrains.com/issue/KT-61313 + withType().configureEach { + val pubName = name.removePrefix("sign").removeSuffix("Publication") + findByName("linkDebugTest$pubName")?.let { + mustRunAfter(it) + } + findByName("compileTestKotlin$pubName")?.let { + mustRunAfter(it) + } + } } From 6e6674871e28ad8b0489201bb151bed40d8500ba Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Thu, 27 Mar 2025 16:12:00 -0400 Subject: [PATCH 154/162] Update Kotlin and dependencies Kotlin updated to 2.1.20 BCV to 0.17.0 JMH to 1.37 --- benchmarks/build.gradle.kts | 2 +- build.gradle.kts | 2 +- gradle.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 3826c92b..bee9cafc 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -97,7 +97,7 @@ benchmark { targets { register("jvm") { this as JvmBenchmarkTarget - jmhVersion = "1.21" + jmhVersion = "1.37" } register("js") register("wasmJs") diff --git a/build.gradle.kts b/build.gradle.kts index bb7de9d9..0ed10d89 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ buildscript { plugins { id("kotlinx.team.infra") version "0.4.0-dev-85" - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.17.0" } infra { diff --git a/gradle.properties b/gradle.properties index a3771d6d..d6c5c872 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,6 +2,6 @@ group=org.jetbrains.kotlinx version=0.4 versionSuffix=SNAPSHOT -kotlin_version=2.0.20 +kotlin_version=2.1.20 org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 From 4528d5352f405fab2c86b16bab0edc728b9a1a04 Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Mon, 28 Apr 2025 19:53:31 +0400 Subject: [PATCH 155/162] Equal PersistentOrderedSets are not equal (#217) Fixes tree invariant and Iterations issues Fixes #204 --- ...ersistentHashMapBuilderContentIterators.kt | 21 ++- .../implementations/immutableMap/TrieNode.kt | 28 +--- .../map/PersistentHashMapBuilderTest.kt | 121 ++++++++++++++++++ .../src/contract/map/PersistentHashMapTest.kt | 35 +++++ .../set/PersistentHashSetBuilderTest.kt | 119 +++++++++++++++++ .../src/contract/set/PersistentHashSetTest.kt | 30 +++++ .../contract/set/PersistentOrderedSetTest.kt | 33 +++++ 7 files changed, 362 insertions(+), 25 deletions(-) create mode 100644 core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt create mode 100644 core/commonTest/src/contract/map/PersistentHashMapTest.kt create mode 100644 core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt create mode 100644 core/commonTest/src/contract/set/PersistentHashSetTest.kt create mode 100644 core/commonTest/src/contract/set/PersistentOrderedSetTest.kt diff --git a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt index 986369ca..ccc03ab5 100644 --- a/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt +++ b/core/commonMain/src/implementations/immutableMap/PersistentHashMapBuilderContentIterators.kt @@ -57,7 +57,7 @@ internal open class PersistentHashMapBuilderBaseIterator( val currentKey = currentKey() builder.remove(lastIteratedKey) - resetPath(currentKey.hashCode(), builder.node, currentKey, 0) + resetPath(currentKey.hashCode(), builder.node, currentKey, 0, lastIteratedKey.hashCode(), afterRemove = true) } else { builder.remove(lastIteratedKey) } @@ -82,7 +82,7 @@ internal open class PersistentHashMapBuilderBaseIterator( expectedModCount = builder.modCount } - private fun resetPath(keyHash: Int, node: TrieNode<*, *>, key: K, pathIndex: Int) { + private fun resetPath(keyHash: Int, node: TrieNode<*, *>, key: K, pathIndex: Int, removedKeyHash: Int = 0, afterRemove: Boolean = false) { val shift = pathIndex * LOG_MAX_BRANCHING_FACTOR if (shift > MAX_SHIFT) { // collision @@ -99,6 +99,21 @@ internal open class PersistentHashMapBuilderBaseIterator( if (node.hasEntryAt(keyPositionMask)) { // key is directly in buffer val keyIndex = node.entryKeyIndex(keyPositionMask) + // After removing an element, we need to handle node promotion properly to maintain a correct iteration order. + // `removedKeyPositionMask` represents the bit position of the removed key's hash at the current level. + // This is needed to detect if the current key was potentially promoted from a deeper level. + val removedKeyPositionMask = if (afterRemove) 1 shl indexSegment(removedKeyHash, shift) else 0 + + // Check if the removed key is at the same position as the current key and was previously at a deeper level. + // This indicates a node promotion occurred during removal, + // and we need to handle it in a special way to prevent re-traversing already visited elements. + if (keyPositionMask == removedKeyPositionMask && pathIndex < pathLastIndex) { + // Instead of traversing the normal way, we create a special path entry at the previous depth + // that points directly to the promoted entry, maintaining the original iteration sequence. + path[pathLastIndex].reset(arrayOf(node.buffer[keyIndex], node.buffer[keyIndex + 1]), ENTRY_SIZE) + return + } + // assert(node.keyAtIndex(keyIndex) == key) path[pathIndex].reset(node.buffer, ENTRY_SIZE * node.entryCount(), keyIndex) @@ -111,7 +126,7 @@ internal open class PersistentHashMapBuilderBaseIterator( val nodeIndex = node.nodeIndex(keyPositionMask) val targetNode = node.nodeAtIndex(nodeIndex) path[pathIndex].reset(node.buffer, ENTRY_SIZE * node.entryCount(), nodeIndex) - resetPath(keyHash, targetNode, key, pathIndex + 1) + resetPath(keyHash, targetNode, key, pathIndex + 1, removedKeyHash, afterRemove) } private fun checkNextWasInvoked() { diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index 9fa6898b..e00eaa7a 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -180,7 +180,7 @@ internal class TrieNode( } /** The given [newNode] must not be a part of any persistent map instance. */ - private fun updateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode): TrieNode { + private fun updateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode, owner: MutabilityOwnership? = null): TrieNode { // assert(buffer[nodeIndex] !== newNode) val newNodeBuffer = newNode.buffer if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) { @@ -192,30 +192,14 @@ internal class TrieNode( val keyIndex = entryKeyIndex(positionMask) val newBuffer = buffer.replaceNodeWithEntry(nodeIndex, keyIndex, newNodeBuffer[0], newNodeBuffer[1]) - return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer) + return TrieNode(dataMap xor positionMask, nodeMap xor positionMask, newBuffer, owner) } - val newBuffer = buffer.copyOf(buffer.size) - newBuffer[nodeIndex] = newNode - return TrieNode(dataMap, nodeMap, newBuffer) - } - - /** The given [newNode] must not be a part of any persistent map instance. */ - private fun mutableUpdateNodeAtIndex(nodeIndex: Int, newNode: TrieNode, owner: MutabilityOwnership): TrieNode { - assert(newNode.ownedBy === owner) -// assert(buffer[nodeIndex] !== newNode) - - // nodes (including collision nodes) that have only one entry are upped if they have no siblings - if (buffer.size == 1 && newNode.buffer.size == ENTRY_SIZE && newNode.nodeMap == 0) { -// assert(dataMap == 0 && nodeMap xor positionMask == 0) - newNode.dataMap = nodeMap - return newNode - } - - if (ownedBy === owner) { + if (owner != null && ownedBy === owner) { buffer[nodeIndex] = newNode return this } + val newBuffer = buffer.copyOf() newBuffer[nodeIndex] = newNode return TrieNode(dataMap, nodeMap, newBuffer, owner) @@ -716,7 +700,7 @@ internal class TrieNode( if (targetNode === newNode) { return this } - return mutableUpdateNodeAtIndex(nodeIndex, newNode, mutator.ownership) + return updateNodeAtIndex(nodeIndex, keyPositionMask, newNode, mutator.ownership) } // key is absent @@ -791,7 +775,7 @@ internal class TrieNode( newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, positionMask, owner) targetNode !== newNode -> - mutableUpdateNodeAtIndex(nodeIndex, newNode, owner) + updateNodeAtIndex(nodeIndex, positionMask, newNode, owner) else -> this } diff --git a/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt new file mode 100644 index 00000000..732eb81a --- /dev/null +++ b/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.map + +import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap +import kotlinx.collections.immutable.persistentHashMapOf +import tests.stress.IntWrapper +import kotlin.collections.iterator +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class PersistentHashMapBuilderTest { + + @Test + fun `should correctly iterate after removing integer key and promotion colliding key during iteration`() { + val removedKey = 0 + val map: PersistentHashMap = + persistentHashMapOf(1 to "a", 2 to "b", 3 to "c", removedKey to "y", 32 to "z") + as PersistentHashMap + + validatePromotion(map, removedKey) + } + + @Test + fun `should correctly iterate after removing IntWrapper key and promotion colliding key during iteration`() { + val removedKey = IntWrapper(0, 0) + val map: PersistentHashMap = persistentHashMapOf( + removedKey to "a", + IntWrapper(1, 0) to "b", + IntWrapper(2, 32) to "c", + IntWrapper(3, 32) to "d" + ) as PersistentHashMap + + validatePromotion(map, removedKey) + } + + private fun validatePromotion(map: PersistentHashMap, removedKey: K) { + val builder = map.builder() + val iterator = builder.entries.iterator() + + val expectedCount = map.size + var actualCount = 0 + + while (iterator.hasNext()) { + val (key, _) = iterator.next() + if (key == removedKey) { + iterator.remove() + } + actualCount++ + } + + val resultMap = builder.build() + for ((key, value) in map) { + if (key != removedKey) { + assertTrue(key in resultMap) + assertEquals(resultMap[key], value) + } else { + assertFalse(key in resultMap) + } + } + + assertEquals(expectedCount, actualCount) + } + + @Test + fun `removing twice on iterators throws IllegalStateException`() { + val map: PersistentHashMap = + persistentHashMapOf(1 to "a", 2 to "b", 3 to "c", 0 to "y", 32 to "z") as PersistentHashMap + val builder = map.builder() + val iterator = builder.entries.iterator() + + assertFailsWith { + while (iterator.hasNext()) { + val (key, _) = iterator.next() + if (key == 0) iterator.remove() + if (key == 0) { + iterator.remove() + iterator.remove() + } + } + } + } + + @Test + fun `removing elements from different iterators throws ConcurrentModificationException`() { + val map: PersistentHashMap = + persistentHashMapOf(1 to "a", 2 to "b", 3 to "c", 0 to "y", 32 to "z") as PersistentHashMap + val builder = map.builder() + val iterator1 = builder.entries.iterator() + val iterator2 = builder.entries.iterator() + + assertFailsWith { + while (iterator1.hasNext()) { + val (key, _) = iterator1.next() + iterator2.next() + if (key == 0) iterator1.remove() + if (key == 2) iterator2.remove() + } + } + } + + @Test + fun `removing element from one iterator and accessing another throws ConcurrentModificationException`() { + val map = persistentHashMapOf(1 to "a", 2 to "b", 3 to "c") + val builder = map.builder() + val iterator1 = builder.entries.iterator() + val iterator2 = builder.entries.iterator() + + assertFailsWith { + iterator1.next() + iterator1.remove() + iterator2.next() + } + } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/map/PersistentHashMapTest.kt b/core/commonTest/src/contract/map/PersistentHashMapTest.kt new file mode 100644 index 00000000..c1152f4a --- /dev/null +++ b/core/commonTest/src/contract/map/PersistentHashMapTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.map + +import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap +import kotlinx.collections.immutable.persistentHashMapOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PersistentHashMapTest { + + @Test + fun `if the collision is of size 2 and one of the keys is removed the remaining key must be promoted`() { + val map1: PersistentHashMap = + persistentHashMapOf(-1 to "a", 0 to "b", 32 to "c") as PersistentHashMap + val builder = map1.builder() + val map2 = builder.build() + + assertTrue(map1.equals(builder)) + assertEquals(map1, map2.toMap()) + assertEquals(map1, map2) + + val map3 = map1.remove(0) + builder.remove(0) + val map4 = builder.build() + + assertTrue(map3.equals(builder)) + assertEquals(map3, map4.toMap()) + assertEquals(map3, map4) + } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt b/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt new file mode 100644 index 00000000..df1df86a --- /dev/null +++ b/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.set + +import kotlinx.collections.immutable.implementations.immutableSet.PersistentHashSet +import kotlinx.collections.immutable.persistentHashSetOf +import tests.stress.IntWrapper +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class PersistentHashSetBuilderTest { + + @Test + fun `should correctly iterate after removing integer element`() { + val removedElement = 0 + val set: PersistentHashSet = + persistentHashSetOf(1, 2, 3, removedElement, 32) + as PersistentHashSet + + validate(set, removedElement) + } + + @Test + fun `should correctly iterate after removing IntWrapper element`() { + val removedElement = IntWrapper(0, 0) + val set: PersistentHashSet = persistentHashSetOf( + removedElement, + IntWrapper(1, 0), + IntWrapper(2, 32), + IntWrapper(3, 32) + ) as PersistentHashSet + + validate(set, removedElement) + } + + private fun validate(set: PersistentHashSet, removedElement: E) { + val builder = set.builder() + val iterator = builder.iterator() + + val expectedCount = set.size + var actualCount = 0 + + while (iterator.hasNext()) { + val element = iterator.next() + if (element == removedElement) { + iterator.remove() + } + actualCount++ + } + + val resultSet = builder.build() + for (element in set) { + if (element != removedElement) { + assertTrue(element in resultSet) + } else { + assertFalse(element in resultSet) + } + } + + assertEquals(expectedCount, actualCount) + } + + @Test + fun `removing twice on iterators throws IllegalStateException`() { + val set: PersistentHashSet = + persistentHashSetOf(1, 2, 3, 0, 32) as PersistentHashSet + val builder = set.builder() + val iterator = builder.iterator() + + assertFailsWith { + while (iterator.hasNext()) { + val element = iterator.next() + if (element == 0) iterator.remove() + if (element == 0) { + iterator.remove() + iterator.remove() + } + } + } + } + + @Test + fun `removing elements from different iterators throws ConcurrentModificationException`() { + val set: PersistentHashSet = + persistentHashSetOf(1, 2, 3, 0, 32) as PersistentHashSet + val builder = set.builder() + val iterator1 = builder.iterator() + val iterator2 = builder.iterator() + + assertFailsWith { + while (iterator1.hasNext()) { + val element1 = iterator1.next() + iterator2.next() + if (element1 == 0) iterator1.remove() + if (element1 == 2) iterator2.remove() + } + } + } + + @Test + fun `removing element from one iterator and accessing another throws ConcurrentModificationException`() { + val set = persistentHashSetOf(1, 2, 3) + val builder = set.builder() + val iterator1 = builder.iterator() + val iterator2 = builder.iterator() + + assertFailsWith { + iterator1.next() + iterator1.remove() + iterator2.next() + } + } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/set/PersistentHashSetTest.kt b/core/commonTest/src/contract/set/PersistentHashSetTest.kt new file mode 100644 index 00000000..d1a5c777 --- /dev/null +++ b/core/commonTest/src/contract/set/PersistentHashSetTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.set + +import kotlinx.collections.immutable.persistentHashSetOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PersistentHashSetTest { + + @Test + fun `persistentHashSet and their builder should be equal before and after modification`() { + val set1 = persistentHashSetOf(-1, 0, 32) + val builder = set1.builder() + + assertTrue(set1.equals(builder)) + assertEquals(set1, builder.build()) + assertEquals(set1, builder.build().toSet()) + + val set2 = set1.remove(0) + builder.remove(0) + + assertEquals(set2, builder.build().toSet()) + assertEquals(set2, builder.build()) + } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/set/PersistentOrderedSetTest.kt b/core/commonTest/src/contract/set/PersistentOrderedSetTest.kt new file mode 100644 index 00000000..a3d639d9 --- /dev/null +++ b/core/commonTest/src/contract/set/PersistentOrderedSetTest.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.set + +import kotlinx.collections.immutable.persistentSetOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PersistentOrderedSetTest { + + /** + * Test from issue: https://github.com/Kotlin/kotlinx.collections.immutable/issues/204 + */ + @Test + fun `persistentOrderedSet and their builder should be equal before and after modification`() { + val set1 = persistentSetOf(-486539264, 16777216, 0, 67108864) + val builder = set1.builder() + + assertTrue(set1.equals(builder)) + assertEquals(set1, builder.build()) + assertEquals(set1, builder.build().toSet()) + + val set2 = set1.remove(0) + builder.remove(0) + + assertEquals(set2, builder.build().toSet()) + assertEquals(set2, builder.build()) + } +} \ No newline at end of file From 5c3af1e76f0e55f4172b71e87897883aa59debe5 Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Wed, 30 Apr 2025 22:35:36 +0400 Subject: [PATCH 156/162] Bug in PersistentMap equals implementation (#218) Fixes #198 --- .../implementations/immutableMap/TrieNode.kt | 11 ++-- .../src/{stress => }/ObjectWrapper.kt | 2 +- .../src/contract/map/ImmutableMapTest.kt | 4 +- .../map/PersistentHashMapBuilderTest.kt | 2 +- .../src/contract/map/PersistentHashMapTest.kt | 41 ++++++++++++++ .../contract/map/PersistentOrderedMapTest.kt | 53 +++++++++++++++++++ .../src/contract/set/ImmutableSetTest.kt | 2 +- .../set/PersistentHashSetBuilderTest.kt | 2 +- .../map/HashMapTrieNodeTest.kt | 2 +- .../commonTest/src/stress/WrapperGenerator.kt | 1 + .../map/PersistentHashMapBuilderTest.kt | 2 +- .../src/stress/map/PersistentHashMapTest.kt | 2 +- .../set/PersistentHashSetBuilderTest.kt | 2 +- .../src/stress/set/PersistentHashSetTest.kt | 2 +- 14 files changed, 110 insertions(+), 18 deletions(-) rename core/commonTest/src/{stress => }/ObjectWrapper.kt (97%) create mode 100644 core/commonTest/src/contract/map/PersistentOrderedMapTest.kt diff --git a/core/commonMain/src/implementations/immutableMap/TrieNode.kt b/core/commonMain/src/implementations/immutableMap/TrieNode.kt index e00eaa7a..fcf68d05 100644 --- a/core/commonMain/src/implementations/immutableMap/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableMap/TrieNode.kt @@ -181,7 +181,6 @@ internal class TrieNode( /** The given [newNode] must not be a part of any persistent map instance. */ private fun updateNodeAtIndex(nodeIndex: Int, positionMask: Int, newNode: TrieNode, owner: MutabilityOwnership? = null): TrieNode { -// assert(buffer[nodeIndex] !== newNode) val newNodeBuffer = newNode.buffer if (newNodeBuffer.size == 2 && newNode.nodeMap == 0) { if (buffer.size == 1) { @@ -764,19 +763,17 @@ internal class TrieNode( } else { targetNode.mutableRemove(keyHash, key, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } - return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership) + return mutableReplaceNode(newNode, nodeIndex, keyPositionMask, mutator.ownership) } // key is absent return this } - private fun mutableReplaceNode(targetNode: TrieNode, newNode: TrieNode?, nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership) = when { + private fun mutableReplaceNode(newNode: TrieNode?, nodeIndex: Int, positionMask: Int, owner: MutabilityOwnership) = when { newNode == null -> mutableRemoveNodeAtIndex(nodeIndex, positionMask, owner) - targetNode !== newNode -> - updateNodeAtIndex(nodeIndex, positionMask, newNode, owner) - else -> this + else -> updateNodeAtIndex(nodeIndex, positionMask, newNode, owner) } fun remove(keyHash: Int, key: K, value: @UnsafeVariance V, shift: Int): TrieNode? { @@ -826,7 +823,7 @@ internal class TrieNode( } else { targetNode.mutableRemove(keyHash, key, value, shift + LOG_MAX_BRANCHING_FACTOR, mutator) } - return mutableReplaceNode(targetNode, newNode, nodeIndex, keyPositionMask, mutator.ownership) + return mutableReplaceNode(newNode, nodeIndex, keyPositionMask, mutator.ownership) } // key is absent diff --git a/core/commonTest/src/stress/ObjectWrapper.kt b/core/commonTest/src/ObjectWrapper.kt similarity index 97% rename from core/commonTest/src/stress/ObjectWrapper.kt rename to core/commonTest/src/ObjectWrapper.kt index c217de6a..19f5c7ca 100644 --- a/core/commonTest/src/stress/ObjectWrapper.kt +++ b/core/commonTest/src/ObjectWrapper.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package tests.stress +package tests import kotlinx.collections.immutable.internal.assert import kotlin.js.JsName diff --git a/core/commonTest/src/contract/map/ImmutableMapTest.kt b/core/commonTest/src/contract/map/ImmutableMapTest.kt index 599f2c1c..d204d443 100644 --- a/core/commonTest/src/contract/map/ImmutableMapTest.kt +++ b/core/commonTest/src/contract/map/ImmutableMapTest.kt @@ -11,8 +11,8 @@ import tests.contract.compare import tests.contract.mapBehavior import tests.contract.setBehavior import tests.remove -import tests.stress.IntWrapper -import tests.stress.ObjectWrapper +import tests.IntWrapper +import tests.ObjectWrapper import kotlin.test.* class ImmutableHashMapTest : ImmutableMapTest() { diff --git a/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt index 732eb81a..a4d47a7c 100644 --- a/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt +++ b/core/commonTest/src/contract/map/PersistentHashMapBuilderTest.kt @@ -7,7 +7,7 @@ package tests.contract.map import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap import kotlinx.collections.immutable.persistentHashMapOf -import tests.stress.IntWrapper +import tests.IntWrapper import kotlin.collections.iterator import kotlin.test.Test import kotlin.test.assertEquals diff --git a/core/commonTest/src/contract/map/PersistentHashMapTest.kt b/core/commonTest/src/contract/map/PersistentHashMapTest.kt index c1152f4a..f7f43029 100644 --- a/core/commonTest/src/contract/map/PersistentHashMapTest.kt +++ b/core/commonTest/src/contract/map/PersistentHashMapTest.kt @@ -7,6 +7,7 @@ package tests.contract.map import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap import kotlinx.collections.immutable.persistentHashMapOf +import tests.IntWrapper import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -32,4 +33,44 @@ class PersistentHashMapTest { assertEquals(map3, map4.toMap()) assertEquals(map3, map4) } + + @Test + fun `builder should correctly handle multiple element removals in case of full collision`() { + val a = IntWrapper(0, 0) + val b = IntWrapper(1, 0) + val c = IntWrapper(2, 0) + + val original: PersistentHashMap = + persistentHashMapOf(a to "a", b to "b", c to "c") as PersistentHashMap + + val onlyA: PersistentHashMap = + persistentHashMapOf(a to "a") as PersistentHashMap + + val builder = original.builder() + builder.remove(b) + builder.remove(c) + val removedBC = builder.build() + + assertEquals(onlyA, removedBC) + } + + @Test + fun `builder should correctly handle multiple element removals in case of partial collision`() { + val a = IntWrapper(0, 0) + val b = IntWrapper(1, 0) + val c = IntWrapper(2, 0) + val d = IntWrapper(3, 11) + + val original: PersistentHashMap = + persistentHashMapOf(a to "a", b to "b", c to "c", d to "d") as PersistentHashMap + + val afterImmutableRemoving = original.remove(b).remove(c) + + val builder = original.builder() + builder.remove(b) + builder.remove(c) + val afterMutableRemoving = builder.build() + + assertEquals(afterImmutableRemoving, afterMutableRemoving) + } } \ No newline at end of file diff --git a/core/commonTest/src/contract/map/PersistentOrderedMapTest.kt b/core/commonTest/src/contract/map/PersistentOrderedMapTest.kt new file mode 100644 index 00000000..be3a1084 --- /dev/null +++ b/core/commonTest/src/contract/map/PersistentOrderedMapTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2025 JetBrains s.r.o. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package tests.contract.map + +import kotlinx.collections.immutable.minus +import kotlinx.collections.immutable.persistentMapOf +import kotlin.test.Test +import kotlin.test.assertEquals + +class PersistentOrderedMapTest { + + /** + * Test from issue: https://github.com/Kotlin/kotlinx.collections.immutable/issues/198 + */ + @Test + fun `when removing multiple keys with identical hashcodes the remaining key should be correctly promoted`() { + class ChosenHashCode( + private val hashCode: Int, + private val name: String, + ) { + override fun equals(other: Any?): Boolean { + return other is ChosenHashCode && (other.name == name) + } + + override fun hashCode(): Int { + return hashCode + } + + override fun toString(): String { + return name + } + } + + val a = ChosenHashCode(123, "A") + val b = ChosenHashCode(123, "B") + val c = ChosenHashCode(123, "C") + + val abc = persistentMapOf( + a to "x", + b to "y", + c to "z", + ) + + val minusAb = abc.minus(arrayOf(a, b)) + val cOnly = persistentMapOf(c to "z") + + assertEquals(minusAb.entries, cOnly.entries) + assertEquals(minusAb, cOnly) + } +} \ No newline at end of file diff --git a/core/commonTest/src/contract/set/ImmutableSetTest.kt b/core/commonTest/src/contract/set/ImmutableSetTest.kt index f017ccae..1339ebcf 100644 --- a/core/commonTest/src/contract/set/ImmutableSetTest.kt +++ b/core/commonTest/src/contract/set/ImmutableSetTest.kt @@ -10,7 +10,7 @@ import tests.contract.compare import tests.contract.setBehavior import tests.isDigit import tests.isUpperCase -import tests.stress.IntWrapper +import tests.IntWrapper import kotlin.test.* class ImmutableHashSetTest : ImmutableSetTestBase() { diff --git a/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt b/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt index df1df86a..6a81ffd5 100644 --- a/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt +++ b/core/commonTest/src/contract/set/PersistentHashSetBuilderTest.kt @@ -7,7 +7,7 @@ package tests.contract.set import kotlinx.collections.immutable.implementations.immutableSet.PersistentHashSet import kotlinx.collections.immutable.persistentHashSetOf -import tests.stress.IntWrapper +import tests.IntWrapper import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith diff --git a/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt index f3e4ef5d..822d3707 100644 --- a/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt +++ b/core/commonTest/src/implementations/map/HashMapTrieNodeTest.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.implementations.immutableMap.LOG_MAX_BRANCH import kotlinx.collections.immutable.implementations.immutableMap.MAX_SHIFT import kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap import kotlinx.collections.immutable.implementations.immutableMap.TrieNode -import tests.stress.IntWrapper +import tests.IntWrapper import kotlin.test.* class HashMapTrieNodeTest { diff --git a/core/commonTest/src/stress/WrapperGenerator.kt b/core/commonTest/src/stress/WrapperGenerator.kt index 377e1762..4e17e189 100644 --- a/core/commonTest/src/stress/WrapperGenerator.kt +++ b/core/commonTest/src/stress/WrapperGenerator.kt @@ -5,6 +5,7 @@ package tests.stress +import tests.ObjectWrapper import kotlin.random.Random diff --git a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt index deb20e4b..4f51852a 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapBuilderTest.kt @@ -11,7 +11,7 @@ import tests.NForAlgorithmComplexity import tests.distinctStringValues import tests.remove import tests.stress.ExecutionTimeMeasuringTest -import tests.stress.IntWrapper +import tests.IntWrapper import tests.stress.WrapperGenerator import kotlin.random.Random import kotlin.test.* diff --git a/core/commonTest/src/stress/map/PersistentHashMapTest.kt b/core/commonTest/src/stress/map/PersistentHashMapTest.kt index 6eb0c08a..dbd9772d 100644 --- a/core/commonTest/src/stress/map/PersistentHashMapTest.kt +++ b/core/commonTest/src/stress/map/PersistentHashMapTest.kt @@ -11,7 +11,7 @@ import tests.NForAlgorithmComplexity import tests.distinctStringValues import tests.remove import tests.stress.ExecutionTimeMeasuringTest -import tests.stress.IntWrapper +import tests.IntWrapper import tests.stress.WrapperGenerator import kotlin.random.Random import kotlin.test.* diff --git a/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt b/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt index 3978b88d..1f84b190 100644 --- a/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt +++ b/core/commonTest/src/stress/set/PersistentHashSetBuilderTest.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentHashSetOf import tests.NForAlgorithmComplexity import tests.distinctStringValues import tests.stress.ExecutionTimeMeasuringTest -import tests.stress.IntWrapper +import tests.IntWrapper import tests.stress.WrapperGenerator import kotlin.random.Random import kotlin.test.Test diff --git a/core/commonTest/src/stress/set/PersistentHashSetTest.kt b/core/commonTest/src/stress/set/PersistentHashSetTest.kt index e37a31d5..8fa14126 100644 --- a/core/commonTest/src/stress/set/PersistentHashSetTest.kt +++ b/core/commonTest/src/stress/set/PersistentHashSetTest.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentHashSetOf import tests.NForAlgorithmComplexity import tests.distinctStringValues import tests.stress.ExecutionTimeMeasuringTest -import tests.stress.IntWrapper +import tests.IntWrapper import tests.stress.WrapperGenerator import kotlin.random.Random import kotlin.test.Test From 57759ea5d7a51e8a42073fbead68da5bcb5b771e Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 6 May 2025 08:10:20 -0400 Subject: [PATCH 157/162] Adopt KUP requirements imposed by KT-75078 (#214) * Adopt KUP requirements imposed by KT-75078 Closes #212 --- build.gradle.kts | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0ed10d89..88d2c31a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,10 +46,29 @@ allprojects { } } + val setAllWarningsAsError = providers.gradleProperty("kotlin_Werror_override").map { + when (it) { + "enable" -> true + "disable" -> false + else -> error("Unexpected value for 'kotlin_Werror_override' property: $it") + } + } + tasks.withType(KotlinCompilationTask::class).configureEach { compilerOptions { - allWarningsAsErrors = true - freeCompilerArgs.add("-Xexpect-actual-classes") + if (setAllWarningsAsError.orNull != false) { + allWarningsAsErrors = true + } else { + freeCompilerArgs.addAll( + "-Wextra", + "-Xuse-fir-experimental-checkers" + ) + } + freeCompilerArgs.addAll( + "-Xexpect-actual-classes", + "-Xreport-all-warnings", + "-Xrender-internal-diagnostic-names" + ) } if (this is KotlinJsCompile) { compilerOptions { @@ -60,6 +79,18 @@ allprojects { freeCompilerArgs.add("-Xjvm-default=disable") } } + + val extraOpts = providers.gradleProperty("kotlin_additional_cli_options").orNull + extraOpts?.split(' ')?.map(String::trim)?.filter(String::isNotBlank)?.let { opts -> + if (opts.isNotEmpty()) { + compilerOptions.freeCompilerArgs.addAll(opts) + } + } + + doFirst { + logger.info("Added Kotlin compiler flags: ${compilerOptions.freeCompilerArgs.get().joinToString(", ")}") + logger.info("allWarningsAsErrors=${compilerOptions.allWarningsAsErrors.get()}") + } } } From 6b1c71c232f0f3dd6bf08a4fb3cfc7a2e1ee09ac Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Thu, 8 May 2025 09:56:16 +0400 Subject: [PATCH 158/162] Ghost element when applying multiples minus operations on a PersistentHashSet (#219) Fixes #144 --- .../implementations/immutableSet/TrieNode.kt | 10 +++---- .../src/contract/set/PersistentHashSetTest.kt | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/core/commonMain/src/implementations/immutableSet/TrieNode.kt b/core/commonMain/src/implementations/immutableSet/TrieNode.kt index 32ecca83..d126ab54 100644 --- a/core/commonMain/src/implementations/immutableSet/TrieNode.kt +++ b/core/commonMain/src/implementations/immutableSet/TrieNode.kt @@ -621,17 +621,17 @@ internal class TrieNode( val realSize = realBitMap.countOneBits() return when { realBitMap == 0 -> EMPTY + // single values are kept only on root level + realSize == 1 && shift != 0 -> when (val single = mutableNode.buffer[mutableNode.indexOfCellAt(realBitMap)]) { + is TrieNode<*> -> TrieNode(realBitMap, arrayOf(single), mutator.ownership) + else -> single + } realBitMap == bitmap -> { when { mutableNode.elementsIdentityEquals(this) -> this else -> mutableNode } } - // single values are kept only on root level - realSize == 1 && shift != 0 -> when (val single = mutableNode.buffer[mutableNode.indexOfCellAt(realBitMap)]) { - is TrieNode<*> -> TrieNode(realBitMap, arrayOf(single), mutator.ownership) - else -> single - } else -> { // clean up all the EMPTYs in the resulting buffer val realBuffer = arrayOfNulls(realSize) diff --git a/core/commonTest/src/contract/set/PersistentHashSetTest.kt b/core/commonTest/src/contract/set/PersistentHashSetTest.kt index d1a5c777..ea86ff70 100644 --- a/core/commonTest/src/contract/set/PersistentHashSetTest.kt +++ b/core/commonTest/src/contract/set/PersistentHashSetTest.kt @@ -5,7 +5,10 @@ package tests.contract.set +import kotlinx.collections.immutable.implementations.immutableSet.PersistentHashSet import kotlinx.collections.immutable.persistentHashSetOf +import kotlinx.collections.immutable.minus +import kotlinx.collections.immutable.toPersistentHashSet import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -27,4 +30,30 @@ class PersistentHashSetTest { assertEquals(set2, builder.build().toSet()) assertEquals(set2, builder.build()) } + + /** + * Test from issue: https://github.com/Kotlin/kotlinx.collections.immutable/issues/144 + */ + @Test + fun `removing multiple batches should leave only remaining elements`() { + val firstBatch = listOf(4554, 9380, 4260, 6602) + val secondBatch = listOf(1188, 14794) + val extraElement = 7450 + + val set = firstBatch.plus(secondBatch).plus(extraElement).toPersistentHashSet() + val result = set.minus(firstBatch.toPersistentHashSet()).minus(secondBatch) + assertEquals(1, result.size) + assertEquals(extraElement, result.first()) + } + + @Test + fun `after removing elements from one collision the remaining one element must be promoted to the root`() { + val set1: PersistentHashSet = persistentHashSetOf(0, 32768, 65536) as PersistentHashSet + val set2: PersistentHashSet = persistentHashSetOf(0, 32768) as PersistentHashSet + + val expected = persistentHashSetOf(65536) + val actual = set1 - set2 + + assertEquals(expected, actual) + } } \ No newline at end of file From bd5ac10f1e3b72c8bd6335005bf27d751e0d8b52 Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Wed, 14 May 2025 16:25:39 +0400 Subject: [PATCH 159/162] Release v0.4.0 (#221) Fixes #220 --- CHANGELOG.md | 8 ++++++++ README.md | 8 +++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c00d579f..7010c6b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # CHANGELOG +## 0.4.0 + +- Fixed the equality bug in PersistentMap — Added proper node promotion during mutable operations to ensure a consistent internal tree structure [#217](https://github.com/Kotlin/kotlinx.collections.immutable/pull/217) +- Fixed the bug in PersistentMap equals implementation — Ensured proper node promotion during mutable key removal even when nodes share the same owner [#218](https://github.com/Kotlin/kotlinx.collections.immutable/pull/218) +- Fixed the ghost element issue in PersistentHashSet — Corrected condition check order to ensure proper recursive element promotion after collision removal [#219](https://github.com/Kotlin/kotlinx.collections.immutable/pull/219) +- Updated Kotlin to version 2.1.20 and core dependencies [#213](https://github.com/Kotlin/kotlinx.collections.immutable/pull/213) +- Enabled '-Xjvm-default=disable' explicitly to prevent API dump changes [#210](https://github.com/Kotlin/kotlinx.collections.immutable/pull/210) + ## 0.3.8 - Add extension functions to convert Array to persistent collections [#159](https://github.com/Kotlin/kotlinx.collections.immutable/issues/159) diff --git a/README.md b/README.md index cf7adfb4..1b01f669 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![GitHub license](https://img.shields.io/github/license/kotlin/kotlinx.collections.immutable)](LICENSE.txt) [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-collections-immutable) +[![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-blue.svg?logo=kotlin)](http://kotlinlang.org) + Immutable collection interfaces and implementation prototypes for Kotlin. @@ -118,7 +120,7 @@ collection.mutate { some_actions_on(it) } The library is published to Maven Central repository. -The library depends on the Kotlin Standard Library of the version at least `1.9.21`. +The library depends on the Kotlin Standard Library of the version at least `2.1.20`. ### Gradle @@ -137,7 +139,7 @@ kotlin { sourceSets { commonMain { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8") + implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0") } } } @@ -153,7 +155,7 @@ Add dependencies (you can also add other modules that you need): org.jetbrains.kotlinx kotlinx-collections-immutable-jvm - 0.3.8 + 0.4.0 ``` From c7f155af4bdadb955d15534f71ed7d7167cd5e6b Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Wed, 4 Jun 2025 19:56:47 +0400 Subject: [PATCH 160/162] Enable Dokka for documentation generation (#225) Fixes #222 --- core/build.gradle.kts | 24 ++++++++++++++++++++++++ core/dokka-templates/README.md | 12 ++++++++++++ gradle.properties | 4 ++++ settings.gradle.kts | 4 ++++ 4 files changed, 44 insertions(+) create mode 100644 core/dokka-templates/README.md diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 53a602b2..020e66fd 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -6,6 +6,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("kotlin-multiplatform") + id("org.jetbrains.dokka") `maven-publish` } @@ -140,6 +141,29 @@ kotlin { } } +dokka { + pluginsConfiguration.html { + templatesDir.set(projectDir.resolve("dokka-templates")) + } + + dokkaPublications.html { + failOnWarning.set(true) + // Enum members and undocumented toString() + suppressInheritedMembers.set(true) + } + + dokkaSourceSets.configureEach { + val platform = name.dropLast(4) + samples.from("$platform/test") + skipDeprecated.set(true) + sourceLink { + localDirectory.set(rootDir) + remoteUrl("https://github.com/Kotlin/kotlinx.collections.immutable/tree/v0.4.0") + remoteLineSuffix.set("#L") + } + } +} + tasks { named("jvmTest", Test::class) { maxHeapSize = "1024m" diff --git a/core/dokka-templates/README.md b/core/dokka-templates/README.md new file mode 100644 index 00000000..f656041e --- /dev/null +++ b/core/dokka-templates/README.md @@ -0,0 +1,12 @@ +# Dokka's template customization +To provide unified navigation for all parts of [kotlinlang.org](https://kotlinlang.org/), +the Kotlin Website Team uses this directory to place custom templates in this folder +during the website build time on TeamCity. + +It is not practical to place these templates in the kotlinx.collections.immutable repository because they change from time to time +and aren't related to the library's release cycle. + +The folder is defined as a source for custom templates by the templatesDir property through Dokka's plugin configuration. + +[Here](https://kotlinlang.org/docs/dokka-html.html#templates), you can +find more about the customization of Dokka's HTML output. \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d6c5c872..84e0d5f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,5 +3,9 @@ version=0.4 versionSuffix=SNAPSHOT kotlin_version=2.1.20 +dokkaVersion=2.0.0 org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.nowarn=true \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 55339c36..e9b342e0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,6 +8,10 @@ pluginManagement { maven(kotlinRepoUrl) } } + val dokkaVersion: String by settings + plugins { + id("org.jetbrains.dokka") version dokkaVersion + } } rootProject.name = "Kotlin-Immutable-Collections" // TODO: Make readable name when it's not used in js module names From 2d31e2fcc30136a05db7f4ddf0a34bf6c2ebac51 Mon Sep 17 00:00:00 2001 From: Dmitry Nekrasov Date: Mon, 16 Jun 2025 16:27:11 +0400 Subject: [PATCH 161/162] Add API reference badge with KDoc link to README (#226) Fixes #205 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b01f669..1b6c37e7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Build status](https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:KotlinTools_KotlinxCollectionsImmutable_Build_All)/statusIcon.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxCollectionsImmutable_Build_All) [![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-collections-immutable.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-collections-immutable) [![Kotlin](https://img.shields.io/badge/kotlin-2.1.20-blue.svg?logo=kotlin)](http://kotlinlang.org) - +[![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlin.github.io/kotlinx.collections.immutable/) Immutable collection interfaces and implementation prototypes for Kotlin. From 8372a43444e27c823cfa88d19ec7aa7b14aac895 Mon Sep 17 00:00:00 2001 From: Stanislav Ruban Date: Wed, 23 Jul 2025 16:06:39 +0300 Subject: [PATCH 162/162] [KUP] Disable opt-in Kotlin compiler warnings (#229) It was decided to deprecate and phase out the concept (see KT-77721). --- build.gradle.kts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 88d2c31a..3516f9bc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -58,11 +58,6 @@ allprojects { compilerOptions { if (setAllWarningsAsError.orNull != false) { allWarningsAsErrors = true - } else { - freeCompilerArgs.addAll( - "-Wextra", - "-Xuse-fir-experimental-checkers" - ) } freeCompilerArgs.addAll( "-Xexpect-actual-classes",