KT-15363 Fix failing test: missing capacity parameter check in JS
Partially restore JVM map capacity implementation
This commit is contained in:
@@ -187,5 +187,15 @@ internal actual fun checkCountOverflow(count: Int): Int {
|
|||||||
* JS map and set implementations do not make use of capacities or load factors.
|
* JS map and set implementations do not make use of capacities or load factors.
|
||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
@kotlin.internal.InlineOnly
|
internal actual fun mapCapacity(expectedSize: Int) = expectedSize
|
||||||
internal actual inline fun mapCapacity(expectedSize: Int) = expectedSize
|
|
||||||
|
/**
|
||||||
|
* Checks a collection builder function capacity argument.
|
||||||
|
* In JS no validation is made in Map/Set constructor yet.
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.3")
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
@PublishedApi
|
||||||
|
internal actual fun checkBuilderCapacity(capacity: Int) {
|
||||||
|
require(capacity >= 0) { "capacity must be non-negative." }
|
||||||
|
}
|
||||||
@@ -97,19 +97,27 @@ internal actual fun <K, V> Map<out K, V>.toSingletonMap(): Map<K, V> =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the initial capacity of a map, based on Guava's
|
* Calculate the initial capacity of a map, based on Guava's
|
||||||
* [com.google.common.collect.Maps.capacity](https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Maps.java)
|
* [com.google.common.collect.Maps.capacity](https://github.com/google/guava/blob/v28.2/guava/src/com/google/common/collect/Maps.java#L325)
|
||||||
* approach. This is similar to [java.util.HashMap.putMapEntries] (JDK8+) but provides further optimizations for small or large sizes.
|
* approach.
|
||||||
*/
|
*/
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal actual fun mapCapacity(expectedSize: Int): Int = when {
|
internal actual fun mapCapacity(expectedSize: Int): Int = when {
|
||||||
// We are not coercing the value to a valid one and not throwing an exception. It is up to the receiving class to
|
// We are not coercing the value to a valid one and not throwing an exception. It is up to the caller to
|
||||||
// properly handle negative values. Note that all built-in classes do exactly that and will throw.
|
// properly handle negative values.
|
||||||
expectedSize < 0 -> expectedSize
|
expectedSize < 0 -> expectedSize
|
||||||
expectedSize < MIN_CAPACITY -> expectedSize + 1
|
expectedSize < 3 -> expectedSize + 1
|
||||||
expectedSize < MAX_CAPACITY -> ((expectedSize / 0.75F) + 1.0F).toInt()
|
expectedSize < INT_MAX_POWER_OF_TWO -> ((expectedSize / 0.75F) + 1.0F).toInt()
|
||||||
else -> MAX_CAPACITY
|
// any large value
|
||||||
|
else -> Int.MAX_VALUE
|
||||||
}
|
}
|
||||||
|
private const val INT_MAX_POWER_OF_TWO: Int = 1 shl (Int.SIZE_BITS - 2)
|
||||||
|
|
||||||
// java.util.HashMap.MAX_CAPACITY
|
/**
|
||||||
private const val MAX_CAPACITY = 1 shl 30
|
* Checks a collection builder function capacity argument.
|
||||||
private const val MIN_CAPACITY = 3
|
* Does nothing, capacity is validated in List/Set/Map constructor
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.3")
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
@PublishedApi
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
internal actual inline fun checkBuilderCapacity(capacity: Int) {}
|
||||||
@@ -178,6 +178,7 @@ public inline fun <E> buildList(@BuilderInference builderAction: MutableList<E>.
|
|||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public inline fun <E> buildList(expectedSize: Int, @BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
|
public inline fun <E> buildList(expectedSize: Int, @BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
|
||||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||||
|
checkBuilderCapacity(expectedSize)
|
||||||
return ArrayList<E>(expectedSize).apply(builderAction)
|
return ArrayList<E>(expectedSize).apply(builderAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ public inline fun <K, V> buildMap(@BuilderInference builderAction: MutableMap<K,
|
|||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public inline fun <K, V> buildMap(expectedSize: Int, @BuilderInference builderAction: MutableMap<K, V>.() -> Unit): Map<K, V> {
|
public inline fun <K, V> buildMap(expectedSize: Int, @BuilderInference builderAction: MutableMap<K, V>.() -> Unit): Map<K, V> {
|
||||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||||
|
checkBuilderCapacity(expectedSize)
|
||||||
return LinkedHashMap<K, V>(mapCapacity(expectedSize)).apply(builderAction)
|
return LinkedHashMap<K, V>(mapCapacity(expectedSize)).apply(builderAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +158,14 @@ public inline fun <K, V> buildMap(expectedSize: Int, @BuilderInference builderAc
|
|||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal expect fun mapCapacity(expectedSize: Int): Int
|
internal expect fun mapCapacity(expectedSize: Int): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a collection builder function capacity argument.
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.3")
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
@PublishedApi
|
||||||
|
internal expect fun checkBuilderCapacity(capacity: Int)
|
||||||
|
|
||||||
/** Returns `true` if this map is not empty. */
|
/** Returns `true` if this map is not empty. */
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public inline fun <K, V> Map<out K, V>.isNotEmpty(): Boolean = !isEmpty()
|
public inline fun <K, V> Map<out K, V>.isNotEmpty(): Boolean = !isEmpty()
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ public inline fun <E> buildSet(@BuilderInference builderAction: MutableSet<E>.()
|
|||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public inline fun <E> buildSet(expectedSize: Int, @BuilderInference builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
public inline fun <E> buildSet(expectedSize: Int, @BuilderInference builderAction: MutableSet<E>.() -> Unit): Set<E> {
|
||||||
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
|
||||||
|
checkBuilderCapacity(expectedSize)
|
||||||
return LinkedHashSet<E>(mapCapacity(expectedSize)).apply(builderAction)
|
return LinkedHashSet<E>(mapCapacity(expectedSize)).apply(builderAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class ContainerBuilderTest {
|
|||||||
|
|
||||||
assertEquals(listOf('a', 'b', 'c', 'd'), y)
|
assertEquals(listOf('a', 'b', 'c', 'd'), y)
|
||||||
|
|
||||||
|
assertEquals(listOf(1), buildList(0) { add(1) })
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
buildList(-1) { add(0) }
|
buildList(-1) { add(0) }
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,7 @@ class ContainerBuilderTest {
|
|||||||
|
|
||||||
assertEquals(setOf('c', 'b', 'd'), y)
|
assertEquals(setOf('c', 'b', 'd'), y)
|
||||||
|
|
||||||
|
assertEquals(setOf(1), buildSet(0) { add(1) })
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
buildSet(-1) { add(0) }
|
buildSet(-1) { add(0) }
|
||||||
}
|
}
|
||||||
@@ -59,6 +61,7 @@ class ContainerBuilderTest {
|
|||||||
|
|
||||||
assertEquals(mapOf('a' to 1, 'c' to 3, 'b' to 2, 'd' to 4), y)
|
assertEquals(mapOf('a' to 1, 'c' to 3, 'b' to 2, 'd' to 4), y)
|
||||||
|
|
||||||
|
assertEquals(mapOf("a" to 1), buildMap<String, Int>(0) { put("a", 1) })
|
||||||
assertFailsWith<IllegalArgumentException> {
|
assertFailsWith<IllegalArgumentException> {
|
||||||
buildMap<String, Int>(-1) { put("x", 1) }
|
buildMap<String, Int>(-1) { put("x", 1) }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user