Implement contains extension functions for URanges (KT-26378)
This commit is contained in:
committed by
Ilya Gorbunov
parent
cb587893c0
commit
bf83f0e070
@@ -20,13 +20,18 @@ object RangeOps : TemplateGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Collection<T>.permutations(): List<Pair<T, T>> = flatMap { a -> map { b -> a to b } }
|
||||
private fun shouldCheckForConversionOverflow(fromType: PrimitiveType, toType: PrimitiveType): Boolean {
|
||||
return toType.isIntegral() && fromType.capacity > toType.capacity ||
|
||||
toType.isUnsigned() && fromType.capacityUnsigned > toType.capacityUnsigned
|
||||
}
|
||||
|
||||
private val numericPrimitives = PrimitiveType.numericPrimitives
|
||||
private val numericPermutations = numericPrimitives.permutations()
|
||||
private val primitivePermutations = numericPermutations + (PrimitiveType.Char to PrimitiveType.Char)
|
||||
private val integralPermutations = primitivePermutations.filter { it.first.isIntegral() && it.second.isIntegral() }
|
||||
private val unsignedPermutations = PrimitiveType.unsignedPrimitives.map { it to it }
|
||||
private fun <T> Collection<T>.combinations(): List<Pair<T, T>> = flatMap { a -> map { b -> a to b } }
|
||||
|
||||
private val numericCombinations = PrimitiveType.numericPrimitives.combinations()
|
||||
private val primitiveCombinations = numericCombinations + (PrimitiveType.Char to PrimitiveType.Char)
|
||||
private val integralCombinations = primitiveCombinations.filter { it.first.isIntegral() && it.second.isIntegral() }
|
||||
private val unsignedCombinations = PrimitiveType.unsignedPrimitives.combinations()
|
||||
private val unsignedMappings = PrimitiveType.unsignedPrimitives.map { it to it }
|
||||
|
||||
val PrimitiveType.stepType get() = when(this) {
|
||||
PrimitiveType.Char -> "Int"
|
||||
@@ -72,7 +77,7 @@ object RangeOps : TemplateGroupBase() {
|
||||
}
|
||||
|
||||
val f_downTo = fn("downTo(to: Primitive)").byTwoPrimitives {
|
||||
include(Primitives, integralPermutations + unsignedPermutations)
|
||||
include(Primitives, integralCombinations + unsignedMappings)
|
||||
} builderWith { (fromType, toType) ->
|
||||
val elementType = rangeElementType(fromType, toType)
|
||||
val progressionType = elementType.name + "Progression"
|
||||
@@ -107,7 +112,7 @@ object RangeOps : TemplateGroupBase() {
|
||||
|
||||
|
||||
val f_until = fn("until(to: Primitive)").byTwoPrimitives {
|
||||
include(Primitives, integralPermutations + unsignedPermutations)
|
||||
include(Primitives, integralCombinations + unsignedMappings)
|
||||
} builderWith { (fromType, toType) ->
|
||||
infix()
|
||||
signature("until(to: $toType)")
|
||||
@@ -115,7 +120,6 @@ object RangeOps : TemplateGroupBase() {
|
||||
val elementType = rangeElementType(fromType, toType)
|
||||
val progressionType = elementType.name + "Range"
|
||||
returns(progressionType)
|
||||
val minValue = if (elementType == PrimitiveType.Char) "'\\u0000'" else "$elementType.MIN_VALUE"
|
||||
|
||||
doc {
|
||||
"""
|
||||
@@ -125,16 +129,15 @@ object RangeOps : TemplateGroupBase() {
|
||||
"""
|
||||
}
|
||||
|
||||
val minValue = when {
|
||||
elementType == PrimitiveType.Char -> "'\\u0000'"
|
||||
elementType.isUnsigned() -> "$toType.MIN_VALUE"
|
||||
else -> "$elementType.MIN_VALUE"
|
||||
}
|
||||
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
|
||||
val u = if (elementType in PrimitiveType.unsignedPrimitives) "u" else ""
|
||||
val u = if (elementType.isUnsigned()) "u" else ""
|
||||
|
||||
if (elementType == toType) {
|
||||
// hack to work around incorrect char overflow behavior in JVM and int overflow behavior in JS
|
||||
val toExpr = when (toType) {
|
||||
PrimitiveType.Char -> "to.toInt()"
|
||||
PrimitiveType.Int -> "to.toLong()"
|
||||
else -> "to"
|
||||
}
|
||||
if (elementType == toType || elementType.isUnsigned()) {
|
||||
body {
|
||||
// <= instead of == for JS
|
||||
"""
|
||||
@@ -148,7 +151,7 @@ object RangeOps : TemplateGroupBase() {
|
||||
}
|
||||
|
||||
val f_contains = fn("contains(value: Primitive)").byTwoPrimitives {
|
||||
include(Ranges, numericPermutations)
|
||||
include(Ranges, numericCombinations)
|
||||
filter { _, (rangeType, itemType) -> rangeType != itemType }
|
||||
} builderWith { (rangeType, itemType) ->
|
||||
operator()
|
||||
@@ -158,14 +161,15 @@ object RangeOps : TemplateGroupBase() {
|
||||
if (rangeType.isIntegral() != itemType.isIntegral()) {
|
||||
deprecate(Deprecation("This `contains` operation mixing integer and floating point arguments has ambiguous semantics and is going to be removed.", level = DeprecationLevel.WARNING))
|
||||
}
|
||||
|
||||
platformName("${rangeType.name.decapitalize()}RangeContains")
|
||||
returns("Boolean")
|
||||
doc { "Checks if the specified [value] belongs to this range." }
|
||||
body {
|
||||
if (rangeType.capacity > itemType.capacity || !rangeType.isIntegral())
|
||||
"return contains(value.to$rangeType())"
|
||||
else
|
||||
if (shouldCheckForConversionOverflow(fromType = itemType, toType = rangeType))
|
||||
"return value.to${rangeType}ExactOrNull().let { if (it != null) contains(it) else false }"
|
||||
else
|
||||
"return contains(value.to$rangeType())"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,9 +192,28 @@ object RangeOps : TemplateGroupBase() {
|
||||
body { "return element != null && contains(element)" }
|
||||
}
|
||||
|
||||
val f_contains_unsigned = fn("contains(element: Primitive)").byTwoPrimitives {
|
||||
include(RangesOfPrimitives, unsignedCombinations)
|
||||
filter { _, (rangeType, itemType) -> rangeType in rangePrimitives && rangeType != itemType }
|
||||
} builderWith { (rangeType, itemType) ->
|
||||
operator()
|
||||
signature("contains(value: $itemType)")
|
||||
returns("Boolean")
|
||||
|
||||
since("1.3")
|
||||
doc { "Checks if the specified [value] belongs to this range." }
|
||||
|
||||
body {
|
||||
if (shouldCheckForConversionOverflow(fromType = itemType, toType = rangeType))
|
||||
"return (value shr $rangeType.SIZE_BITS) == ${itemType.zero()} && contains(value.to$rangeType())"
|
||||
else
|
||||
"return contains(value.to$rangeType())"
|
||||
}
|
||||
}
|
||||
|
||||
val f_toPrimitiveExactOrNull = fn("to{}ExactOrNull()").byTwoPrimitives {
|
||||
include(Primitives, numericPermutations)
|
||||
filter { _, (fromType, toType) -> fromType.capacity > toType.capacity && toType.isIntegral() }
|
||||
include(Primitives, numericCombinations)
|
||||
filter { _, (fromType, toType) -> shouldCheckForConversionOverflow(fromType, toType) }
|
||||
} builderWith { (fromType, toType) ->
|
||||
check(toType.isIntegral())
|
||||
visibility("internal")
|
||||
|
||||
@@ -54,6 +54,7 @@ enum class PrimitiveType {
|
||||
ULong;
|
||||
|
||||
val capacity by lazy { descendingByDomainCapacity.indexOf(this).let { if (it < 0) it else descendingByDomainCapacity.size - it } }
|
||||
val capacityUnsigned by lazy { descendingByDomainCapacityUnsigned.indexOf(this).let { if (it < 0) it else descendingByDomainCapacityUnsigned.size - it } }
|
||||
|
||||
companion object {
|
||||
val unsignedPrimitives = setOf(UInt, ULong, UByte, UShort)
|
||||
|
||||
Reference in New Issue
Block a user