Kotlin:编码和解码二进制结构数据(Kotlin 等效于 Python 的 struct.pack 和 struct.unpack)

Kotlin: encode and decode binary struct data (Kotlin equivalent for Python's struct.pack and struct.unpack)

对于基于 C 的嵌入式硬件模块,配置结构由特定布局中的多个字段组成,例如以这个 8 字节的结构为例:

Offset Datatype Field
0 UInt8 fieldA
1 UInt16 some_value
3 UInt32 another_value
7 UInt8 aByte

这个8字节的配置可以通过NFC通信进行读写。所以有一个 Android 应用程序读取此数据(作为 8 个字节的序列)并将其写回,以便嵌入式硬件模块(用 C 编写)上的固件可以“理解”它。

现在的任务是解码 8 字节序列,例如12 ab cd 04 fe ff 56 77 (little-endian!) 到解码值中:

Field Bytes to be decoded Decoded, human-readable number
fieldA 12 0x12
some_value ab cd 0xCDAB
another_value 04 fe ff 56 0x56FFFE04
aByte 77 0x77

请注意,这是 Kotlin 或 Java 问题,不是 C 问题 ;)

现在我的问题是找到一种 Kotlin 方法来将这样的二进制结构解码为相应的值(如上所示),以便可以将这些值呈现给应用程序用户。并且,将值(在用户编辑了一些值之后)编码回 8 字节的二进制结构。

请注意字节顺序也是一个问题。一般来说,目标系统是小端 ARM,Android 应用程序也在小端系统上运行,因此可能没有问题。然而,这是巧合,我想明确这一点。

什么是 Kotlin 将 decoding/encoding 数字转换为此类字节的方式,使用必要的显式字节序转换?

如果是 Python,struct 库及其 packunpack 函数是 PERFECT任务。但是如何在 Kotlin 中做到这一点?我很想看到这样的功能......

我能想到的最好的办法就是将字节数组包装在一个ByteBuffer中,然后一个一个地读取它。

假设你有:

data class SomeStructure(
    val fieldA: UByte,
    val someValue: UShort,
    val anotherValue: UInt,
    val aByte: UByte,
)

你可以这样做:

val byteArray: ByteArray = ....
val buffer = ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN)
val someStruct = SomeStructure(
    buffer.get().toUByte(),
    buffer.getShort().toUShort(),
    buffer.getInt().toUInt(),
    buffer.get().toUByte(),
)
println(someStruct)

请注意,ByteBuffergetgetXXX 方法会推进“阅读”位置,因此会改变 ByteBuffer,因此如果您想 re-read 创建 SomeStructure 后出于任何原因再次创建缓冲区,你应该 flip 它,或者只是创建一个新的字节缓冲区。

您也可以将其设为 SomeStructure.

的辅助构造函数
constructor(buffer: ByteBuffer): this(
    buffer.get().toUByte(),
    buffer.getShort().toUShort(),
    buffer.getInt().toUInt(),
    buffer.get().toUByte(),
)

这样,您甚至可以支持从字节缓冲区中引用 SomeStructure 来读取数据 类:

data class SomeOtherStructure(
    val struct1: SomeStructure,
    val struct2: SomeStructure,
) {
    constructor(buffer: ByteBuffer): this(
        SomeStructure(buffer), // recall that this advances the current position of the buffer
        SomeStructure(buffer)
    )
}