From 2646bf2108fc5182ff6de97023c1b31ee4ef1cd5 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 4 Jun 2026 10:42:10 -0400 Subject: [PATCH 1/3] perf(note): stream excerpt instead of reading full note Signed-off-by: Josh --- lib/Service/Note.php | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/Service/Note.php b/lib/Service/Note.php index 86adfd3b9..0eb92f2a7 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -62,9 +62,9 @@ public function getContent() : string { } public function getExcerpt(int $maxlen = 100) : string { - $excerpt = trim($this->noteUtil->stripMarkdown($this->getContent())); + $excerpt = trim($this->noteUtil->stripMarkdown($this->getExcerptSource($maxlen))); $title = $this->getTitle(); - if (!empty($title)) { + if ($title !== '') { $length = mb_strlen($title, 'utf-8'); if (strncasecmp($excerpt, $title, $length) === 0) { $excerpt = mb_substr($excerpt, $length, null, 'utf-8'); @@ -77,6 +77,37 @@ public function getExcerpt(int $maxlen = 100) : string { return str_replace("\n", "\u{2003}", $excerpt); } + /** + * Lightweight best-effort content reader for excerpts only. + */ + private function getExcerptContent(int $maxlen) : string { + $handle = $this->file->read(); + if (!is_resource($handle)) { + return ''; + } + + // Over-read bytes assuming worst-case UTF-8 size (up to 4 bytes per + // character). This is only a heuristic for preview generation; markdown + // stripping may reduce the visible character count further. + $bytesToRead = max(512, $maxlen * 4); + + try { + $content = fread($handle, $bytesToRead); + if ($content === false) { + return ''; + } + } finally { + fclose($handle); + } + + // Remove any partial trailing multibyte character from the truncated read. + $content = mb_strcut($content, 0, strlen($content), 'UTF-8'); + + $content = str_replace([ pack('H*', 'FEFF'), pack('H*', 'FFEF'), pack('H*', 'EFBBBF') ], '', $content); + + return $content; + } + public function getModified() : int { return $this->file->getMTime(); } From a1827ea90046cfd483b629fe50a3420211e3a9f0 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 4 Jun 2026 11:16:50 -0400 Subject: [PATCH 2/3] perf: use pre-compiled string literal for BOM stripping Signed-off-by: Josh --- lib/Service/Note.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/Service/Note.php b/lib/Service/Note.php index 0eb92f2a7..b4ecb8d80 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -57,20 +57,26 @@ public function getContent() : string { ); $content = mb_convert_encoding($content, 'UTF-8'); } - $content = str_replace([ pack('H*', 'FEFF'), pack('H*', 'FFEF'), pack('H*', 'EFBBBF') ], '', $content); + + // Strip Byte Order Marks (BOM) for UTF-8, UTF-16 BE, and UTF-16 LE + $content = str_replace(["\xEF\xBB\xBF", "\xFE\xFF", "\xFF\xFE"], '', $content); + return $content; } public function getExcerpt(int $maxlen = 100) : string { - $excerpt = trim($this->noteUtil->stripMarkdown($this->getExcerptSource($maxlen))); + $excerpt = $this->noteUtil->stripMarkdown($this->getExcerptSource($maxlen)); + $title = $this->getTitle(); if ($title !== '') { - $length = mb_strlen($title, 'utf-8'); - if (strncasecmp($excerpt, $title, $length) === 0) { - $excerpt = mb_substr($excerpt, $length, null, 'utf-8'); + $titleLength = mb_strlen($title, 'utf-8'); + if (strncasecmp($excerpt, $title, $titleLength) === 0) { + $excerpt = mb_substr($excerpt, $titleLength, null, 'utf-8'); } } + $excerpt = trim($excerpt); + if (mb_strlen($excerpt, 'utf-8') > $maxlen) { $excerpt = mb_substr($excerpt, 0, $maxlen, 'utf-8') . '…'; } @@ -103,7 +109,8 @@ private function getExcerptContent(int $maxlen) : string { // Remove any partial trailing multibyte character from the truncated read. $content = mb_strcut($content, 0, strlen($content), 'UTF-8'); - $content = str_replace([ pack('H*', 'FEFF'), pack('H*', 'FFEF'), pack('H*', 'EFBBBF') ], '', $content); + // Strip Byte Order Marks (BOM) for UTF-8, UTF-16 BE, and UTF-16 LE + $content = str_replace(["\xEF\xBB\xBF", "\xFE\xFF", "\xFF\xFE"], '', $content); return $content; } From 93364d6bb42bd956c3e0588211b294bd4391b98f Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 4 Jun 2026 11:25:16 -0400 Subject: [PATCH 3/3] chore(Note): fixup (not using ISimpleFile oops) Signed-off-by: Josh --- lib/Service/Note.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Service/Note.php b/lib/Service/Note.php index b4ecb8d80..3f590af38 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -65,7 +65,7 @@ public function getContent() : string { } public function getExcerpt(int $maxlen = 100) : string { - $excerpt = $this->noteUtil->stripMarkdown($this->getExcerptSource($maxlen)); + $excerpt = $this->noteUtil->stripMarkdown($this->getExcerptContent($maxlen)); $title = $this->getTitle(); if ($title !== '') { @@ -87,7 +87,7 @@ public function getExcerpt(int $maxlen = 100) : string { * Lightweight best-effort content reader for excerpts only. */ private function getExcerptContent(int $maxlen) : string { - $handle = $this->file->read(); + $handle = $this->file->fopen('r'); if (!is_resource($handle)) { return ''; }