Public Access
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:
@@ -103,8 +103,11 @@ void GeminiApp::show(GeminiPage&& page) {
|
||||
}
|
||||
|
||||
void GeminiApp::analyse() {
|
||||
types_.clear();
|
||||
links_.clear();
|
||||
// Sized exactly: the Service budgeted these bytes per line (kBytesPerLine), growth by
|
||||
// doubling would take up to twice that.
|
||||
std::vector<uint8_t>().swap(types_);
|
||||
std::vector<uint32_t>().swap(links_);
|
||||
types_.reserve(page_.text.lineCount());
|
||||
// A window may start inside a preformatted block: the index says (bit 31).
|
||||
size_t entry = page_.windowStart / GeminiService::kIndexEvery;
|
||||
bool pre = page_.windowed && entry < page_.lineIndex.size() && (page_.lineIndex[entry] & 0x80000000u);
|
||||
@@ -114,6 +117,7 @@ void GeminiApp::analyse() {
|
||||
types_.push_back(static_cast<uint8_t>(t));
|
||||
if (t == LineType::Link) links_.push_back(i);
|
||||
}
|
||||
links_.shrink_to_fit();
|
||||
layoutDirty_ = true;
|
||||
requestRedraw();
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -64,6 +64,10 @@ class GeminiService {
|
||||
static constexpr int kMaxLinkedPages = 30; // Q83
|
||||
static constexpr size_t kIndexEvery = 64; // lines between lineIndex entries
|
||||
static constexpr size_t kMaxLineBytes = 8192; // longer lines are cut
|
||||
// What a page costs per line on top of its text: TextBuffer's index (8) and the App's tables
|
||||
// (type, first row, links: about 8). Counting only the first left the steady floor 1.5 to 3 KB
|
||||
// short on a windowed page (found in M3).
|
||||
static constexpr size_t kBytesPerLine = 16;
|
||||
static constexpr const char* kBookmarks = "/gemini/bookmarks.gmi";
|
||||
static constexpr const char* kStartUrl = "about:start";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user