diff --git a/.jules/bolt.md b/.jules/bolt.md index 83cc604..e8b1eb0 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -5,3 +5,7 @@ ## 2024-05-24 - Loop Allocation Hot Paths **Learning:** Rendering directory entries with repeated string concatenation and list-based exclusion lookups creates avoidable allocation and lookup cost in large directories. **Action:** Use `StringBuilder` for entry rendering and a `Set` for excluded file names. + +## 2024-07-25 - Avoid Chained String Replacements +**Learning:** In Kotlin, using multiple chained `.replace()` calls on a String for frequent operations (like HTML escaping) is inefficient due to multiple intermediate allocations. +**Action:** Use a single-pass loop with a lazily-initialized `StringBuilder` to prevent intermediate string allocations, significantly improving performance on hot paths. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 2e2809f..cd6527f 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -48,13 +48,33 @@ fun go(topDir: String, maxLevel: Int) { } } +// ⚡ Bolt Optimization: Replace chained .replace() with single-pass StringBuilder +// Reduces intermediate allocations and improves performance when escaping strings. fun String.escapeHtml(): String { - return this.replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\"", """) - .replace("'", "'") - .replace("`", "`") + var sb: StringBuilder? = null + for (i in this.indices) { + val char = this[i] + val replacement = when (char) { + '&' -> "&" + '<' -> "<" + '>' -> ">" + '"' -> """ + '\'' -> "'" + '`' -> "`" + else -> null + } + + if (replacement != null) { + if (sb == null) { + sb = StringBuilder(this.length + 16) + sb.append(this, 0, i) + } + sb.append(replacement) + } else { + sb?.append(char) + } + } + return sb?.toString() ?: this } fun String.urlEncodePath(): String { diff --git a/src/test/kotlin/html4tree/MainTest.kt b/src/test/kotlin/html4tree/MainTest.kt index e8a3082..ab73b1a 100644 --- a/src/test/kotlin/html4tree/MainTest.kt +++ b/src/test/kotlin/html4tree/MainTest.kt @@ -39,6 +39,8 @@ class MainTest { assertEquals("`", "`".escapeHtml()) assertEquals("&<>"'`", "&<>\"'`".escapeHtml()) assertEquals("normal text", "normal text".escapeHtml()) + assertEquals("a&b", "a&b".escapeHtml()) + assertEquals("hello & world", "hello & world".escapeHtml()) } @Test