kotlincrcunsigned-integercrc32

Calculate CRC32 in Kotlin (convert C# Code to Kotlin)


Could someone help me out with converting this C# code to Kotlin?

public static UInt32 CalculdateCrc32(byte[] bytes, int length)
{
    UInt32 Checksum = 0xFFFFFFFF;
    for (int i = 0; i < length; i++)
    {
        byte top = (byte)(Checksum >> 24);
        top ^= bytes[i];
        Checksum = (Checksum << 8) ^ crc_table[top];
    }
    return Checksum;
}

It allows the CRC32 caluclation of the first length bytes of bytes.

I have tried different approaches to deal with the unsigned datatypes, but I cannot get it to return the correct CRC.

This was the closest I got

Generating the crc table (taken from this repo)

private val crcTable = (0 until 256).map {
    crc32(it.toUByte(), 0x04C11DB7.toUInt())
}

private fun crc32(input: UByte, polynomial: UInt): UInt {
    val bigEndianInput = input.toBigEndianUInt()

    return (0 until 8).fold(bigEndianInput) { result, _ ->
        val isMostSignificantBitOne = result and 0x80000000.toUInt() != 0.toUInt()
        val shiftedResult = result shl 1

        when (isMostSignificantBitOne) {
            true -> shiftedResult xor polynomial
            false -> shiftedResult
        }
    }
}

private fun UByte.toBigEndianUInt(): UInt = this.toUInt() shl 24

Converting the C# method to Kotlin

private fun calculateCrc32(bytes: ByteArray, length: Int): UInt {
    var checksum : UInt = 0xFFFFFFFFu
    for (i in 0 until length) {
        var top = (checksum shr 24).toByte()
        top = top xor bytes[i]
        checksum = checksum shl 8 xor crcTable[top.toInt()]
    }
    return checksum
}

But this code throws an IndexOutOfBoundsException, because top ends up being -1.

Unit Test

class CrcUtilTest {


    @Test
    fun crc16_correctByteArray_returnsCorrectCrc16() {

        val data = byteArrayOf(
            0xaa.toByte(),
            0xbb.toByte(),
            0xcc.toByte(),
            0xdd.toByte(),

            0xaa.toByte(),
            0xbb.toByte(),
            0xcc.toByte(),
            0xdd.toByte(),

            0xaa.toByte(),
            0xbb.toByte(),
            0xcc.toByte(),
            0xdd.toByte(),

            0xaa.toByte(),

            0xaa.toByte(),
            0xbb.toByte()
        )

        CrcUtil.updateCrc16(data)

        assertThat(data[13]).isEqualTo(0xaa)
        assertThat(data[14]).isEqualTo(0xbb)

    }

}

Solution

  • Try toUByte() instead of toByte() in calcCrc32(), also applying it to the result of bytes[i].

    private fun calcCrc32(bytes: ByteArray, length: Int): UInt {
        var checksum: UInt = 0xFFFFFFFFu
        val uBytes = bytes.toUByteArray()
        for (i in 0 until length) {
            var top = (checksum shr 24).toUByte()
            top = top xor uBytes[i]
            checksum = checksum shl 8 xor crcTable[top.toInt()]
        }
        return checksum
    }