From fc7ffc0f45a11f9e3938902678321fb42ccbb280 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 3 Aug 2026 14:13:49 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20IPC=20=EB=B8=8C=EB=A6=AC=EC=A7=80=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=ED=8A=B8=20=EB=B0=B0=EC=97=B4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=9C=20for-loop=20early=20exit=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=84=B1=EB=8A=A5=20=ED=96=A5?= =?UTF-8?q?=EC=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 5 +++++ .../src/features/score/scoreStorage.test.ts | 14 ++++++++++++++ apps/desktop/src/features/score/scoreStorage.ts | 13 +++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index d54cf10fc..e9018ad96 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -61,3 +61,8 @@ ## 2026-07-13 - Array.from mapping optimization **Learning:** Using `Array.from({ length: N }).map(...)` creates an intermediate array of `undefined` values which requires memory allocation and garbage collection, adding O(N) unnecessary overhead in frequently re-rendered UI components. **Action:** Use `Array.from({ length: N }, (_, index) => ...)` to map elements directly during array creation, avoiding intermediate allocations. + +## 2026-07-20 - Avoid Array.every() for large byte array validation + +**Learning:** Validating large payloads over the IPC bridge with `.every()` creates significant O(N) intermediate callback allocations and Garbage Collection overhead on the critical path. +**Action:** Replace `.every()` with a standard `for...of` loop with an early `break` for O(1) memory and substantially faster checks for malformed payload chunks. diff --git a/apps/desktop/src/features/score/scoreStorage.test.ts b/apps/desktop/src/features/score/scoreStorage.test.ts index 0feec199e..123c654eb 100644 --- a/apps/desktop/src/features/score/scoreStorage.test.ts +++ b/apps/desktop/src/features/score/scoreStorage.test.ts @@ -32,4 +32,18 @@ describe("scoreStorage bridge resolution", () => { BRIDGE_UNAVAILABLE_MESSAGE ); }); + + it("fails when the response array contains non-number elements", async () => { + const tauriWindow = window as TauriWindow; + tauriWindow.__TAURI_INVOKE__ = async (command: string) => { + if (command === "read_score_pdf") { + return [1, 2, "not a number", 4]; + } + return null; + }; + + await expect(readScorePdf("project-1", "score-1")).rejects.toThrow( + "Invalid score bridge response" + ); + }); }); diff --git a/apps/desktop/src/features/score/scoreStorage.ts b/apps/desktop/src/features/score/scoreStorage.ts index 492f12591..c5feba9a8 100644 --- a/apps/desktop/src/features/score/scoreStorage.ts +++ b/apps/desktop/src/features/score/scoreStorage.ts @@ -91,8 +91,17 @@ export async function readScorePdf(projectId: string, scoreId: string): Promise< if (response instanceof ArrayBuffer) { return new Uint8Array(response); } - if (Array.isArray(response) && response.every((byte) => typeof byte === "number")) { - return Uint8Array.from(response as number[]); + if (Array.isArray(response)) { + let isValid = true; + for (const byte of response) { + if (typeof byte !== "number") { + isValid = false; + break; + } + } + if (isValid) { + return Uint8Array.from(response as number[]); + } } throw new Error(INVALID_RESPONSE_MESSAGE);