# N-elements byte array to integer conversion (little/big endian and signed/unsigned)

I'm trying to come up with a function to convert byte arrays to their integer representation, using both endianness, and with a signed/unsigned option.

enum class ByteOrder {
LITTLE,
BIG
}

fun ByteArray.toInt(byteOrder: ByteOrder = LITTLE, signed: Boolean = true): Int {
if (isEmpty()) {
throw UnsupportedOperationException("Cannot convert an empty byte array")
}

val negative = signed &&
(this[if (byteOrder == LITTLE) size - 1 else 0]).toInt() and 0b1000000 != 0

if (!negative) {
return absoluteToInt(byteOrder)
}

// The number is stored as two's complement.
// Thus we invert each byte and then sum 1 to obtain the absolute value
val negatedBytes = ByteArray(size)

for ((i, byte) in withIndex()) {
negatedBytes[i] = byte.inv()
}

return -(negatedBytes.absoluteToInt(byteOrder) + 1)
}

private fun ByteArray.absoluteToInt(byteOrder: ByteOrder): Int {
var result = 0
var shift = 8 * (size - 1)
val range =
if (byteOrder == LITTLE) {
size - 1 downTo 0
} else {
0 until size
}

for (i in range) {
result = this[i].toInt() and 0b11111111 shl shift or result
shift -= 8
}

return result
}


You can submit a byte array of arbitrary size. Example:

val bytes: ByteArray = 9388608.toBytes() // Little endian, 4 elements
val intVal: Int = bytes.toInt() // Little endian


What's your opinion? Can this be done in a better way?

• Explicitly separating the logic for converting signed vs unsigned would help with readability I think Commented Jul 11, 2020 at 22:11
• @TedBrownlow the unsigned switch is basically signed &&, so there is no logic to separate imho. Commented Jul 11, 2020 at 22:14