Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2024-06-21 - Regex Compilation in Loops
**Learning:** In Kotlin, compiling regular expressions (`.toRegex()`) inside a loop over files is a significant O(N * M) performance bottleneck when processing ignore files (N files * M rules).
**Action:** Always map string rules to compiled `Regex` objects outside of the file iteration loop (O(M) compilation) to avoid unnecessary regex re-compilations.
## 2024-06-25 - Collection Lookup Performance in Loops
**Learning:** Checking for file exclusion (`in exclude`) inside a loop iterating over directory contents can be a significant bottleneck (O(N) lookup for each file) if the `exclude` collection is a `List`.
**Action:** Always prefer using `Set` (e.g., `mutableSetOf()`) over `List` for collections used primarily for lookup/containment checks inside loops, changing the lookup from O(N) to O(1).
16 changes: 8 additions & 8 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,20 @@ fun String.urlEncodePath(): String {
return java.net.URLEncoder.encode(this, "UTF-8").replace("+", "%20")
}

fun process_ignore_file(curr_dir: File): List<String> {
fun process_ignore_file(curr_dir: File): Set<String> {

val ignore_filename = ".html4ignore"

val ignore_file_path = curr_dir.getAbsolutePath()+"/"+ignore_filename

val ignore_file = File(ignore_file_path)

val files_to_exclude = mutableListOf<String>()
val files_to_exclude = mutableSetOf<String>()

if(ignore_file.exists()){
val ignored_regexes = mutableListOf<Regex>()

// O(M) regex compilation outside of N files loop
ignore_file.forEachLine { ignored_regexes.add(("^"+it+"$").toRegex()) }

curr_dir.list().sorted().forEach {
Expand All @@ -79,15 +80,14 @@ fun process_ignore_file(curr_dir: File): List<String> {
}
}

if ("index.html" !in files_to_exclude)
files_to_exclude.add("index.html")
files_to_exclude.add("index.html") // Sets handle duplicates automatically, but no harm ensuring it's there

return files_to_exclude
}

fun process_dir(curr_dir: File){

val exclude: List<String> = process_ignore_file(curr_dir)
val exclude: Set<String> = process_ignore_file(curr_dir)

val css = """
<style>
Expand Down Expand Up @@ -126,7 +126,7 @@ fun process_dir(curr_dir: File){
"""

val index_middle = fun():String{
var l=""
val l = java.lang.StringBuilder()

val dir_files: MutableList<File> = curr_dir.listFiles()?.toMutableList() ?: mutableListOf()
dir_files.sortWith(compareBy ({it.name}) )
Expand All @@ -136,11 +136,11 @@ fun process_dir(curr_dir: File){
val fileName = it.getName()
val encodedHref = if (isLinkedDirectory) { "./${fileName.urlEncodePath()}/" } else { "./${fileName.urlEncodePath()}" }
val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml()
l += """ <li><a style="display:block; width:100%" href="${encodedHref}" aria-label="${ariaLabel}">${if (isLinkedDirectory) { "&#128193;" } else { "&rtrif;" }} ${fileName.escapeHtml()}</a></li>"""+"\n"
l.append(""" <li><a style="display:block; width:100%" href="${encodedHref}" aria-label="${ariaLabel}">${if (isLinkedDirectory) { "&#128193;" } else { "&rtrif;" }} ${fileName.escapeHtml()}</a></li>"""+"\n")
}
}

return l;
return l.toString();
}

val index_bottom="""
Expand Down
Loading