Safe sign conversions for int types
This commit is contained in:
commit
4f63a229e1
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
build/
|
||||
/.idea/
|
||||
/.gradle/
|
||||
/gradle/
|
||||
/gradlew*
|
||||
/.kotlin/
|
||||
/kotlin-js-store/
|
40
build.gradle.kts
Normal file
40
build.gradle.kts
Normal file
@ -0,0 +1,40 @@
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.defineAllMultiplatformTargets
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.optInContracts
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.defineXomrkGiteaMavenRepo
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk
|
||||
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.warningsAsErrors
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://maven.landgrafhomyak.ru/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("ru.landgrafhomyak.kotlin:kotlin-mpp-gradle-build:v0.2k2.0.20")
|
||||
}
|
||||
}
|
||||
|
||||
group = "ru.landgrafhomyak.kotlin"
|
||||
version = "v1.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
xomrk {
|
||||
kotlin {
|
||||
explicitApi()
|
||||
warningsAsErrors()
|
||||
optInContracts()
|
||||
|
||||
jvmToolchain(8)
|
||||
defineAllMultiplatformTargets()
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
defineXomrkGiteaMavenRepo()
|
||||
}
|
||||
}
|
||||
}
|
2
gradle.properties
Normal file
2
gradle.properties
Normal file
@ -0,0 +1,2 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.native.ignoreDisabledTargets=true
|
2
settings.gradle.kts
Normal file
2
settings.gradle.kts
Normal file
@ -0,0 +1,2 @@
|
||||
rootProject.name = "kotlin-utilities"
|
||||
|
@ -0,0 +1,321 @@
|
||||
@file:JvmName("IntConversions_Sign")
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package ru.landrafhomyak.kotlin.multiplatform_switches
|
||||
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
|
||||
/********************************************* Reinterpret sign casts ****************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [Byte] value to [UByte].
|
||||
*
|
||||
* If this value is positive, the resulting `UByte` value represents the same numerical value as this `Byte`.
|
||||
*
|
||||
* The resulting `UByte` value has the same binary representation as this `Byte` value.
|
||||
*/
|
||||
public inline fun Byte.asUByte(): UByte = this.toUByte()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UByte] value to [Byte].
|
||||
*
|
||||
* If this value is less than or equals to [Byte.MAX_VALUE], the resulting `Byte` value represents
|
||||
* the same numerical value as this `UByte`. Otherwise the result is negative.
|
||||
*
|
||||
* The resulting `Byte` value has the same binary representation as this `UByte` value.
|
||||
*/
|
||||
public inline fun UByte.asByte(): Byte = this.toByte()
|
||||
|
||||
/**
|
||||
* Converts this [Short] value to [UShort].
|
||||
*
|
||||
* If this value is positive, the resulting `UShort` value represents the same numerical value as this `Short`.
|
||||
*
|
||||
* The resulting `UShort` value has the same binary representation as this `Short` value.
|
||||
*/
|
||||
public inline fun Short.asUShort(): UShort = this.toUShort()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UShort] value to [Short].
|
||||
*
|
||||
* If this value is less than or equals to [Short.MAX_VALUE], the resulting `Short` value represents
|
||||
* the same numerical value as this `UShort`. Otherwise the result is negative.
|
||||
*
|
||||
* The resulting `Short` value has the same binary representation as this `UShort` value.
|
||||
*/
|
||||
public inline fun UShort.asShort(): Short = this.toShort()
|
||||
|
||||
/**
|
||||
* Converts this [Int] value to [UInt].
|
||||
*
|
||||
* If this value is positive, the resulting `UInt` value represents the same numerical value as this `Int`.
|
||||
*
|
||||
* The resulting `UInt` value has the same binary representation as this `Int` value.
|
||||
*/
|
||||
public inline fun Int.asUInt(): UInt = this.toUInt()
|
||||
|
||||
/**
|
||||
* Converts this [UInt] value to [Int].
|
||||
*
|
||||
* If this value is less than or equals to [Int.MAX_VALUE], the resulting `Int` value represents
|
||||
* the same numerical value as this `UInt`. Otherwise the result is negative.
|
||||
*
|
||||
* The resulting `Int` value has the same binary representation as this `UInt` value.
|
||||
*/
|
||||
public inline fun UInt.asInt(): Int = this.toInt()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [Long] value to [ULong].
|
||||
*
|
||||
* If this value is positive, the resulting `ULong` value represents the same numerical value as this `Long`.
|
||||
*
|
||||
* The resulting `ULong` value has the same binary representation as this `Long` value.
|
||||
*/
|
||||
public inline fun Long.asULong(): ULong = this.toULong()
|
||||
|
||||
/**
|
||||
* Converts this [ULong] value to [Long].
|
||||
*
|
||||
* If this value is less than or equals to [Long.MAX_VALUE], the resulting `Long` value represents
|
||||
* the same numerical value as this `ULong`. Otherwise the result is negative.
|
||||
*
|
||||
* The resulting `Long` value has the same binary representation as this `ULong` value.
|
||||
*/
|
||||
public inline fun ULong.asLong(): Long = this.toLong()
|
||||
|
||||
|
||||
/********************************************* Safe sign casts (or null) ****************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [Byte] value to [UByte].
|
||||
*
|
||||
* If this value is positive, the resulting [UByte] value represents the same numerical value as this [Byte].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
|
||||
public inline fun Byte.toUByteOrNull(): UByte? = if (this < 0) null else this.asUByte()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UByte] value to [Byte].
|
||||
*
|
||||
* If this value is less than or equals to [Byte.MAX_VALUE], the resulting [Byte] value represents
|
||||
* the same numerical value as this [UByte].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun UByte.toByteOrNull(): Byte? = if (this >= Byte.MAX_VALUE.asUByte()) null else this.asByte()
|
||||
|
||||
/**
|
||||
* Converts this [Short] value to [UShort].
|
||||
*
|
||||
* If this value is positive, the resulting [UShort] value represents the same numerical value as this [Short].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun Short.toUShortOrNull(): UShort? = if (this < 0) null else this.asUShort()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UShort] value to [Short].
|
||||
*
|
||||
* If this value is less than or equals to [Short.MAX_VALUE], the resulting [Short] value represents
|
||||
* the same numerical value as this [UShort].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun UShort.toShortOrNull(): Short? = if (this >= Short.MAX_VALUE.asUShort()) null else this.asShort()
|
||||
|
||||
/**
|
||||
* Converts this [Int] value to [UInt].
|
||||
*
|
||||
* If this value is positive, the resulting [UInt] value represents the same numerical value as this [Int].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun Int.toUIntOrNull(): UInt? = if (this < 0) null else this.asUInt()
|
||||
|
||||
/**
|
||||
* Converts this [UInt] value to [Int].
|
||||
*
|
||||
* If this value is less than or equals to [Int.MAX_VALUE], the resulting [Int] value represents
|
||||
* the same numerical value as this [UInt].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun UInt.toIntOrNull(): Int? = if (this >= Int.MAX_VALUE.asUInt()) null else this.asInt()
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [Long] value to [ULong].
|
||||
*
|
||||
* If this value is positive, the resulting [ULong] value represents the same numerical value as this [Long].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun Long.toULongOrNull(): ULong? = if (this < 0) null else this.asULong()
|
||||
|
||||
/**
|
||||
* Converts this [ULong] value to [Long].
|
||||
*
|
||||
* If this value is less than or equals to [Long.MAX_VALUE], the resulting [Long] value represents
|
||||
* the same numerical value as this [ULong].
|
||||
* Otherwise `null` returned.
|
||||
*/
|
||||
public inline fun ULong.toLongOrNull(): Long? = if (this >= Long.MAX_VALUE.asULong()) null else this.asLong()
|
||||
|
||||
|
||||
/********************************************* Conversion error formatters ****************************************/
|
||||
|
||||
@Suppress("FunctionName")
|
||||
private inline fun <reified S, reified U> _formatUnsignedToSignedOverflow(value: U, limit: S) =
|
||||
"Can't convert ${U::class.simpleName!!} value to ${S::class.simpleName!!} because $value > $limit"
|
||||
|
||||
@Suppress("FunctionName")
|
||||
private inline fun <reified S, reified U> _formatSignedToUnsignedNegative(value: S) =
|
||||
"Can't convert ${S::class.simpleName!!} value to ${U::class.simpleName!!} because $value is negative"
|
||||
|
||||
|
||||
public fun Byte.formatToUByteError(): String = _formatSignedToUnsignedNegative<Byte, UByte>(this)
|
||||
|
||||
public fun UByte.formatToByteError(): String = _formatUnsignedToSignedOverflow<Byte, UByte>(this, Byte.MAX_VALUE)
|
||||
|
||||
public fun Short.formatToUShortError(): String = _formatSignedToUnsignedNegative<Short, UShort>(this)
|
||||
|
||||
public fun UShort.formatToShortError(): String = _formatUnsignedToSignedOverflow<Short, UShort>(this, Short.MAX_VALUE)
|
||||
|
||||
public fun Int.formatToUIntError(): String = _formatSignedToUnsignedNegative<Int, UInt>(this)
|
||||
|
||||
public fun UInt.formatToIntError(): String = _formatUnsignedToSignedOverflow<Int, UInt>(this, Int.MAX_VALUE)
|
||||
|
||||
public fun Long.formatToULongError(): String = _formatSignedToUnsignedNegative<Long, ULong>(this)
|
||||
|
||||
public fun ULong.formatToLongError(): String = _formatUnsignedToSignedOverflow<Long, ULong>(this, Long.MAX_VALUE)
|
||||
|
||||
|
||||
/********************************************* Safe sign casts (or exception) ****************************************/
|
||||
|
||||
/**
|
||||
* Converts this [Byte] value to [UByte].
|
||||
*
|
||||
* If this value is positive, the resulting [UByte] value represents the same numerical value as this [Byte].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
|
||||
public inline fun Byte.toUByteOrThrow(exception: (Byte) -> Nothing = { b -> throw IllegalArgumentException(b.formatToUByteError()) }): UByte {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toUByteOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UByte] value to [Byte].
|
||||
*
|
||||
* If this value is less than or equals to [Byte.MAX_VALUE], the resulting [Byte] value represents
|
||||
* the same numerical value as this [UByte].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun UByte.toByteOrThrow(exception: (UByte) -> Nothing = { b -> throw IllegalArgumentException(b.formatToByteError()) }): Byte {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toByteOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this [Short] value to [UShort].
|
||||
*
|
||||
* If this value is positive, the resulting [UShort] value represents the same numerical value as this [Short].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun Short.toUShortOrThrow(exception: (Short) -> Nothing = { b -> throw IllegalArgumentException(b.formatToUShortError()) }): UShort {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toUShortOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [UShort] value to [Short].
|
||||
*
|
||||
* If this value is less than or equals to [Short.MAX_VALUE], the resulting [Short] value represents
|
||||
* the same numerical value as this [UShort].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun UShort.toShortOrThrow(exception: (UShort) -> Nothing = { b -> throw IllegalArgumentException(b.formatToShortError()) }): Short {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toShortOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this [Int] value to [UInt].
|
||||
*
|
||||
* If this value is positive, the resulting [UInt] value represents the same numerical value as this [Int].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun Int.toUIntOrThrow(exception: (Int) -> Nothing = { b -> throw IllegalArgumentException(b.formatToUIntError()) }): UInt {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toUIntOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this [UInt] value to [Int].
|
||||
*
|
||||
* If this value is less than or equals to [Int.MAX_VALUE], the resulting [Int] value represents
|
||||
* the same numerical value as this [UInt].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun UInt.toIntOrThrow(exception: (UInt) -> Nothing = { b -> throw IllegalArgumentException(b.formatToIntError()) }): Int {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toIntOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [Long] value to [ULong].
|
||||
*
|
||||
* If this value is positive, the resulting [ULong] value represents the same numerical value as this [Long].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun Long.toULongOrThrow(exception: (Long) -> Nothing = { b -> throw IllegalArgumentException(b.formatToULongError()) }): ULong {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toULongOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts this [ULong] value to [Long].
|
||||
*
|
||||
* If this value is less than or equals to [Long.MAX_VALUE], the resulting [Long] value represents
|
||||
* the same numerical value as this [ULong].
|
||||
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
|
||||
*/
|
||||
public inline fun ULong.toLongOrThrow(exception: (ULong) -> Nothing = { b -> throw IllegalArgumentException(b.formatToLongError()) }): Long {
|
||||
contract {
|
||||
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
|
||||
return this.toLongOrNull() ?: exception(this)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user