Public Access
GeminiService fetches on a short-lived task and hands the App a GeminiPage: header, the body as lines in 4 KB chunks (TextBuffer: no large block, no doubling copies), the final URL after up to 5 redirects. Certificates are pinned on first use per host and port; a change comes back as its own outcome with both fingerprints. No fetch starts below 55 KB free (Q86). With a card, the body streams to /gemini/cache/page.gmi in 1 KB pieces while the connection is open, then loads into RAM once its memory is back (Q87); StorageService::runAndWait (moved from the Debug Console) keeps every card access on the storage task. Without a card: RAM, with the steady and transient floors. Measured with IRC connected: Cosmos (31.6 KB) went from 4.6 KB to the whole page on the card and 20 KB on screen; lowest free heap 19.5 KB in transfer, 43 KB once loaded. `gemini get` and `gemini trust` on the console. 14 Gemini tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
48 lines
1.3 KiB
C++
48 lines
1.3 KiB
C++
#include "text_buffer.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace roro::gemini {
|
|
|
|
void TextBuffer::append(const char* data, size_t len) {
|
|
bytes_ += len;
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (data[i] == '\n') endLine();
|
|
else partial_ += data[i];
|
|
}
|
|
}
|
|
|
|
void TextBuffer::finish() {
|
|
if (!partial_.empty()) endLine();
|
|
}
|
|
|
|
void TextBuffer::endLine() {
|
|
if (!partial_.empty() && partial_.back() == '\r') partial_.pop_back();
|
|
size_t len = std::min<size_t>(partial_.size(), 0xFFFF);
|
|
// A line never spans two chunks: a new chunk when it doesn't fit (bigger if the line is).
|
|
if (chunks_.empty() || chunks_.back().size() + len > chunks_.back().capacity()) {
|
|
chunks_.emplace_back();
|
|
chunks_.back().reserve(std::max(kChunk, len));
|
|
}
|
|
std::string& chunk = chunks_.back();
|
|
lines_.push_back({static_cast<uint16_t>(chunks_.size() - 1), static_cast<uint16_t>(chunk.size()),
|
|
static_cast<uint16_t>(len)});
|
|
chunk.append(partial_, 0, len);
|
|
partial_.clear();
|
|
}
|
|
|
|
std::string TextBuffer::line(size_t i) const {
|
|
if (i >= lines_.size()) return "";
|
|
const Ref& r = lines_[i];
|
|
return chunks_[r.chunk].substr(r.offset, r.length);
|
|
}
|
|
|
|
void TextBuffer::clear() {
|
|
chunks_.clear();
|
|
lines_.clear();
|
|
partial_.clear();
|
|
bytes_ = 0;
|
|
}
|
|
|
|
} // namespace roro::gemini
|