Skip to content

Commit

Permalink
Optimize Uri.percentDecode. (#2149)
Browse files Browse the repository at this point in the history
* Optimize Uri.percentDecode.

* Tweak test case.
  • Loading branch information
colinrtwhite authored Feb 28, 2024
1 parent ab0128f commit 3167126
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 19 deletions.
43 changes: 24 additions & 19 deletions coil-core/src/commonMain/kotlin/coil3/Uri.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,19 @@ private fun parseUri(data: String): Uri {
fragment = data.substring(fragmentStartIndex, data.length)
}

val size = maxOf(
scheme.length,
authority.length,
val maxLength = maxOf(
0,
maxOf(
path.length,
query.length,
fragment.length,
),
scheme.length,
authority.length,
maxOf(
path.length,
query.length,
fragment.length,
),
) - 2,
)
val bytes = ByteArray(size)
val bytes = ByteArray(maxLength)
return Uri(
data = data,
scheme = scheme?.percentDecode(bytes),
Expand All @@ -161,9 +164,19 @@ private fun parseUri(data: String): Uri {
private fun String.percentDecode(bytes: ByteArray): String {
var size = 0
var index = 0

while (index < length) {
if (get(index) == '%' && index + 2 < length) {
val length = length
val searchLength = maxOf(0, length - 2)

while (true) {
if (index >= searchLength) {
if (index == size) {
// Fast path: the string doesn't have any encoded characters.
return this
} else if (index >= length) {
// Slow path: decode the byte array.
return bytes.decodeToString(endIndex = size)
}
} else if (get(index) == '%') {
try {
val hex = substring(index + 1, index + 3)
bytes[size] = hex.toInt(16).toByte()
Expand All @@ -177,14 +190,6 @@ private fun String.percentDecode(bytes: ByteArray): String {
size++
index++
}

if (size == length) {
// Fast path: the string doesn't have any encoded characters.
return this
} else {
// Slow path: decode the byte array.
return bytes.decodeToString(endIndex = size)
}
}

private val String?.length: Int
Expand Down
13 changes: 13 additions & 0 deletions coil-core/src/commonTest/kotlin/coil3/UriTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ class UriTest {
assertEquals(string, uri.toString())
}

@Test
fun encodedSingle() {
val string = "https://example.com/something%20"
val uri = string.toUri()
assertEquals("https", uri.scheme)
assertEquals("example.com", uri.authority)
assertEquals("/something ", uri.path)
assertEquals(listOf("something "), uri.pathSegments)
assertNull(uri.query)
assertNull(uri.fragment)
assertEquals(string, uri.toString())
}

@Test
fun encodedMalformed() {
val string = "https://example.com/%E4%B8%8A%E6%B5%B7%2B%E4%B8%AD%E5%9C%8B%"
Expand Down

0 comments on commit 3167126

Please # to comment.