Reputation: 97140
Kotlin 1.3 introduced unsigned integer types, but I can't seem to figure out how to get an unsigned integer from a ByteArray
in Kotlin JVM.
Kotlin Native has a convenient ByteArray.getUIntAt()
method, but this does not exist for Kotlin JVM.
val bytes: ByteArray = byteArrayOf(1, 1, 1, 1)
val uint: UInt // = ???
What are my options here? Is there a more elegant way than using a ByteBuffer
, or bit-shifting my way out of this?
Upvotes: 13
Views: 13171
Reputation: 3201
Shocked that there is no such library yet, I've created https://github.com/mvysny/kotlin-unsigned-jvm . It's in Maven Central, please take a look. Adds extension functions for ByteArray
and supports both Endian.Big and Endian.Little.
Upvotes: 2
Reputation: 812
Great answers here. Here is a solution that can take a byte array of any size and do the proper conversion (I used the above answers as a starting point)
fun ByteArray.toUInt(): UInt {
//Note: UInt is always 32 bits (4 bytes) regardless of platform architecture
//See https://kotlinlang.org/docs/basic-types.html#unsigned-integers
val bytes = 4
val paddedArray = ByteArray(bytes) { 0 }
val offset = (size-bytes)
for (i in (size-1).coerceAtLeast(0) downTo offset.coerceAtLeast(0)) {
paddedArray[i-offset] = this[i]
}
return ((paddedArray[0].toUInt() and 0xFFu) shl 24) or
((paddedArray[1].toUInt() and 0xFFu) shl 16) or
((paddedArray[2].toUInt() and 0xFFu) shl 8) or
(paddedArray[3].toUInt() and 0xFFu)
}
Upvotes: 0
Reputation: 1718
To expand upon @Alexander's excellent answer, I wrote a version that accepts ByteArrays that have fewer than four values. I've found this particularly useful when parsing ByteArrays with a mix of signed and unsigned integers (in my case bluetooth characteristic update notifications) that I need to slice into sub-arrays.
fun ByteArray.fromUnsignedBytesToInt(): Int {
//Note: UInt is always 32 bits (4 bytes) regardless of platform architecture
//See https://kotlinlang.org/docs/basic-types.html#unsigned-integers
val bytes = 4
val paddedArray = ByteArray(bytes)
for (i in 0 until bytes-this.size) paddedArray[i] = 0
for (i in bytes-this.size until paddedArray.size) paddedArray[i] = this[i-(bytes-this.size)]
return (((paddedArray[0].toULong() and 0xFFu) shl 24) or
((paddedArray[1].toULong() and 0xFFu) shl 16) or
((paddedArray[2].toULong() and 0xFFu) shl 8) or
(paddedArray[3].toULong() and 0xFFu)).toInt()
}
The test class below confirms the expected values:
import org.junit.Test
import org.junit.Assert.*
internal class ByteArrayExtKtTest {
@Test
fun testAsUnsignedToIntTwoBytes() {
val bytes = byteArrayOf (0x0A.toByte(), 0xBA.toByte())
assertEquals(2746, bytes.fromUnsignedBytesToInt())
}
@Test
fun testAsUnsignedToIntFourBytes() {
val bytes = byteArrayOf(1,1,1,1)
assertEquals(16843009, bytes.fromUnsignedBytesToInt())
}
}
Upvotes: 1
Reputation: 5300
As mentioned in the comments there is no out of the box solution in the JVM version of Kotlin. An extension function doing the same as the Kotlin/Native function might look like this:
fun ByteArray.getUIntAt(idx: Int) =
((this[idx].toUInt() and 0xFFu) shl 24) or
((this[idx + 1].toUInt() and 0xFFu) shl 16) or
((this[idx + 2].toUInt() and 0xFFu) shl 8) or
(this[idx + 3].toUInt() and 0xFFu)
fun main(args: Array<String>) {
// 16843009
println(byteArrayOf(1, 1, 1, 1).getUIntAt(0))
// 4294967295, which is UInt.MAX_VALUE
println(byteArrayOf(-1, -1, -1, -1).getUIntAt(0))
}
Upvotes: 18