Skip to content

Commit ca1c1f1

Browse files
committed
working implementation of BigInteger/BigDecimal for Kotlin/Native
1 parent 66c7e83 commit ca1c1f1

File tree

7 files changed

+270
-66
lines changed

7 files changed

+270
-66
lines changed

runtime/build.gradle.kts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,27 @@ subprojects {
8888
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile> {
8989
compilerOptions {
9090
freeCompilerArgs.add("-Xexpect-actual-classes")
91+
92+
// FIXME When building LinuxX64 on AL2 the linker inclues a bunch of dynamic links to unavailable versions
93+
// of zlib. The below workaround forces the linker to statically link zlib but it's a hack because the
94+
// linker will still dynamically link zlib (although the executable will no longer fail at runtime due to
95+
// link resolution failures). The correct solution for this is probably containerized builds similar to
96+
// what we do in aws-crt-kotlin. The following compiler args were helpful in debugging this issue:
97+
// * Enable verbose compiler output : -verbose
98+
// * Increase verbosity during the compiler's linker phase : -Xverbose-phases=Linker
99+
// * Enable verbose linker output from gold : -linker-option --verbose
100+
if (target.contains("linux", ignoreCase = true)) {
101+
freeCompilerArgs.addAll(
102+
listOf(
103+
"-linker-option", // The subsequent argument is for the linker
104+
"-Bstatic", // Enable static linking for the libraries that follow
105+
"-linker-option", // The subsequent argument is for the linker
106+
"-lz", // Link zlib statically (because of -Bstatic above)
107+
"-linker-option", // The subsequent argument is for the linker
108+
"-Bdynamic", // Restore dynamic linking, which is the default
109+
),
110+
)
111+
}
91112
}
92113
}
93114
}

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/content/BigDecimal.kt

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,94 @@ package aws.smithy.kotlin.runtime.content
1111
public expect class BigDecimal(value: String) :
1212
Number,
1313
Comparable<BigDecimal> {
14+
1415
/**
1516
* Create an instance of [BigDecimal] from a mantissa and exponent.
16-
* @param mantissa a [BigInteger] representing the mantissa of this big decimal
17-
* @param exponent an [Int] representing the exponent of this big decimal
17+
* @param mantissa a [BigInteger] representing the [significant digits](https://en.wikipedia.org/wiki/Significand)
18+
* of this decimal value
19+
* @param exponent an [Int] representing the exponent of this decimal value
1820
*/
1921
public constructor(mantissa: BigInteger, exponent: Int)
2022

2123
/**
22-
* The mantissa of this decimal number
24+
* The [significant digits](https://en.wikipedia.org/wiki/Significand) of this decimal value
2325
*/
2426
public val mantissa: BigInteger
2527

2628
/**
27-
* The exponent of this decimal number.
28-
* If zero or positive, this represents the number of digits to the right of the decimal point.
29-
* If negative, the mantissa is multiplied by ten to the power of the negation of the scale.
29+
* The exponent of this decimal number. If zero or positive, this represents the number of digits to the right of
30+
* the decimal point. If negative, the [mantissa] is multiplied by ten to the power of the negation of the scale.
3031
*/
3132
public val exponent: Int
3233

34+
/**
35+
* Converts this value to a [Byte], which may involve rounding or truncation
36+
*/
3337
override fun toByte(): Byte
38+
39+
/**
40+
* Converts this value to a [Double], which may involve rounding or truncation
41+
*/
3442
override fun toDouble(): Double
43+
44+
/**
45+
* Converts this value to a [Float], which may involve rounding or truncation
46+
*/
3547
override fun toFloat(): Float
48+
49+
/**
50+
* Converts this value to a [Short], which may involve rounding or truncation
51+
*/
3652
override fun toShort(): Short
53+
54+
/**
55+
* Converts this value to an [Int], which may involve rounding or truncation
56+
*/
3757
override fun toInt(): Int
58+
59+
/**
60+
* Converts this value to a [Long], which may involve rounding or truncation
61+
*/
3862
override fun toLong(): Long
63+
64+
/**
65+
* Returns the decimal (i.e., radix-10) string representation of this value in long-form (i.e., _not_ scientific)
66+
* notation
67+
*/
3968
public fun toPlainString(): String
69+
70+
/**
71+
* Returns the decimal (i.e., radix-10) string representation of this value using scientific notation if an exponent
72+
* is needed
73+
*/
74+
override fun toString(): String
75+
76+
/**
77+
* Returns a hash code for this value
78+
*/
79+
override fun hashCode(): Int
80+
81+
/**
82+
* Checks if this value is equal to the given object
83+
* @param other The other value to compare against
84+
*/
4085
override fun equals(other: Any?): Boolean
86+
87+
/**
88+
* Returns the sum of this value and the given value
89+
* @param other The other value to add (i.e., the addend)
90+
*/
91+
public operator fun plus(other: BigDecimal): BigDecimal
92+
93+
/**
94+
* Returns the difference of this value and the given value
95+
* @param other The value to subtract (i.e., the subtrahend)
96+
*/
97+
public operator fun minus(other: BigDecimal): BigDecimal
98+
99+
/**
100+
* Compare this value to the given value for in/equality
101+
* @param other The value to compare against
102+
*/
41103
public override operator fun compareTo(other: BigDecimal): Int
42104
}

runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/content/BigInteger.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,86 @@ package aws.smithy.kotlin.runtime.content
1111
public expect class BigInteger(value: String) :
1212
Number,
1313
Comparable<BigInteger> {
14+
1415
/**
1516
* Create an instance of [BigInteger] from a [ByteArray]
1617
* @param bytes ByteArray representing the large integer
1718
*/
1819
public constructor(bytes: ByteArray)
1920

21+
/**
22+
* Converts this value to a [Byte], which may involve rounding or truncation
23+
*/
2024
override fun toByte(): Byte
25+
26+
/**
27+
* Converts this value to a [Long], which may involve rounding or truncation
28+
*/
2129
override fun toLong(): Long
30+
31+
/**
32+
* Converts this value to a [Short], which may involve rounding or truncation
33+
*/
2234
override fun toShort(): Short
35+
36+
/**
37+
* Converts this value to an [Int], which may involve rounding or truncation
38+
*/
2339
override fun toInt(): Int
40+
41+
/**
42+
* Converts this value to a [Float], which may involve rounding or truncation
43+
*/
2444
override fun toFloat(): Float
45+
46+
/**
47+
* Converts this value to a [Double], which may involve rounding or truncation
48+
*/
2549
override fun toDouble(): Double
50+
51+
/**
52+
* Returns the decimal (i.e., radix-10) string representation of this value
53+
*/
2654
override fun toString(): String
55+
56+
/**
57+
* Returns a string representation of this value in the given radix
58+
* @param radix The [numerical base](https://en.wikipedia.org/wiki/Radix) in which to represent the value
59+
*/
60+
public fun toString(radix: Int = 10): String
61+
62+
/**
63+
* Returns a hash code for this value
64+
*/
2765
override fun hashCode(): Int
66+
67+
/**
68+
* Checks if this value is equal to the given object
69+
* @param other The other value to compare against
70+
*/
2871
override fun equals(other: Any?): Boolean
72+
73+
/**
74+
* Returns the sum of this value and the given value
75+
* @param other The other value to add (i.e., the addend)
76+
*/
2977
public operator fun plus(other: BigInteger): BigInteger
78+
79+
/**
80+
* Returns the difference of this value and the given value
81+
* @param other The value to subtract (i.e., the subtrahend)
82+
*/
3083
public operator fun minus(other: BigInteger): BigInteger
84+
85+
/**
86+
* Returns the [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) binary representation of this
87+
* value
88+
*/
3189
public fun toByteArray(): ByteArray
90+
91+
/**
92+
* Compare this value to the given value for in/equality
93+
* @param other The value to compare against
94+
*/
3295
public override operator fun compareTo(other: BigInteger): Int
3396
}

runtime/runtime-core/jvm/src/aws/smithy/kotlin/runtime/content/BigDecimalJVM.kt

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,52 @@
44
*/
55
package aws.smithy.kotlin.runtime.content
66

