New UNDF assignments (693→720): elixir-0002 → UNDF-2026-000000698 (typespec used_type_pairs O(T²)) r-source-0002 → UNDF-2026-000000711 (.walkClassGraph match dedup O(S²)) ruby-0003 → UNDF-2026-000000712 (RubyGems dependent_gems O(N²×D)) victoria-metrics-0002 → UNDF-2026-000000717 (MetricName tag-filter O(T×I)) Total: 720 UNDF assigned
3.5 KiB
UNDF: UNDF-2026-000000719
weechat-0003 — irc_channel_search O(C) linked-list scan — no hash index
Ecosystem
weechat (C)
Severity
MEDIUM — hot path: irc_channel_search() is called ~30+ times per IRC protocol message in irc-protocol.c (JOIN, PART, KICK, MODE, TOPIC, PRIVMSG, etc.)
Location
src/plugins/irc/irc-channel.c
- Function:
irc_channel_search(~line 92): O(C) linear walk ofserver->channels
src/plugins/irc/irc-protocol.c
- ~30+ call sites including: JOIN (~line 1560, 1587), PART (~line 1753), KICK (~line 1928), MODE (~line 2180), NICK (~line 2297), PRIVMSG (~line 3112), numeric 353/366/332 handlers, etc.
Description
irc_channel_search scans a linked list of all channels on a server to find
a channel by name:
// irc-channel.c:92
struct t_irc_channel *
irc_channel_search (struct t_irc_server *server, const char *channel_name)
{
struct t_irc_channel *ptr_channel;
if (!server || !channel_name)
return NULL;
for (ptr_channel = server->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel) // O(C) walk
{
if (irc_server_strcasecmp (server, ptr_channel->name, channel_name) == 0)
return ptr_channel;
}
return NULL;
}
With C channels on a server, each call is O(C). In irc-protocol.c, this
function is called ~30+ times across different protocol message handlers,
each of which processes one message. The cost per message is O(C) per
irc_channel_search call.
In extreme cases (e.g., a bot joined to thousands of channels, or an IRC network with large JOIN parameter lists), the cumulative cost per protocol event grows significantly.
The NICK handler at line ~2297 calls irc_channel_search once, then
iterates over all C channels calling irc_nick_search (O(N) each) — that
O(C×N) pattern is weechat-0001. The standalone irc_channel_search O(C)
cost itself is weechat-0003.
Fix
Add a channels_hashtable to struct t_irc_server (channel_name → t_irc_channel*),
mirroring the nicks_hashtable fix from weechat-0001:
--- a/src/plugins/irc/irc-server.h
+++ b/src/plugins/irc/irc-server.h
@@ struct t_irc_server {
struct t_irc_channel *channels;
+ struct t_hashtable *channels_hashtable; /* channel_name(lower) → t_irc_channel* */
--- a/src/plugins/irc/irc-channel.c
+++ b/src/plugins/irc/irc-channel.c
struct t_irc_channel *
irc_channel_search (struct t_irc_server *server, const char *channel_name)
{
- for (ptr_channel = server->channels; ptr_channel; ...)
- if (irc_server_strcasecmp (...) == 0) return ptr_channel;
+ if (server->channels_hashtable) {
+ char lower[512];
+ snprintf(lower, sizeof(lower), "%s", channel_name);
+ /* lowercase using irc_server_casemapping */
+ return weechat_hashtable_get(server->channels_hashtable, lower);
+ }
+ /* fallback to linear scan */
+ ...
}
Complexity
| Variant | Cost per lookup |
|---|---|
| Before | O(C) — linear scan through all channels |
| After | O(1) — hash table lookup |
| Speedup | C× — at C=1000 channels: 1000× |
Notes
- IRC servers typically allow users to join 100-2000 channels
- Bots joining many channels (monitoring, bridging) are common and hit this hard
- The
t_irc_server.channelslist is modified on JOIN/PART; the hashtable must be kept in sync inirc_channel_new()andirc_channel_free() - weechat-0001 fixed
nicks_hashtable; this defect is the parallel issue for channel lookup on the server