Gemini: count the App's per-line tables in the page budget

Found in M3 while checking floors with the radio on: a windowed page left
1.5 to 3 KB less than the 40 KB steady floor (Q86), radio or not. The
budget counted TextBuffer's index (8 B/line) but not the App's tables,
which also grew by doubling. Now 16 B/line, and the App sizes them
exactly. The FAQ (1051 lines, windowed): 38.5 -> 39.6 KB after, radio
asleep; 37.1 -> 39.0 KB with the radio listening.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-05 19:19:52 +02:00
co-authored by Claude Opus 5.5
parent a0e3868934
commit 30a827070b
3 changed files with 14 additions and 6 deletions
+4 -4
View File
@@ -192,7 +192,7 @@ bool GeminiService::loadWindow(const GeminiPage& page, size_t firstLine) {
windowPage_.lineIndex = page.lineIndex;
windowLine_ = firstLine;
// The App drops its current window once the new one is in: count that memory as coming back.
windowReleasing_ = page.text.bytes() + page.text.lineCount() * 8;
windowReleasing_ = page.text.bytes() + page.text.lineCount() * kBytesPerLine;
return start(Job::Window);
}
@@ -618,7 +618,7 @@ void GeminiService::fetchOne(const std::string& url, GeminiPage& page, bool load
// what was free before minus the steady floor; meanwhile the heap must stay above the
// transient floor. Both counted with the next 4 KB chunk and the line index.
size_t take = std::min<size_t>(n - i, kMaxBody - page.text.bytes());
size_t pageCost = page.text.bytes() + page.text.lineCount() * 8 + TextBuffer::kChunk + take;
size_t pageCost = page.text.bytes() + page.text.lineCount() * kBytesPerLine + TextBuffer::kChunk + take;
if (take < static_cast<size_t>(n - i)) page.truncatedWhy = "longer than 64 KB";
else if (freeBefore < kSteadyFloor + pageCost) page.truncatedWhy = "not enough memory to keep it";
else if (esp_get_free_heap_size() < kTransientFloor + TextBuffer::kChunk + take)
@@ -720,7 +720,7 @@ void GeminiService::loadFromCard(GeminiPage& page, size_t freeBefore, const std:
if (page.totalLines % kIndexEvery == 0) page.lineIndex.push_back(lineStart | (pre ? 0x80000000u : 0));
if (line.compare(0, 3, "```") == 0) pre = !pre;
if (loading) {
if (page.text.bytes() + page.text.lineCount() * 8 + line.size() + 1 > budget) loading = false;
if (page.text.bytes() + page.text.lineCount() * kBytesPerLine + line.size() + 1 > budget) loading = false;
else {
line += '\n';
page.text.append(line.data(), line.size());
@@ -768,7 +768,7 @@ void GeminiService::readWindow(GeminiPage& page, size_t firstLine, size_t budget
continue;
}
if (lineNo >= firstLine) {
if (page.text.bytes() + page.text.lineCount() * 8 + line.size() + 1 > budget) done = true;
if (page.text.bytes() + page.text.lineCount() * kBytesPerLine + line.size() + 1 > budget) done = true;
else {
line += '\n';
page.text.append(line.data(), line.size());