diff --git a/.jules/bolt.md b/.jules/bolt.md index 4f20b36047..7334b8d90c 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -54,3 +54,6 @@ ## 2026-09-01 - 대용량 문자열 서브스트링 스캐닝 루프 최적화 **Learning:** 긴 텍스트에서 여러 기준 문자열(`candidate`)을 탐색하여 다음 구역의 시작점을 찾을 때, 텍스트 전체에 대해 반복적으로 `text.find(candidate)`를 호출하면 O(N)의 비효율적인 중복 스캐닝 오버헤드가 발생합니다. 특히 가장 가까운 시작점을 찾기 위해 모든 후보를 스캔할 때 이 문제가 심화됩니다. **Action:** 기준점(`start`)을 잡은 후, `idx = text.find(candidate, start, end)`를 사용하여 검색 범위를 동적으로 축소(`end = min(end, idx)`)하십시오. 이렇게 하면 불필요한 스캐닝 오버헤드를 막고 검색 범위를 안전하게 줄여 매우 큰 성능 향상을 얻을 수 있습니다. +## 2024-11-23 - Python 파싱 로직에서 개별 문자 루프 추가(O(N^2)) 회피 +**Learning:** Python 문자열 파싱 시, 선형 루프 안에서 문자 하나씩 `output.append(text[i])`를 호출하면 함수 호출 오버헤드와 함께 큰 병목이 발생할 수 있습니다. 특히 입력 텍스트가 매우 길어지면 메모리 할당 및 리스트 확장 비용이 O(N^2)에 가까운 영향을 줄 수 있습니다. +**Action:** 긴 문자열을 파싱할 때 `while` 루프 등에서 매치되지 않은 텍스트를 처리하려면, 인덱스를 이용해 탐색(scan ahead)한 뒤 스트링 슬라이싱(`output.append(text[start:cursor])`)을 통해 일괄적으로 매치되지 않은 부분 전체를 잘라내어 리스트에 추가하십시오. diff --git a/rewrite.py b/rewrite.py new file mode 100644 index 0000000000..85c38f4442 --- /dev/null +++ b/rewrite.py @@ -0,0 +1,63 @@ +with open('scripts/ci/assert_opencode_reasoning_effort.py', 'r') as f: + text = f.read() + +new_text = """def strip_jsonc_comments(text: str) -> str: + \"\"\"Return ``text`` with ``//`` and ``/* */`` comments removed outside strings. + + ``opencode.jsonc`` is genuinely JSONC (it carries explanatory ``//`` notes, + e.g. above the ``contextual-orchestrator`` provider block), so a plain + :func:`json.loads` rejects it. Comment markers are only recognized outside + JSON string literals, so a string value that itself contains ``//`` (the + ``"$schema": "https://opencode.ai/config.json"`` line) is preserved + unchanged. Newlines inside removed content are kept so any remaining + ``json.JSONDecodeError`` still reports an accurate line number. + \"\"\" + result: list[str] = [] + in_string = False + index = 0 + length = len(text) + last_append = 0 + while index < length: + char = text[index] + if in_string: + if char == "\\\\" and index + 1 < length: + index += 2 + continue + if char == '"': + in_string = False + index += 1 + continue + if char == '"': + in_string = True + index += 1 + continue + if char == "/" and index + 1 < length and text[index + 1] == "/": + result.append(text[last_append:index]) + index += 2 + while index < length and text[index] not in "\\r\\n": + index += 1 + last_append = index + continue + if char == "/" and index + 1 < length and text[index + 1] == "*": + result.append(text[last_append:index]) + index += 2 + while index + 1 < length and not ( + text[index] == "*" and text[index + 1] == "/" + ): + if text[index] in "\\r\\n": + result.append(text[index]) + index += 1 + index += 2 + last_append = index + continue + index += 1 + result.append(text[last_append:]) + return "".join(result)""" + +old_func = text.split("def strip_jsonc_comments")[1].split("def load_config")[0] +old_func = "def strip_jsonc_comments" + old_func + +text = text.replace(old_func, new_text + "\n\n\n") + +with open('scripts/ci/assert_opencode_reasoning_effort.py', 'w') as f: + f.write(text) diff --git a/scripts/ci/assert_opencode_reasoning_effort.py b/scripts/ci/assert_opencode_reasoning_effort.py index 82079d5112..400af66856 100644 --- a/scripts/ci/assert_opencode_reasoning_effort.py +++ b/scripts/ci/assert_opencode_reasoning_effort.py @@ -35,12 +35,11 @@ def strip_jsonc_comments(text: str) -> str: in_string = False index = 0 length = len(text) + last_append = 0 while index < length: char = text[index] if in_string: - result.append(char) if char == "\\" and index + 1 < length: - result.append(text[index + 1]) index += 2 continue if char == '"': @@ -49,15 +48,17 @@ def strip_jsonc_comments(text: str) -> str: continue if char == '"': in_string = True - result.append(char) index += 1 continue if char == "/" and index + 1 < length and text[index + 1] == "/": + result.append(text[last_append:index]) index += 2 while index < length and text[index] not in "\r\n": index += 1 + last_append = index continue if char == "/" and index + 1 < length and text[index + 1] == "*": + result.append(text[last_append:index]) index += 2 while index + 1 < length and not ( text[index] == "*" and text[index + 1] == "/" @@ -66,9 +67,10 @@ def strip_jsonc_comments(text: str) -> str: result.append(text[index]) index += 1 index += 2 + last_append = index continue - result.append(char) index += 1 + result.append(text[last_append:]) return "".join(result)