Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-02 - Chained String Replaces
**Learning:** Using multiple chained `.replace()` calls on a Kotlin String (e.g., for HTML escaping) allocates new intermediate strings and arrays for every replacement pass, causing significant GC pressure and CPU overhead when called frequently on long lists.
**Action:** Replace chained `.replace()` calls with a single-pass loop over the string characters. Use a lazily-initialized `StringBuilder` to append escaped values and original characters, avoiding allocations completely on the fast path (when no escaping is needed).
31 changes: 24 additions & 7 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fun main(args: Array<String>) = Html4tree().main(args)

fun go(topDir: String, maxLevel: Int) {
require(topDir.isNotBlank())
val top_dir = File(topDir).canonicalFile
val top_dir = File(topDir).toPath().toAbsolutePath().normalize().toFile()
require(Files.isDirectory(top_dir.toPath(), LinkOption.NOFOLLOW_LINKS)) { "Top directory must be an existing non-symlink directory" }

val ll = LinkedList()
Expand All @@ -49,12 +49,29 @@ fun go(topDir: String, maxLevel: Int) {
}

fun String.escapeHtml(): String {
return this.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
.replace("'", "&#x27;")
.replace("`", "&#x60;")
var encoded: StringBuilder? = null
for (i in 0 until this.length) {
val c = this[i]
val replacement = when (c) {
'&' -> "&amp;"
'<' -> "&lt;"
'>' -> "&gt;"
'"' -> "&quot;"
'\'' -> "&#x27;"
'`' -> "&#x60;"
else -> null
}
if (replacement != null) {
if (encoded == null) {
encoded = StringBuilder(this.length + 16)
encoded.append(this, 0, i)
}
encoded.append(replacement)
} else {
encoded?.append(c)
}
}
return encoded?.toString() ?: this
}

fun String.urlEncodePath(): String {
Expand Down