Merge 3754d416d2ce070a1b6164b4626c0b2914323bb2 into c127eb83ab94c069c32d37530d2faecd381cd2a8

This commit is contained in:
Taksh Kothari 2026-09-14 09:43:25 +05:30 committed by GitHub
commit afb2f16b86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 86 additions and 9 deletions

View File

@ -19,16 +19,28 @@ fun ByteArray.hexEncodedString(): String {
}
fun String.dataFromHexString(): ByteArray? {
val len = this.length / 2
val data = ByteArray(len)
var index = 0
for (i in 0 until len) {
val hexByte = this.substring(i * 2, i * 2 + 2)
val byte = hexByte.toIntOrNull(16)?.toByte() ?: return null
data[index++] = byte
// Match iOS Data(hexString:): trim, optional 0x/0X prefix, reject odd length.
var hex = this.trim()
if (hex.startsWith("0x", ignoreCase = true)) {
hex = hex.substring(2)
}
if (hex.isEmpty()) {
return ByteArray(0)
}
// Radix parsing accepts signs and Unicode digits; encoded bytes require ASCII hex.
if (hex.length % 2 != 0 || hex.any { it !in '0'..'9' && it !in 'a'..'f' && it !in 'A'..'F' }) {
return null
}
val len = hex.length / 2
val data = ByteArray(len)
for (i in 0 until len) {
val hexByte = hex.substring(i * 2, i * 2 + 2)
val byte = hexByte.toIntOrNull(16)?.toByte() ?: return null
data[i] = byte
}
return data
}

View File

@ -0,0 +1,65 @@
package com.bitchat.android.util
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class HexStringTest {
@Test
fun `decodes even-length lowercase hex`() {
assertArrayEquals(
byteArrayOf(0x0a, 0xbc.toByte()),
"0abc".dataFromHexString()
)
}
@Test
fun `rejects odd-length hex instead of truncating`() {
// Previously length/2 silently dropped the trailing nibble.
assertNull("abc".dataFromHexString())
assertNull("0ab".dataFromHexString())
}
@Test
fun `strips optional 0x prefix like iOS`() {
assertArrayEquals(
byteArrayOf(0xde.toByte(), 0xad.toByte()),
"0xdead".dataFromHexString()
)
assertArrayEquals(
byteArrayOf(0xbe.toByte(), 0xef.toByte()),
"0Xbeef".dataFromHexString()
)
}
@Test
fun `empty string and empty after prefix yield empty array`() {
assertArrayEquals(ByteArray(0), "".dataFromHexString())
assertArrayEquals(ByteArray(0), "0x".dataFromHexString())
assertTrue(" ".dataFromHexString()!!.isEmpty())
}
@Test
fun `rejects non-hex characters`() {
assertNull("zz".dataFromHexString())
assertNull("0xgg".dataFromHexString())
}
@Test
fun `rejects signed byte chunks and non-ascii digits`() {
for (invalid in listOf("0x-1", "0x+1", "-1", "+1", "00-1", "0X+100", "01")) {
assertNull(invalid.dataFromHexString())
}
}
@Test
fun `round-trips with hexEncodedString`() {
val original = byteArrayOf(0x00, 0x7f, 0xff.toByte())
val encoded = original.hexEncodedString()
assertEquals("007fff", encoded)
assertArrayEquals(original, encoded.dataFromHexString())
}
}