Dealing with endianess in Kotlin

156 views Asked by At

I'm trying to deserialize a byte array in an Android app (compiled on MacOS with M1 chip). The bytes come from the network, which is generated by a C++ application running on Ubuntu. in the C++ application, I checked that it's using Little Endian:

bool isBigEndian()
{
    uint16_t word = 1;                      // 0x0001
    uint8_t *first_byte = (uint8_t *)&word; // points to the first byte of word
    return !(*first_byte);                  // true if the first byte is zero
}

// Check:
if (isBigEndian())
    printf("Big endian\n");
else
    printf("Little endian\n");

Above code in C++ print out Little endian

On the Kotlin (Android) side, I also checked and see it's using Little Endian too, but the converted long number from the byte array is not correct.

fun isLittleEndian(): Boolean {
    return ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN
}

/**
 * Represent 8 bytes of [Long] into byte array
 */
fun Long.toBytes(): ByteArray {
    return ByteBuffer.allocate(Long.SIZE_BYTES).putLong(this).array()
}
fun ByteArray.toLong(): Long {
    return ByteBuffer.wrap(this).long
}
fun test(){
    val longNumber = 1000L
    val isLittleEndian = isLittleEndian() // true
    val bytes = longNumber.toBytes()      // bytes: [0,0,0,0,0,0,3,-24] => Big Endian?
}

C++ app serializes the long number 1000 into [ -24, 3,0,0,0,0,0,0] (correct Little Endian ordering) while Kotlin code converts the same long number into [0,0,0,0,0,0,3,-24] (This is Big Endian ordering).

When converting the bytes from C++ app using Kotlin, I got strange value -1728537831980138496 instead of 1000

Please help to check if I made any mistake while dealing with the endianess?

1

There are 1 answers

0
Slaw On BEST ANSWER

The allocate and wrap methods will both always return a BIG_ENDIAN buffer. You have to call order to change the endianness to LITTLE_ENDIAN.

For example:

import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.ByteOrder.BIG_ENDIAN
import java.nio.ByteOrder.LITTLE_ENDIAN

fun Long.toBytes(order: ByteOrder = BIG_ENDIAN): ByteArray {
    return ByteBuffer.allocate(Long.SIZE_BYTES).order(order).putLong(this).array()
}

fun ByteArray.toLong(order: ByteOrder = BIG_ENDIAN): Long {
    return ByteBuffer.wrap(this).order(order).long
}

fun main() { 
    val number = 1000L
    val encoded = number.toBytes(order = LITTLE_ENDIAN)
    val decoded = encoded.toLong(order = LITTLE_ENDIAN)
    
    println("Number  = $number")
    println("Encoded = ${encoded.contentToString()}")
    println("Decoded = $decoded")
}

Output:

Number  = 1000
Encoded = [-24, 3, 0, 0, 0, 0, 0, 0]
Decoded = 1000