Home > Software design >  Calculate CRC32 in Kotlin (convert C# Code to Kotlin)
Calculate CRC32 in Kotlin (convert C# Code to Kotlin)

Time:10-07

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

public static UInt32 CalcCrc32(byte[] bytes, int length)
{
    // GLB_u32CalcCrc32OverBytes
    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 calcCrc32(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

import com.google.common.truth.Truth.assertThat
import com.proregia.pump.common.CrcUtil
import org.junit.Test

class CrcUtilTest {


    @Test
    fun crc16_correctByteArray_returnsCorrectCrc16() {


        val data = byteArrayOf(
            0xe8.toByte(),
            0x03.toByte(),
            0x00.toByte(),
            0x00.toByte(),

            0x3c.toByte(),
            0x00.toByte(),
            0x00.toByte(),
            0x00.toByte(),

            0x90.toByte(),
            0x01.toByte(),
            0x00.toByte(),
            0x00.toByte(),

            0x02.toByte(),

            0x00.toByte(),
            0x00.toByte()
        )

        CrcUtil.updateCrc16(data)

        assertThat(data[13]).isEqualTo(0xAD)
        assertThat(data[14]).isEqualTo(0xC1)

    }

}

CodePudding user response:

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

CodePudding user response:

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

  • Related