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
库及其 pack
和 unpack
函数是 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)
请注意,ByteBuffer
的 get
和 getXXX
方法会推进“阅读”位置,因此会改变 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)
)
}
对于基于 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
库及其 pack
和 unpack
函数是 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)
请注意,ByteBuffer
的 get
和 getXXX
方法会推进“阅读”位置,因此会改变 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)
)
}