Robby Cornelissen
Robby Cornelissen

Reputation: 97140

How to get an unsigned integer from a byte array in Kotlin JVM?

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

Answers (4)

Martin Vysny
Martin Vysny

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

Marc DiNino
Marc DiNino

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

James Jordan Taylor
James Jordan Taylor

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

Alexander Egger
Alexander Egger

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

Related Questions