7-
public actual class BigDecimal actual constructor(public val value: String) :
7+
import java.math.BigDecimal as JvmBigDecimal
8+
9+
public actual class BigDecimal private constructor(private val delegate: JvmBigDecimal) :
810
Number(),
911
Comparable<BigDecimal> {
10-
private val delegate = java.math.BigDecimal(value)
1112

12-
public actual constructor(mantissa: BigInteger, exponent: Int) : this(
13-
java.math.BigDecimal(
14-
java.math.BigInteger(mantissa.toString()),
15-
exponent,
16-
).toPlainString(),
17-
)
13+
private companion object {
14+
/**
15+
* Returns a new or existing [BigDecimal] wrapper for the given delegate [value]
16+
* @param value The delegate value to wrap
17+
* @param left A candidate wrapper which may already contain [value]
18+
* @param right A candidate wrapper which may already contain [value]
19+
*/
20+
fun coalesceOrCreate(value: JvmBigDecimal, left: BigDecimal, right: BigDecimal): BigDecimal = when (value) {
21+
left.delegate -> left
22+
right.delegate -> right
23+
else -> BigDecimal(value)
24+
}
25+
}
26+
27+
public actual constructor(value: String) : this(JvmBigDecimal(value))
28+
public actual constructor(mantissa: BigInteger, exponent: Int) : this(JvmBigDecimal(mantissa.delegate, exponent))
1829

1930
public actual fun toPlainString(): String = delegate.toPlainString()
20-
actual override fun toByte(): Byte = delegate.toByte()
21-
actual override fun toDouble(): Double = delegate.toDouble()
22-
actual override fun toFloat(): Float = delegate.toFloat()
23-
actual override fun toInt(): Int = delegate.toInt()
24-
actual override fun toLong(): Long = delegate.toLong()
25-
actual override fun toShort(): Short = delegate.toShort()
31+
public actual override fun toString(): String = delegate.toString()
32+
public actual override fun toByte(): Byte = delegate.toByte()
33+
public actual override fun toDouble(): Double = delegate.toDouble()
34+
public actual override fun toFloat(): Float = delegate.toFloat()
35+
public actual override fun toInt(): Int = delegate.toInt()
36+
public actual override fun toLong(): Long = delegate.toLong()
37+
public actual override fun toShort(): Short = delegate.toShort()
2638

27-
actual override fun equals(other: Any?): Boolean = other is BigDecimal && delegate == other.delegate
39+
public actual override fun equals(other: Any?): Boolean = other is BigDecimal && delegate == other.delegate
40+
public actual override fun hashCode(): Int = 31 + delegate.hashCode()
2841

2942
public actual val mantissa: BigInteger
30-
get() = BigInteger(delegate.unscaledValue().toString())
43+
get() = BigInteger(delegate.unscaledValue())
3144

3245
public actual val exponent: Int
3346
get() = delegate.scale()
3447

48+
public actual operator fun plus(other: BigDecimal): BigDecimal =
49+
coalesceOrCreate(delegate + other.delegate, this, other)
50+
51+
public actual operator fun minus(other: BigDecimal): BigDecimal =
52+
coalesceOrCreate(delegate - other.delegate, this, other)
53+
3554
actual override fun compareTo(other: BigDecimal): Int = delegate.compareTo(other.delegate)
3655
}

runtime/runtime-core/jvm/src/aws/smithy/kotlin/runtime/content/BigIntegerJVM.kt

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,47 @@
44
*/
55
package aws.smithy.kotlin.runtime.content
66

7-
public actual class BigInteger actual constructor(public val value: String) :
7+
import java.math.BigInteger as JvmBigInteger
8+
9+
public actual class BigInteger internal constructor(internal val delegate: JvmBigInteger) :
810
Number(),
911
Comparable<BigInteger> {
10-
private val delegate = java.math.BigInteger(value)
1112

12-
public actual constructor(bytes: ByteArray) : this(java.math.BigInteger(bytes).toString())
13+
private companion object {
14+
/**
15+
* Returns a new or existing [BigInteger] wrapper for the given delegate [value]
16+
* @param value The delegate value to wrap
17+
* @param left A candidate wrapper which may already contain [value]
18+
* @param right A candidate wrapper which may already contain [value]
19+
*/
20+
fun coalesceOrCreate(value: JvmBigInteger, left: BigInteger, right: BigInteger): BigInteger = when (value) {
21+
left.delegate -> left
22+
right.delegate -> right
23+
else -> BigInteger(value)
24+
}
25+
}
26+
27+
public actual constructor(value: String) : this(JvmBigInteger(value))
28+
public actual constructor(bytes: ByteArray) : this(JvmBigInteger(bytes))
1329

1430
public actual override fun toByte(): Byte = delegate.toByte()
1531
public actual override fun toLong(): Long = delegate.toLong()
1632
public actual override fun toShort(): Short = delegate.toShort()
1733
public actual override fun toInt(): Int = delegate.toInt()
1834
public actual override fun toFloat(): Float = delegate.toFloat()
1935
public actual override fun toDouble(): Double = delegate.toDouble()
20-
public actual override fun toString(): String = delegate.toString()
21-
public actual override fun hashCode(): Int = delegate.hashCode()
36+
public actual override fun toString(): String = toString(10)
37+
public actual fun toString(radix: Int): String = delegate.toString(radix)
38+
39+
public actual override fun hashCode(): Int = 17 + delegate.hashCode()
2240
public actual override fun equals(other: Any?): Boolean = other is BigInteger && delegate == other.delegate
2341

24-
public actual operator fun plus(other: BigInteger): BigInteger = BigInteger((delegate + other.delegate).toString())
25-
public actual operator fun minus(other: BigInteger): BigInteger = BigInteger((delegate - other.delegate).toString())
42+
public actual operator fun plus(other: BigInteger): BigInteger =
43+
coalesceOrCreate(delegate + other.delegate, this, other)
44+
45+
public actual operator fun minus(other: BigInteger): BigInteger =
46+
coalesceOrCreate(delegate - other.delegate, this, other)
47+
2648
public actual override operator fun compareTo(other: BigInteger): Int = delegate.compareTo(other.delegate)
2749
public actual fun toByteArray(): ByteArray = delegate.toByteArray()
2850
}

0 commit comments

Comments
 (0)