muetzenflo
muetzenflo

Reputation: 5700

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)

    }

}

Upvotes: 0

Views: 1765

Answers (2)

Mark Adler
Mark Adler

Reputation: 112502

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
}

Upvotes: 2

user2340612
user2340612

Reputation: 10733

The JDK has a built-in class that allows you to compute the CRC32 of a given value: https://docs.oracle.com/javase/8/docs/api/java/util/zip/CRC32.html

You can take advantage of Kotlin's interoperability with Java to compute the CRC32 checksum of a given value:

import java.util.zip.CRC32

fun calculateCRC32(value: ByteArray): Long {
    val crc32Calculator = CRC32()
    crc32Calculator.update(value)
    return crc32Calculator.value
}

println(calculateCRC32("Some text".toByteArray())) // prints 3444260633

Upvotes: 0

Related Questions