perf(ui): dedupe time-ago + allocation-free case-insensitive filter (audit #1-3)
Per-frame hot paths in the immediate-mode UI were allocating needlessly:
- Address filtering in the balance tab rebuilt a std::string filter per address AND
containsIgnoreCase() lower-cased two fresh copies per call — ~6×N allocations/frame
on large wallets. New util::containsIgnoreCase(string_view, string_view) is
allocation-free, and the filter is now built once outside the loop.
- Four duplicated "time ago" implementations (balance_tab_helpers, balance_recent_tx,
send_tab, transactions_tab) are consolidated into util::formatTimeAgo (localized long
form) + util::formatTimeAgoShort (compact "5s ago"), preserving each call site's exact
display style. Both use snprintf, no per-row string concatenation.
- The send-tab address-suggestion scan (a walk over the whole tx list) is memoized on the
typed text + tx count, so it no longer recomputes every frame while the user pauses.
New src/util/text_format.{h,cpp}; the two existing containsIgnoreCase/timeAgo definitions
now delegate to it. Added to both the app and test targets (test target also gains i18n.cpp,
which text_format's localized path needs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include "../../data/wallet_state.h"
|
||||
#include "../../util/address_validation.h"
|
||||
#include "../../util/i18n.h"
|
||||
#include "../../util/text_format.h"
|
||||
#include "../notifications.h"
|
||||
#include "../layout.h"
|
||||
#include "../schema/ui_schema.h"
|
||||
@@ -163,14 +164,7 @@ static bool IsValidTransparentAddr(const char* a) {
|
||||
}
|
||||
|
||||
static std::string timeAgo(int64_t timestamp) {
|
||||
if (timestamp <= 0) return "";
|
||||
int64_t now = (int64_t)std::time(nullptr);
|
||||
int64_t diff = now - timestamp;
|
||||
if (diff < 0) diff = 0;
|
||||
if (diff < 60) return std::to_string(diff) + "s ago";
|
||||
if (diff < 3600) return std::to_string(diff / 60) + "m ago";
|
||||
if (diff < 86400) return std::to_string(diff / 3600) + "h ago";
|
||||
return std::to_string(diff / 86400) + "d ago";
|
||||
return dragonx::util::formatTimeAgoShort(timestamp);
|
||||
}
|
||||
|
||||
static void DrawTxIcon(ImDrawList* dl, const std::string& type,
|
||||
@@ -324,14 +318,24 @@ static void RenderAddressSuggestions(const WalletState& state, float width, cons
|
||||
bool is_valid_t = IsValidTransparentAddr(s_to_address);
|
||||
if (is_valid_z || is_valid_t) return;
|
||||
|
||||
std::vector<std::string> suggestions;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" || tx.address.empty()) continue;
|
||||
if (tx.address.find(partial) != std::string::npos) {
|
||||
bool dup = false;
|
||||
for (const auto& s : suggestions) { if (s == tx.address) { dup = true; break; } }
|
||||
if (!dup) suggestions.push_back(tx.address);
|
||||
if (suggestions.size() >= (size_t)schema::UI().drawElement("tabs.send", "max-suggestions").size) break;
|
||||
// Memoize the suggestion scan: it walks the whole transaction list, so rebuilding it every
|
||||
// frame (the user pauses on a partial address for many frames) is wasted work. Recompute only
|
||||
// when the typed text or the transaction count changes.
|
||||
static std::vector<std::string> suggestions;
|
||||
static std::string s_sugKey;
|
||||
const std::string sugKey = partial + "#" + std::to_string(state.transactions.size());
|
||||
if (sugKey != s_sugKey) {
|
||||
s_sugKey = sugKey;
|
||||
suggestions.clear();
|
||||
const size_t maxSug = (size_t)schema::UI().drawElement("tabs.send", "max-suggestions").size;
|
||||
for (const auto& tx : state.transactions) {
|
||||
if (tx.type != "send" || tx.address.empty()) continue;
|
||||
if (tx.address.find(partial) != std::string::npos) {
|
||||
bool dup = false;
|
||||
for (const auto& s : suggestions) { if (s == tx.address) { dup = true; break; } }
|
||||
if (!dup) suggestions.push_back(tx.address);
|
||||
if (suggestions.size() >= maxSug) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (suggestions.empty()) return;
|
||||
|
||||
Reference in New Issue
Block a user