mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-11-10 14:47:24 +01:00
30% Faster isHex for strings with 64 bytes.
This commit is contained in:
@@ -100,17 +100,7 @@ class HexBenchmark {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun newIsHex() {
|
||||
val isHexChar =
|
||||
BooleanArray(256).apply {
|
||||
"0123456789abcdefABCDEF".forEach { this[it.code] = true }
|
||||
}
|
||||
|
||||
r.measureRepeated {
|
||||
for (c in hex.indices) {
|
||||
if (!isHexChar[hex[c].code]) return@measureRepeated
|
||||
}
|
||||
return@measureRepeated
|
||||
}
|
||||
fun isHex64() {
|
||||
r.measureRepeated { Hex.isHex64(hex) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,17 +83,50 @@ class HexEncodingTest {
|
||||
assertFalse("`a", Hex.isHex("`a"))
|
||||
assertFalse("gg", Hex.isHex("gg"))
|
||||
assertFalse("fg", Hex.isHex("fg"))
|
||||
assertFalse("\uD83E\uDD70", Hex.isHex("\uD83E\uDD70"))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@Test
|
||||
fun testRandomsIsHex() {
|
||||
val lowerCaseHexNeg = "ghijklmnopqrstuvwxyz"
|
||||
val upperCaseHexNeg = "GHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
for (i in 0..10000) {
|
||||
val bytes = RandomInstance.bytes(32)
|
||||
val hex = bytes.toHexString(HexFormat.Default)
|
||||
assertTrue(hex, Hex.isHex(hex))
|
||||
val hexUpper = bytes.toHexString(HexFormat.UpperCase)
|
||||
assertTrue(hexUpper, Hex.isHex(hexUpper))
|
||||
|
||||
// scramble
|
||||
val negHex = hex.replaceFirst(hex.random(), lowerCaseHexNeg.random())
|
||||
val negHexUpper = hexUpper.replaceFirst(hexUpper.random(), upperCaseHexNeg.random())
|
||||
|
||||
assertFalse(negHex, Hex.isHex(negHex))
|
||||
assertFalse(negHexUpper, Hex.isHex(negHexUpper))
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@Test
|
||||
fun testRandomsIsHex64() {
|
||||
val lowerCaseHexNeg = "ghijklmnopqrstuvwxyz"
|
||||
val upperCaseHexNeg = "GHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
for (i in 0..10000) {
|
||||
val bytes = RandomInstance.bytes(32)
|
||||
val hex = bytes.toHexString(HexFormat.Default)
|
||||
assertTrue(hex, Hex.isHex64(hex))
|
||||
val hexUpper = bytes.toHexString(HexFormat.UpperCase)
|
||||
assertTrue(hexUpper, Hex.isHex64(hexUpper))
|
||||
|
||||
// scramble
|
||||
val negHex = hex.replaceFirst(hex.random(), lowerCaseHexNeg.random())
|
||||
val negHexUpper = hexUpper.replaceFirst(hexUpper.random(), upperCaseHexNeg.random())
|
||||
|
||||
assertFalse(hex + ":" + negHex, Hex.isHex64(negHex))
|
||||
assertFalse(hexUpper + ":" + negHexUpper, Hex.isHex64(negHexUpper))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,23 +36,114 @@ object Hex {
|
||||
(LOWER_CASE_HEX[(it shr 4)].code shl 8) or LOWER_CASE_HEX[(it and 0xF)].code
|
||||
}
|
||||
|
||||
// 47ns in debug on the Emulator
|
||||
fun isHex(hex: String?): Boolean {
|
||||
if (hex.isNullOrEmpty()) return false
|
||||
if (hex == null) return false
|
||||
if (hex.length and 1 != 0) return false
|
||||
|
||||
try {
|
||||
for (c in hex.indices) {
|
||||
if (c < 0 || c > 255) return false
|
||||
if (hexToByte[hex[c].code] < 0) return false
|
||||
}
|
||||
return try {
|
||||
internalIsHex(hex, hexToByte)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// there are p tags with emoji's which makes the hex[c].code > 256
|
||||
return false
|
||||
false
|
||||
} catch (_: IndexOutOfBoundsException) {
|
||||
// there are p tags with emoji's which makes the hex[c].code > 256
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// breaking this function away from the main one improves performance for some reason
|
||||
fun internalIsHex(
|
||||
hex: String,
|
||||
hexToByte: IntArray,
|
||||
): Boolean {
|
||||
for (c in hex.indices) {
|
||||
if (hexToByte[hex[c].code] < 0) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 30% faster than isHex
|
||||
fun isHex64(hex: String): Boolean =
|
||||
try {
|
||||
hexToByte[hex[0].code] >= 0 &&
|
||||
hexToByte[hex[1].code] >= 0 &&
|
||||
hexToByte[hex[2].code] >= 0 &&
|
||||
hexToByte[hex[3].code] >= 0 &&
|
||||
hexToByte[hex[4].code] >= 0 &&
|
||||
hexToByte[hex[5].code] >= 0 &&
|
||||
hexToByte[hex[6].code] >= 0 &&
|
||||
hexToByte[hex[7].code] >= 0 &&
|
||||
hexToByte[hex[8].code] >= 0 &&
|
||||
hexToByte[hex[9].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[10].code] >= 0 &&
|
||||
hexToByte[hex[11].code] >= 0 &&
|
||||
hexToByte[hex[12].code] >= 0 &&
|
||||
hexToByte[hex[13].code] >= 0 &&
|
||||
hexToByte[hex[14].code] >= 0 &&
|
||||
hexToByte[hex[15].code] >= 0 &&
|
||||
hexToByte[hex[16].code] >= 0 &&
|
||||
hexToByte[hex[17].code] >= 0 &&
|
||||
hexToByte[hex[18].code] >= 0 &&
|
||||
hexToByte[hex[19].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[20].code] >= 0 &&
|
||||
hexToByte[hex[21].code] >= 0 &&
|
||||
hexToByte[hex[22].code] >= 0 &&
|
||||
hexToByte[hex[23].code] >= 0 &&
|
||||
hexToByte[hex[24].code] >= 0 &&
|
||||
hexToByte[hex[25].code] >= 0 &&
|
||||
hexToByte[hex[26].code] >= 0 &&
|
||||
hexToByte[hex[27].code] >= 0 &&
|
||||
hexToByte[hex[28].code] >= 0 &&
|
||||
hexToByte[hex[29].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[30].code] >= 0 &&
|
||||
hexToByte[hex[31].code] >= 0 &&
|
||||
hexToByte[hex[32].code] >= 0 &&
|
||||
hexToByte[hex[33].code] >= 0 &&
|
||||
hexToByte[hex[34].code] >= 0 &&
|
||||
hexToByte[hex[35].code] >= 0 &&
|
||||
hexToByte[hex[36].code] >= 0 &&
|
||||
hexToByte[hex[37].code] >= 0 &&
|
||||
hexToByte[hex[38].code] >= 0 &&
|
||||
hexToByte[hex[39].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[40].code] >= 0 &&
|
||||
hexToByte[hex[41].code] >= 0 &&
|
||||
hexToByte[hex[42].code] >= 0 &&
|
||||
hexToByte[hex[43].code] >= 0 &&
|
||||
hexToByte[hex[44].code] >= 0 &&
|
||||
hexToByte[hex[45].code] >= 0 &&
|
||||
hexToByte[hex[46].code] >= 0 &&
|
||||
hexToByte[hex[47].code] >= 0 &&
|
||||
hexToByte[hex[48].code] >= 0 &&
|
||||
hexToByte[hex[49].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[50].code] >= 0 &&
|
||||
hexToByte[hex[51].code] >= 0 &&
|
||||
hexToByte[hex[52].code] >= 0 &&
|
||||
hexToByte[hex[53].code] >= 0 &&
|
||||
hexToByte[hex[54].code] >= 0 &&
|
||||
hexToByte[hex[55].code] >= 0 &&
|
||||
hexToByte[hex[56].code] >= 0 &&
|
||||
hexToByte[hex[57].code] >= 0 &&
|
||||
hexToByte[hex[58].code] >= 0 &&
|
||||
hexToByte[hex[59].code] >= 0 &&
|
||||
|
||||
hexToByte[hex[60].code] >= 0 &&
|
||||
hexToByte[hex[61].code] >= 0 &&
|
||||
hexToByte[hex[62].code] >= 0 &&
|
||||
hexToByte[hex[63].code] >= 0
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// there are p tags with emoji's which makes the hex[c].code > 256
|
||||
false
|
||||
} catch (_: IndexOutOfBoundsException) {
|
||||
// there are p tags with emoji's which makes the hex[c].code > 256
|
||||
false
|
||||
}
|
||||
|
||||
fun decode(hex: String): ByteArray {
|
||||
// faster version of hex decoder
|
||||
require(hex.length and 1 == 0)
|
||||
|
||||
Reference in New Issue
Block a user