java-topology/docs/tickets/jami-daemon-0001-conversation-load-replies-vector-linear-scan.md

2.2 KiB
Raw Blame History

jami-daemon-0001: std::find on replies vector O(n) per git commit in conversation history load

Target: savoirfairelinux/jami-daemon Severity: MEDIUM CWE: CWE-407 (Inefficient Algorithmic Complexity) File: src/jamidht/conversation.cpp Lines: 832, 837 Status: PATCHED

Description

Conversation::loadMessages() walks the conversation git log via a callback (emplaceCb). For each commit it performs two std::find scans on the replies vector:

  1. Line 832: Check if message["reply-to"] is already tracked.
  2. Line 837: Remove message["id"] from the tracked set.

replies is a std::vector<std::string> that grows as threaded messages accumulate. If the conversation contains R reply-chains the cost per commit is O(R), and with C total commits the history load is O(C × R).

Conversations with long threaded discussions (common in P2P group chats) will exhibit quadratic load times when loading or syncing history.

// conversation.cpp:783
std::vector<std::string> replies;
...
// emplaceCb called once per git commit:
if (message.find("reply-to") != message.end()) {
    auto it = std::find(replies.begin(), replies.end(), message.at("reply-to")); // O(R)
    if (it == replies.end()) {
        replies.emplace_back(message.at("reply-to"));
    }
}
auto it = std::find(replies.begin(), replies.end(), message.at("id"));           // O(R)
if (it != replies.end()) {
    replies.erase(it);
}

Fix

Replace std::vector<std::string> replies with std::unordered_set<std::string>:

std::unordered_set<std::string> replies;
...
if (message.find("reply-to") != message.end()) {
    replies.insert(message.at("reply-to"));                    // O(1) avg
}
auto eraseIt = replies.find(message.at("id"));                 // O(1) avg
if (eraseIt != replies.end()) {
    replies.erase(eraseIt);
}

Erase-by-iterator on unordered_set is O(1) amortized, eliminating the O(R) scan entirely.

Complexity

Before After
Per commit O(R) O(1) amortized
Full load (C commits, R replies) O(C × R) O(C)

For a conversation with 1 000 commits and 200 reply-chains: 200 000 → 1 000 operations — 200× speedup.