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
13 changes: 8 additions & 5 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@
**조치:** `Set`과 같은 순서에 무관한 자료구조에 요소를 추가하기 위한 필터링 작업에서는 디렉토리 목록에서 `.sorted()` 호출을 제거하여 성능을 최적화합니다.

## 2024-05-18 - [디렉토리 목록 캐싱을 통한 I/O 오버헤드 최적화]
**Learning:** `process_dir` 및 `process_ignore_file`과 같은 함수에서 동일한 디렉토리에 대해 `listFiles()` 또는 `list()`를 반복적으로 호출하면, 파일 시스템 I/O로 인한 불필요한 성능 저하가 발생합니다.
**Action:** 디렉토리를 순회할 때 상위 루프에서 `listFiles()`를 한 번만 호출하여 캐싱한 후, 결과를 인자로 전달(예: `dirFiles` 배열)하여 중복된 파일 시스템 호출을 제거해야 합니다.
**학습:** `process_dir` 및 `process_ignore_file`과 같은 함수에서 동일한 디렉토리에 대해 `listFiles()` 또는 `list()`를 반복적으로 호출하면, 파일 시스템 I/O로 인한 불필요한 성능 저하가 발생합니다.
**조치:** 디렉토리를 순회할 때 상위 루프에서 `listFiles()`를 한 번만 호출하여 캐싱한 후, 결과를 인자로 전달(예: `dirFiles` 배열)하여 중복된 파일 시스템 호출을 제거해야 합니다.

## 2024-08-01 - URL 인코딩 빌더 지연 생성
**학습:** URL 인코딩이 필요 없는 안전한 경로 문자열에서도 항상 `StringBuilder`를 생성하면 hot path에서 불필요한 할당이 발생합니다.
**조치:** 예약 바이트를 처음 만났을 때만 `StringBuilder`를 만들고, 그 전까지는 원본 문자열을 그대로 반환하는 지연 생성 패턴을 사용합니다.
## $(date +%Y-%m-%d) - Optimize OS stat calls in file listing
**Learning:** Replaced three separate OS stat calls (`Files.isDirectory(it.toPath(), LinkOption.NOFOLLOW_LINKS)`, `!it.isDirectory()`, and `!Files.isSymbolicLink(it.toPath())`) with a single `Files.readAttributes` call. The original code caused significant I/O overhead. This reduces file metadata fetching time significantly.
**Action:** Always consider using `Files.readAttributes` to fetch multiple file attributes at once rather than calling separate boolean checks like `isDirectory` or `isSymbolicLink` on individual files when iterating directories.
## 2025-01-24 - OS stat 호출 최적화
**학습:** 3개의 개별 OS stat 호출을 단일 `Files.readAttributes` 호출로 교체했습니다. 원래 코드는 상당한 I/O 오버헤드를 유발했습니다. 이를 통해 파일 메타데이터를 가져오는 시간을 크게 줄였습니다.
**조치:** 디렉토리를 반복할 때 개별 파일에 대해 여러 파일 속성을 한 번에 가져오기 위해 항상 `Files.readAttributes`를 사용하는 것을 고려하세요.
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
**학습:** `isDirectory`, `!it.isDirectory()`, `isSymbolicLink` 3개의 개별적인 파일 시스템 I/O 호출을 수행하면 성능 저하가 큽니다. 이를 단일 `Files.readAttributes` 호출로 변경하여 메타데이터를 한 번에 조회함으로써 I/O 오버헤드를 대폭 줄일 수 있음을 확인했습니다.
**조치:** 디렉토리 순회 시 파일의 여러 속성을 확인할 때는 개별적인 stat 호출보다 `Files.readAttributes`를 사용하여 필요한 모든 속성을 한 번에 가져오는 방식을 우선적으로 고려해야 합니다.
## 2025-01-24 - 단일 readAttributes 호출로 파일 속성 조회 최적화
**학습:** `isDirectory`, `!it.isDirectory()`, `isSymbolicLink` 3개의 개별적인 파일 시스템 I/O 호출을 수행하면 성능 저하가 큽니다. 이를 단일 `Files.readAttributes` 호출로 변경하여 메타데이터를 한 번에 조회함으로써 I/O 오버헤드를 대폭 줄일 수 있음을 확인했습니다.
**조치:** 디렉토리 순회 시 파일의 여러 속성을 확인할 때는 개별적인 stat 호출보다 `Files.readAttributes`를 사용하여 필요한 모든 속성을 한 번에 가져오는 방식을 우선적으로 고려해야 합니다.
## 2024-08-11 - 정적 문자열 및 비용이 큰 암호화 연산의 최상위 레벨 추출
**학습:** 재귀적인 디렉토리 순회 알고리즘(예: `process_dir`) 내부에서 정적 문자열(CSS)을 할당하고 SHA-256 해시(`MessageDigest.getInstance("SHA-256")`)를 매번 계산하는 것은 불필요한 메모리 할당과 막대한 CPU 오버헤드를 유발합니다.
**조치:** 변경되지 않는 정적 문자열과 비용이 큰 연산 결과는 파일의 최상위 레벨(top-level property)로 추출하여 한 번만 계산되고 재사용되도록 최적화합니다.
15 changes: 8 additions & 7 deletions src/main/kotlin/html4tree/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,8 @@ fun write_index_file(curr_dir: File, content: String) {
}
}

fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array<File>? = null){

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

val cssContent = """
// ⚡ Bolt Performance Optimization: 디렉토리마다 정적 문자열을 할당하고 무거운 SHA-256 해시를 계산하는 것을 방지하기 위해 파일 최상위 레벨로 이동
val cssContent = """
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.5;
Expand Down Expand Up @@ -313,13 +310,17 @@ fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array
}
"""

val styleHash = "sha256-" + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(cssContent.toByteArray(Charsets.UTF_8)))
val styleHash = "sha256-" + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(cssContent.toByteArray(Charsets.UTF_8)))

val css = """
val css = """
<style>
${cssContent} </style>
"""
Comment on lines +313 to 318

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import base64
import hashlib

source = Path("src/main/kotlin/html4tree/main.kt").read_text(encoding="utf-8")

def raw_value(name: str) -> str:
    marker = f'val {name} = """'
    start = source.index(marker) + len(marker)
    end = source.index('"""', start)
    return source[start:end]

css_content = raw_value("cssContent")
css = raw_value("css")
style_body = css.split("<style>", 1)[1].split("</style>", 1)[0]

def csp_hash(value: str) -> str:
    digest = hashlib.sha256(value.encode("utf-8")).digest()
    return "sha256-" + base64.b64encode(digest).decode("ascii")

print("cssContent hash:", csp_hash(css_content))
print("style body hash:", csp_hash(style_body))

if style_body != css_content:
    raise SystemExit("style body and cssContent differ")
PY

Repository: ContextualWisdomLab/html4tree

Length of output: 338


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '285,335p' src/main/kotlin/html4tree/main.kt
printf '\n--- styleHash usages ---\n'
rg -n -C 3 'styleHash|cssContent|<style>' src/main/kotlin/html4tree/main.kt

Repository: ContextualWisdomLab/html4tree

Length of output: 3248


styleHash가 실제 <style> 본문을 해시하도록 수정하세요.

현재 두 값의 SHA-256 해시가 다릅니다. <style> 내부의 개행과 공백을 포함한 실제 본문을 해시하거나, <style>${cssContent}</style>로 구성하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/kotlin/html4tree/main.kt` around lines 313 - 318, Update the
styleHash calculation near css so it hashes the exact CSS text emitted inside
the <style> element, including its newline and whitespace formatting; either
construct the hashed value as the complete <style>${cssContent}</style> body or
otherwise reuse the same rendered style content to keep the hash and output
identical.


fun process_dir(curr_dir: File, excludeSet: Set<String>? = null, dirFiles: Array<File>? = null){

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

val index_top = """<!doctype html>
<html lang="ko">
<head>
Expand Down
7 changes: 7 additions & 0 deletions src/test/kotlin/html4tree/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -706,4 +706,11 @@ class MainTest {
assertFalse(processed, "fileKey mismatch should skip directory processing")
assertFalse(listed, "fileKey mismatch should skip child listing")
}

@Test
fun testTopLevelCssProperties() {
assertTrue(cssContent.contains("body {"))
assertTrue(styleHash.startsWith("sha256-"))
assertTrue(css.contains("<style>"))
}
}
Loading