From db6ceecfcdc337e04fe1ecb437b806e1700b8e3b 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 e50487f71..bdfcc0b79 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -61,9 +61,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'); @@ -76,6 +76,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 957ec081ae396918f991147e6e02f5ea95cebec7 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 bdfcc0b79..ba0bd511d 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -56,20 +56,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') . '…'; } @@ -102,7 +108,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 9a9f8b13ce293488222dad8811c852e708918fe7 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 ba0bd511d..ce33aa4c6 100644 --- a/lib/Service/Note.php +++ b/lib/Service/Note.php @@ -64,7 +64,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 !== '') { @@ -86,7 +86,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 ''; }