52

Is there a better/shorter way in creating byte array from constant hex than the version below?

byteArrayOf(0xA1.toByte(), 0x2E.toByte(), 0x38.toByte(), 0xD4.toByte(), 0x89.toByte(), 0xC3.toByte())

I tried to put 0xA1 without .toByte() but I receive syntax error complaint saying integer literal does not conform to the expected type Byte. Putting integer is fine but I prefer in hex form since my source is in hex string. Any hints would be greatly appreciated. Thanks!

You Qi
  • 8,353
  • 8
  • 50
  • 68

5 Answers5

50

as an option you can create simple function

fun byteArrayOfInts(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }

and use it

val arr = byteArrayOfInts(0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3)
44

If all your bytes were less than or equal to 0x7F, you could put them directly:

byteArrayOf(0x2E, 0x38)

If you need to use bytes greater than 0x7F, you can use unsigned literals to make a UByteArray and then convert it back into a ByteArray:

ubyteArrayOf(0xA1U, 0x2EU, 0x38U, 0xD4U, 0x89U, 0xC3U).toByteArray()

I think it's a lot better than appending .toByte() at every element, and there's no need to define a custom function as well.

However, Kotlin's unsigned types are an experimental feature, so you may have some trouble with warnings.

Milack27
  • 1,619
  • 2
  • 20
  • 31
12

The issue is that bytes in Kotlin are signed, which means they can only represent values in the [-128, 127] range. You can test this by creating a ByteArray like this:

val limits = byteArrayOf(-0x81, -0x80, -0x79, 0x00, 0x79, 0x80)

Only the first and last values will produce an error, because they are out of the valid range by 1.

This is the same behaviour as in Java, and the solution will probably be to use a larger number type if your values don't fit in a Byte (or offset them by 128, etc).


Side note: if you print the contents of the array you've created with toInt calls, you'll see that your values larger than 127 have flipped over to negative numbers:

val bytes = byteArrayOf(0xA1.toByte(), 0x2E.toByte(), 0x38.toByte(), 0xD4.toByte(), 0x89.toByte(), 0xC3.toByte())
println(bytes.joinToString()) // -95, 46, 56, -44, -119, -61
zsmb13
  • 85,752
  • 11
  • 221
  • 226
6

I just do:

val bytes = listOf(0xa1, 0x2e, 0x38, 0xd4, 0x89, 0xc3)
    .map { it.toByte() }
    .toByteArray()
user511824
  • 316
  • 5
  • 7
2

Just to improve upon the accepted solution, I took an inspiration from here. Basically, since Kotlin declares its own Byte class, we can augment an operator onto its companion object, like so:

operator fun Byte.Companion.get(vararg ints: Int) = ByteArray(ints.size) { pos -> ints[pos].toByte() }

Then, you can declare a Byte array more "naturally":

val bytes = Byte[0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3]
b0nyb0y
  • 1,436
  • 1
  • 14
  • 14