Safe size conversions for int types

This commit is contained in:
Andrew Golovashevich 2024-11-10 15:30:52 +03:00
parent 4f63a229e1
commit af6396fbcb

View File

@ -0,0 +1,438 @@
@file:JvmName("IntConversions_Size")
@file:Suppress("NOTHING_TO_INLINE")
package ru.landrafhomyak.kotlin.multiplatform_switches
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
/********************************** extend *****************************************/
/**
* Converts this [Byte] value to [Short].
*
* The resulting [Short] value represents the same numerical value as this [Byte].
*/
public inline fun Byte.asShort(): Short = this.toShort()
/**
* Converts this [Byte] value to [Int].
*
* The resulting [Int] value represents the same numerical value as this [Byte].
*/
public inline fun Byte.asInt(): Int = this.toInt()
/**
* Converts this [Byte] value to [Long].
*
* The resulting [Long] value represents the same numerical value as this [Byte].
*/
public inline fun Byte.asLong(): Long = this.toLong()
/**
* Converts this [UByte] value to [UShort].
*
* The resulting [UShort] value represents the same numerical value as this [UByte].
*/
public inline fun UByte.asUShort(): UShort = this.toUShort()
/**
* Converts this [UByte] value to [UInt].
*
* The resulting [UInt] value represents the same numerical value as this [UByte].
*/
public inline fun UByte.asUInt(): UInt = this.toUInt()
/**
* Converts this [UByte] value to [ULong].
*
* The resulting [ULong] value represents the same numerical value as this [UByte].
*/
public inline fun UByte.asULong(): ULong = this.toULong()
/**
* Converts this [Short] value to [Int].
*
* The resulting [Int] value represents the same numerical value as this [Short].
*/
public inline fun Short.asInt(): Int = this.toInt()
/**
* Converts this [Short] value to [Long].
*
* The resulting [Long] value represents the same numerical value as this [Short].
*/
public inline fun Short.asLong(): Long = this.toLong()
/**
* Converts this [UShort] value to [UInt].
*
* The resulting [UInt] value represents the same numerical value as this [UShort].
*/
public inline fun UShort.asUInt(): UInt = this.toUInt()
/**
* Converts this [UShort] value to [ULong].
*
* The resulting [ULong] value represents the same numerical value as this [UShort].
*/
public inline fun UShort.asULong(): ULong = this.toULong()
/**
* Converts this [Int] value to [Long].
*
* The resulting [Long] value represents the same numerical value as this [Int].
*/
public inline fun Int.asLong(): Long = this.toLong()
/**
* Converts this [UInt] value to [ULong].
*
* The resulting [ULong] value represents the same numerical value as this [UInt].
*/
public inline fun UInt.asULong(): ULong = this.toULong()
/******************************** trims (or null) ****************************/
/**
* Converts this [Short] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Short].
* Otherwise `null` returned.
*/
public inline fun Short.asByteOrNull(): Byte? = if (this in (Byte.MIN_VALUE.asShort())..(Byte.MAX_VALUE.asShort())) this.toByte() else null
/**
* Converts this [UShort] value to [UByte].
*
* If this value is in 0..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [UShort].
* Otherwise `null` returned.
*/
public inline fun UShort.asUByteOrNull(): UByte? = if (this <= UByte.MAX_VALUE.asUShort()) this.toUByte() else null
/**
* Converts this [Int] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Int].
* Otherwise `null` returned.
*/
public inline fun Int.asByteOrNull(): Byte? = if (this in (Byte.MIN_VALUE.asInt())..(Byte.MAX_VALUE.asInt())) this.toByte() else null
/**
* Converts this [Int] value to [Short].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Short] value represents
* the same numerical value as this [Int].
* Otherwise `null` returned.
*/
public inline fun Int.asShortOrNull(): Short? = if (this in (Short.MIN_VALUE.asInt())..(Short.MAX_VALUE.asInt())) this.toShort() else null
/**
* Converts this [UInt] value to [UByte].
*
* If this value is in [UByte.MIN_VALUE]..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [UInt].
* Otherwise `null` returned.
*/
public inline fun UInt.asUByteOrNull(): UByte? = if (this <= UByte.MAX_VALUE.asUInt()) this.toUByte() else null
/**
* Converts this [Int] value to [Short].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Short] value represents
* the same numerical value as this [Int].
* Otherwise `null` returned.
*/
public inline fun UInt.asUShortOrNull(): UShort? = if (this <= UShort.MAX_VALUE.asUInt()) this.toUShort() else null
/**
* Converts this [Long] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Long].
* Otherwise `null` returned.
*/
public inline fun Long.asByteOrNull(): Byte? = if (this in (Byte.MIN_VALUE.asLong())..(Byte.MAX_VALUE.asLong())) this.toByte() else null
/**
* Converts this [Long] value to [Short].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Short] value represents
* the same numerical value as this [Long].
* Otherwise `null` returned.
*/
public inline fun Long.asShortOrNull(): Short? = if (this in (Short.MIN_VALUE.asLong())..(Short.MAX_VALUE.asLong())) this.toShort() else null
/**
* Converts this [Long] value to [Int].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Int] value represents
* the same numerical value as this [Long].
* Otherwise `null` returned.
*/
public inline fun Long.asIntOrNull(): Int? = if (this in (Int.MIN_VALUE.asLong())..(Int.MAX_VALUE.asLong())) this.toInt() else null
/**
* Converts this [ULong] value to [UByte].
*
* If this value is in [UByte.MIN_VALUE]..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [ULong].
* Otherwise `null` returned.
*/
public inline fun ULong.asUByteOrNull(): UByte? = if (this in (UByte.MIN_VALUE.asULong())..(UByte.MAX_VALUE.asULong())) this.toUByte() else null
/**
* Converts this [ULong] value to [UShort].
*
* If this value is in [UShort.MIN_VALUE]..[UShort.MAX_VALUE], the resulting [UShort] value represents
* the same numerical value as this [ULong].
* Otherwise `null` returned.
*/
public inline fun ULong.asUShortOrNull(): UShort? = if (this in (UShort.MIN_VALUE.asULong())..(UShort.MAX_VALUE.asULong())) this.toUShort() else null
/**
* Converts this [ULong] value to [UInt].
*
* If this value is in [UShort.MIN_VALUE]..[UShort.MAX_VALUE], the resulting [UInt] value represents
* the same numerical value as this [ULong].
* Otherwise `null` returned.
*/
public inline fun ULong.asUIntOrNull(): UInt? = if (this in (UInt.MIN_VALUE.asULong())..(UInt.MAX_VALUE.asULong())) this.toUInt() else null
/********************************************* Conversion error formatters ****************************************/
@Suppress("FunctionName")
private inline fun <reified W, reified N> _formatTrimOverflowS(value: W, min: N, max: N) =
"Can't convert ${W::class.simpleName!!} value to ${N::class.simpleName!!} because $value !in ${min}..${max}"
@Suppress("FunctionName")
private inline fun <reified W, reified N> _formatTrimOverflowU(value: W, max: N) =
"Can't convert ${W::class.simpleName!!} value to ${N::class.simpleName!!} because $value > $max"
public fun Short.formatAsByteError(): String = _formatTrimOverflowS<Short, Byte>(this, Byte.MIN_VALUE, Byte.MAX_VALUE)
public fun UShort.formatAsUByteError(): String = _formatTrimOverflowU<UShort, UByte>(this, UByte.MAX_VALUE)
public fun Int.formatAsByteError(): String = _formatTrimOverflowS<Int, Byte>(this, Byte.MIN_VALUE, Byte.MAX_VALUE)
public fun Int.formatAsShortError(): String = _formatTrimOverflowS<Int, Short>(this, Short.MIN_VALUE, Short.MAX_VALUE)
public fun UInt.formatAsUByteError(): String = _formatTrimOverflowU<UInt, UByte>(this, UByte.MAX_VALUE)
public fun UInt.formatAsUShortError(): String = _formatTrimOverflowU<UInt, UShort>(this, UShort.MAX_VALUE)
public fun Long.formatAsByteError(): String = _formatTrimOverflowS<Long, Byte>(this, Byte.MIN_VALUE, Byte.MAX_VALUE)
public fun Long.formatAsShortError(): String = _formatTrimOverflowS<Long, Short>(this, Short.MIN_VALUE, Short.MAX_VALUE)
public fun Long.formatAsIntError(): String = _formatTrimOverflowS<Long, Int>(this, Int.MIN_VALUE, Int.MAX_VALUE)
public fun ULong.formatAsUByteError(): String = _formatTrimOverflowU<ULong, UByte>(this, UByte.MAX_VALUE)
public fun ULong.formatAsUShortError(): String = _formatTrimOverflowU<ULong, UShort>(this, UShort.MAX_VALUE)
public fun ULong.formatAsUIntError(): String = _formatTrimOverflowU<ULong, UInt>(this, UInt.MAX_VALUE)
/******************************** trims (or exception) ****************************/
/**
* Converts this [Short] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Short].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Short.asByteOrThrow(exception: (Short) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsByteError()) }): Byte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asByteOrNull() ?: exception(this)
}
/**
* Converts this [UShort] value to [UByte].
*
* If this value is in 0..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [UShort].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun UShort.asByteOrThrow(exception: (UShort) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUByteError()) }): UByte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUByteOrNull() ?: exception(this)
}
/**
* Converts this [Int] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Int].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Int.asByteOrThrow(exception: (Int) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsByteError()) }): Byte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asByteOrNull() ?: exception(this)
}
/**
* Converts this [Int] value to [Short].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Short] value represents
* the same numerical value as this [Int].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Int.asShortOrThrow(exception: (Int) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsShortError()) }): Short {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asShortOrNull() ?: exception(this)
}
/**
* Converts this [UInt] value to [UByte].
*
* If this value is in 0..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [UInt].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun UInt.asUByteOrThrow(exception: (UInt) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUByteError()) }): UByte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUByteOrNull() ?: exception(this)
}
/**
* Converts this [UInt] value to [UShort].
*
* If this value is in 0..[UShort.MAX_VALUE], the resulting [UShort] value represents
* the same numerical value as this [UShort].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun UInt.asUShortOrThrow(exception: (UInt) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUShortError()) }): UShort {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUShortOrNull() ?: exception(this)
}
/**
* Converts this [Long] value to [Byte].
*
* If this value is in [Byte.MIN_VALUE]..[Byte.MAX_VALUE], the resulting [Byte] value represents
* the same numerical value as this [Long].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Long.asByteOrThrow(exception: (Long) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsByteError()) }): Byte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asByteOrNull() ?: exception(this)
}
/**
* Converts this [Long] value to [Short].
*
* If this value is in [Short.MIN_VALUE]..[Short.MAX_VALUE], the resulting [Short] value represents
* the same numerical value as this [Long].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Long.asShortOrThrow(exception: (Long) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsShortError()) }): Short {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asShortOrNull() ?: exception(this)
}
/**
* Converts this [Long] value to [Int].
*
* If this value is in [Int.MIN_VALUE]..[Int.MAX_VALUE], the resulting [Int] value represents
* the same numerical value as this [Long].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun Long.asIntOrThrow(exception: (Long) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsIntError()) }): Int {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asIntOrNull() ?: exception(this)
}
/**
* Converts this [ULong] value to [UByte].
*
* If this value is in 0..[UByte.MAX_VALUE], the resulting [UByte] value represents
* the same numerical value as this [ULong].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun ULong.asUByteOrThrow(exception: (ULong) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUByteError()) }): UByte {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUByteOrNull() ?: exception(this)
}
/**
* Converts this [ULong] value to [UShort].
*
* If this value is in 0..[UShort.MAX_VALUE], the resulting [UShort] value represents
* the same numerical value as this [ULong].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun ULong.asUShortOrThrow(exception: (ULong) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUShortError()) }): UShort {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUShortOrNull() ?: exception(this)
}
/**
* Converts this [ULong] value to [UInt].
*
* If this value is in 0..[UInt.MAX_VALUE], the resulting [UInt] value represents
* the same numerical value as this [ULong].
* Otherwise [exception] function called (by default throws [IllegalArgumentException]).
*/
public inline fun ULong.asUIntOrThrow(exception: (ULong) -> Nothing = { b -> throw IllegalArgumentException(b.formatAsUIntError()) }): UInt {
contract {
callsInPlace(exception, InvocationKind.AT_MOST_ONCE)
}
return this.asUIntOrNull() ?: exception(this)
}