diff --git a/.jules/bolt.md b/.jules/bolt.md index 39f32f6e..3340bbeb 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ ## 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-28 - String Concatenation in Loops +**Learning:** In `html4tree`, using `+=` string concatenation inside a loop over directory files causes unnecessary intermediate object creation and memory overhead, resulting in O(N^2) complexity. The Jacoco plugin configuration treats the implicit null check on `.listFiles()?.toMutableList()` as a partially missed branch, causing test coverage issues. +**Action:** Use `StringBuilder` for loop string building (O(N)), and split `curr_dir.listFiles()` into a clear `if (files != null)` check to satisfy Jacoco branch coverage to maintain 100%. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 50b2680d..8f58bc43 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -124,18 +124,20 @@ fun process_dir(curr_dir: File){ """ val index_middle = fun():String{ - var l="" + // 성능 향상: 반복문 내 문자열 연결(concatenation)을 StringBuilder로 변경하여 메모리 할당 및 복사 오버헤드 감소 (O(N^2) -> O(N)) + val sb = StringBuilder() - val dir_files: MutableList = curr_dir.listFiles()?.toMutableList() ?: mutableListOf() + val files = curr_dir.listFiles() + val dir_files: MutableList = if (files != null) files.toMutableList() else mutableListOf() dir_files.sortWith(compareBy ({it.name}) ) dir_files.forEach { val isLinkedDirectory = it.isDirectory() && !java.nio.file.Files.isSymbolicLink(it.toPath()) if((it.getName() !in exclude) && (isLinkedDirectory || !it.isDirectory())) { - l += """
  • ${if (isLinkedDirectory) { "📁" } else { "▸" }} ${it.getName().escapeHtml()}
  • """+"\n" + sb.append("""
  • ${if (isLinkedDirectory) { "📁" } else { "▸" }} ${it.getName().escapeHtml()}
  • """).append("\n") } } - return l; + return sb.toString() } val index_bottom=""" diff --git a/src/test/kotlin/html4tree/MainTest2.kt b/src/test/kotlin/html4tree/MainTest2.kt new file mode 100644 index 00000000..6cb016ec --- /dev/null +++ b/src/test/kotlin/html4tree/MainTest2.kt @@ -0,0 +1,40 @@ +package html4tree + +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.io.File +import java.nio.file.Files +import kotlin.test.assertTrue + +class MainTest2 { + private lateinit var tempDir: File + + @Before + fun setup() { + tempDir = Files.createTempDirectory("html4tree-test2-").toFile() + } + + @After + fun teardown() { + if (tempDir.exists()) { + tempDir.deleteRecursively() + } + } + + @Test + fun testProcessDirListFilesNull() { + val unreadableDir = File(tempDir, "unreadable2") + unreadableDir.mkdir() + unreadableDir.setReadable(false, false) + try { + process_dir(unreadableDir) + val indexFile = File(unreadableDir, "index.html") + assertTrue(indexFile.exists()) + } finally { + unreadableDir.setReadable(true, false) + unreadableDir.setWritable(true, false) + unreadableDir.setExecutable(true, false) + } + } +}