From 1be32cc42104c262be9eda8d72e5045f9a45895b Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 2 Mar 2025 18:26:33 +0200 Subject: [PATCH] map: more c++ification --- src/admin.cpp | 5 +-- src/common.h | 6 +-- src/irc.cpp | 122 ++++++++++++++++++++++---------------------------- src/main.cpp | 5 +-- src/map.cpp | 5 +-- src/map.h | 118 ++++++++++++++++++++++++++++-------------------- src/net.cpp | 9 ++-- 7 files changed, 134 insertions(+), 136 deletions(-) diff --git a/src/admin.cpp b/src/admin.cpp index b059b1a..8257d6d 100644 --- a/src/admin.cpp +++ b/src/admin.cpp @@ -279,11 +279,10 @@ static int fn_channel_new(lua_State *L) char key[33]; memcpy(key, channel, chanlen+1); stringlower(key); - struct channel *chan = (struct channel *)map_get(&channel_map, key); + struct channel *chan = channel_map.get(key); if (!chan) { chan = channel_new(channel, key, creation ? creation : get_time()); - if (map_set(&channel_map, key, chan, NULL) == -1) - abort(); + channel_map.add(chan); } return 0; } diff --git a/src/common.h b/src/common.h index 3827de8..5d7e02b 100644 --- a/src/common.h +++ b/src/common.h @@ -33,12 +33,12 @@ struct tls; #define CAP_SETNAME 128 #define CAP_ACCOUNTNOTIFY 256 struct client_data { + char *key; // case-mapped nick SOCKET FD; struct tls *cctx; char type; struct client_data *next; char *nick; - char *key; // case-mapped nick char *username; char *realname; char *hostname; @@ -106,8 +106,8 @@ struct channel { #define DMODE_NOEXTERNAL 8 #define DMODE_SECRET 16 #define DMODE_TOPICPROT 32 -#define is_chanop(chan, client) (!!map_get(&(chan)->o, (client)->key)) -#define is_chanvoice(chan, client) (!!map_get(&(chan)->v, (client)->key)) +#define is_chanop(chan, client) (!!(chan)->o.has((client))) +#define is_chanvoice(chan, client) (!!(chan)->v.has((client))) extern clientmap client_map; extern channelmap channel_map; extern stringset autojoin; diff --git a/src/irc.cpp b/src/irc.cpp index 0b2373b..290c8e5 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -594,10 +594,9 @@ saslcleanup: struct channel *chan = findChannel(args[0]); if (!chan) return err_nosuchchannel(client, args[0]), sendbuf(client), 0; - if (!map_get(&chan->members, client->key)) + if (!chan->members.has(client)) return err_notonchannel(client, chan), sendbuf(client), 0; - if (map_set(&chan->o, client->key, client, NULL) == -1) - abort(); + chan->o.add(client); copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick); sendbuf_chan(chan); } else if(strcmp(command, "JOIN") == 0) { @@ -606,7 +605,7 @@ saslcleanup: if (!args[0]) return err_needmoreparams(client, command), sendbuf(client), 0; if (!strcmp(args[0], "0")) { - for (struct channel *chan; (chan = (struct channel *)map_pop(&client->channels)); ) + for (auto chan : client->channels.pop_view()) channel_part(client, chan, "I fell for a classic prank", NULL); return 0; } @@ -649,7 +648,7 @@ saslcleanup: channel_sendtopic(client, chan, 0); } else { // TODO: membership check, maybe? - if (chan->dmode & DMODE_TOPICPROT && !map_get(&chan->o, client->key)) + if (chan->dmode & DMODE_TOPICPROT && !chan->o.has(client)) return 0; // FIXME: error 482 channel_topic(client, chan, args[1], client->nick, get_time()); } @@ -668,21 +667,19 @@ saslcleanup: if (!chan) return err_nosuchchannel(client, args[1]), sendbuf(client), 0; if (chan->dmode & DMODE_INVITEONLY) { - if (!map_get(&chan->o, client->key)) + if (!chan->o.has(client)) return err_chanoprivsneeded(client, chan), sendbuf(client), 0; } else { - if (!map_get(&chan->members, client->key)) + if (!chan->members.has(client)) return err_notonchannel(client, chan), sendbuf(client), 0; } struct client_data *cl = findClient(args[0]); if (!cl) return err_nosuchnick(client, args[0]), sendbuf(client), 0; - if (map_get(&chan->members, cl->key)) + if (chan->members.has(cl)) return err_useronchannel(client, cl->nick, chan), sendbuf(client), 0; - if (map_set(&chan->invitees, cl->nick, cl, NULL) == -1) - abort(); - if (map_set(&cl->invites, chan->name, chan, NULL) == -1) - abort(); + chan->invitees.add(cl); + cl->invites.add(chan); rpl_inviting(client, cl, chan); sendbuf(client); msg_invite(client, cl, chan); @@ -696,9 +693,9 @@ saslcleanup: struct channel *chan = findChannel(args[0]); if (!chan) return err_nosuchchannel(client, args[0]), sendbuf(client), 0; - if (!map_get(&chan->o, client->key)) + if (!chan->o.has(client)) return err_chanoprivsneeded(client, chan), sendbuf(client), 0; - if (!map_get(&chan->members, client->key)) + if (!chan->members.has(client)) return err_notonchannel(client, chan), sendbuf(client), 0; char *p = args[1]; do { @@ -738,11 +735,11 @@ saslcleanup: if (match_list(client, chan->listb) && !match_list(client, chan->liste)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; if (chan->dmode & DMODE_INVITEONLY - && !map_get(&chan->invitees, client->key) && !match_list(client, chan->listI)) + && !chan->invitees.has(client) && !match_list(client, chan->listI)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; - if (chan->dmode & DMODE_NOEXTERNAL && !map_get(&chan->members, client->key)) + if (chan->dmode & DMODE_NOEXTERNAL && !chan->members.has(client)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; - if (chan->dmode & DMODE_MODERATED && !map_get(&chan->v, client->key) && !map_get(&chan->o, client->key)) + if (chan->dmode & DMODE_MODERATED && !chan->v.has(client) && !chan->o.has(client)) return err_cannotsendtochan(client, chan->name), sendbuf(client), 0; #ifdef HAS_LUA switch (admin_hook("sendtochannel", client, 0, chan, args[1])) { @@ -912,7 +909,7 @@ saslcleanup: struct channel *chan = findChannel(nick); if (!chan) return err_nosuchchannel(client, nick), sendbuf(client), 0; - if (!map_get(&chan->o, client->key)) + if (!chan->o.has(client)) return 0; // TODO: error if (argc == 2) { // Type A channel modes int all = !strcmp(args[1], "+0"); @@ -965,7 +962,7 @@ saslcleanup: return err_notregistered(client), sendbuf(client), 0; rpl_liststart(client), sendbuf(client); for (auto chan : channel_map) - if (client->umode & UMODE_OPER || !(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key)) + if (client->umode & UMODE_OPER || !(chan->dmode & DMODE_SECRET) || chan->members.has(client)) rpl_list(client, chan), sendbuf(client); rpl_listend(client), sendbuf(client); } else if(strcmp(command, "NAMES") == 0) { @@ -979,7 +976,7 @@ saslcleanup: if ((p = strchr(channel, ','))) *p++ = '\0'; struct channel *chan = findChannel(channel); - if (chan && (!(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key))) { + if (chan && (!(chan->dmode & DMODE_SECRET) || chan->members.has(client))) { channel_sendnames(client, chan); } else { rpl_endofnames(client, channel); @@ -1026,7 +1023,7 @@ saslcleanup: int visible = client->umode & UMODE_OPER || !(cl->umode & UMODE_INVISIBLE) || client == cl; if (!visible) { for (auto chan : cl->channels) { - if (map_get(&client->channels, chan->key)) { + if (client->channels.has(chan)) { visible = 1; break; } @@ -1061,7 +1058,7 @@ saslcleanup: if (cl->away) rpl_away(client, cl), sendbuf(client); for (auto chan : cl->channels) { - if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !map_get(&chan->members, client->key)) + if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !chan->members.has(client)) continue; rpl_whoischannels(client, cl, chan), sendbuf(client); } @@ -1120,7 +1117,7 @@ saslcleanup: *p++ = '\0'; if (client->monitor.add(nick)) monitor_count++; - struct client_data *cl = (struct client_data *)map_get(&client_map, nick); + struct client_data *cl = client_map.get(nick); if (cl) rpl_mononline(client, cl), sendbuf(client); else @@ -1153,7 +1150,7 @@ saslcleanup: break; case 'S': for (auto key : client->monitor) { - struct client_data *cl = (struct client_data *)map_get(&client_map, key); + struct client_data *cl = client_map.get(key); if (cl) rpl_mononline(client, cl), sendbuf(client); else @@ -1310,7 +1307,7 @@ void setNick(struct client_data *client, char* nickname) { stringlower(key); // TODO: make the cia nickname a define - if (!strcmp(key, "cia") || map_get(&client_map, key)) { + if (!strcmp(key, "cia") || client_map.get(key)) { err_nicknameinuse(client, nickname), sendbuf(client); return; } @@ -1324,20 +1321,11 @@ void setNick(struct client_data *client, char* nickname) { sendbuf(client); sendbuf_visible(client); rpl_monoffline(0, client->key), sendbuf_monitor(client->key, 0, 0, 0); - map_unset(&client_map, client->key, NULL); + client_map.remove(client); for (auto chan : client->channels) { - map_unset(&chan->members, client->key, NULL); - if (map_set(&chan->members, key, client, NULL) == -1) - abort(); - void *old; - map_unset(&chan->o, client->key, &old); - if (old) - if (map_set(&chan->o, key, client, NULL) == -1) - abort(); - map_unset(&chan->v, client->key, &old); - if (old) - if (map_set(&chan->v, key, client, NULL) == -1) - abort(); + chan->members.rename(client, key); + chan->o.rename(client, key); + chan->v.rename(client, key); } free(client->nick); free(client->key); @@ -1354,8 +1342,7 @@ void setNick(struct client_data *client, char* nickname) { client->key = mystrdup(key); if (!client->key) abort(); - if (map_set(&client_map, client->key, client, 0) == -1) - abort(); + client_map.add(client); rpl_mononline(0, client), sendbuf_monitor(client->key, 0, 0, 0); return; } @@ -1418,11 +1405,11 @@ void client_chghost(struct client_data *client, const char *newusername, const c sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_EXTENDEDJOIN|CAP_CHGHOST); msg_join_extended(client, chan); sendbuf_list_sans_self_caps(client, chan->members, 0, CAP_EXTENDEDJOIN, CAP_CHGHOST); - if (map_get(&chan->v, client->key)) { + if (chan->v.has(client)) { copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +v %s\r\n", chan->name, client->nick); sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_CHGHOST); } - if (map_get(&chan->o, client->key)) { + if (chan->o.has(client)) { copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick); sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_CHGHOST); } @@ -1487,17 +1474,15 @@ void channel_join(struct client_data *client, const char *channel, const char *c char key[33]; memcpy(key, channel, chanlen+1); stringlower(key); - struct channel *chan = (struct channel *)map_get(&channel_map, key); + struct channel *chan = channel_map.get(key); int newchan = !chan; if (!chan) { chan = channel_new(channel, key, get_time()); chan->dmode &= ~DMODE_PERSIST; - if (map_set(&channel_map, key, chan, NULL) == -1) - abort(); - if (map_set(&chan->o, client->key, client, NULL) == -1) - abort(); + channel_map.add(chan); + chan->o.add(client); } else { - if (map_get(&chan->members, client->key)) + if (chan->members.has(client)) return; if (chan->chankey && (!chankey || strcmp(chan->chankey, chankey) == 0)) return err_badchannelkey(client, chan), sendbuf(client); @@ -1506,13 +1491,11 @@ void channel_join(struct client_data *client, const char *channel, const char *c if (match_list(client, chan->listb) && !match_list(client, chan->liste)) return err_bannedfromchan(client, chan), sendbuf(client); if (chan->dmode & DMODE_INVITEONLY - && !map_get(&chan->invitees, client->key) && !match_list(client, chan->listI)) + && !chan->invitees.has(client) && !match_list(client, chan->listI)) return err_inviteonlychan(client, chan), sendbuf(client); } - if (map_set(&chan->members, client->key, client, NULL) == -1) - abort(); - if (map_set(&client->channels, key, chan, NULL) == -1) - abort(); + chan->members.add(client); + client->channels.add(chan); chan->member_count++; // send JOIN to everyone msg_join(client, chan); @@ -1529,14 +1512,12 @@ void channel_join(struct client_data *client, const char *channel, const char *c // TODO: send channel modes to the joining client // apply +w modes if (match_list(client, chan->listwo)) { - if (map_set(&chan->o, client->key, client, NULL) == -1) - abort(); + chan->o.add(client); copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick); sendbuf_chan(chan); } if (match_list(client, chan->listwv)) { - if (map_set(&chan->v, client->key, client, NULL) == -1) - abort(); + chan->v.add(client); copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +v %s\r\n", chan->name, client->nick); sendbuf_chan(chan); } @@ -1561,7 +1542,7 @@ struct channel *channel_new(const char *channel, const char *key, time_t creatio } void channel_part(struct client_data *client, struct channel *chan, const char *reason, struct client_data *kicker) { - if (!map_get(&chan->members, client->key)) + if (!chan->members.has(client)) return; if (kicker) { msg_kick(kicker, client, chan, reason); @@ -1570,19 +1551,19 @@ void channel_part(struct client_data *client, struct channel *chan, const char * msg_part(client, chan, *reason ? reason : NULL); sendbuf_chan(chan); } - map_unset(&client->channels, chan->key, NULL); - map_unset(&chan->members, client->key, NULL); - map_unset(&chan->o, client->key, NULL); - map_unset(&chan->v, client->key, NULL); + client->channels.remove(chan); + chan->members.remove(client); + chan->o.remove(client); + chan->v.remove(client); chan->member_count--; if (!chan->member_count && !(chan->dmode & DMODE_PERSIST)) channel_kill(chan); } void channel_kill(struct channel *chan) { - map_unset(&channel_map, chan->key, NULL); + channel_map.remove(chan); for (auto cl : chan->invitees) - map_unset(&cl->invites, chan->key, NULL); + cl->invites.remove(chan); free(chan->key); free(chan->name); free(chan->topic); @@ -1743,13 +1724,16 @@ void channel_mode(struct client_data *client, struct channel *chan, char *args[] err_nosuchnick(client, nick), sendbuf(client); break; } - if (!map_get(&chan->members, cl->key)) { + if (!chan->members.has(cl)) { if (client) err_usernotinchannel(client, cl->nick, chan), sendbuf(client); break; } - if (map_set(*p == 'o' ? &chan->o : &chan->v, cl->key, is_plus ? cl : NULL, NULL) == -1) - abort(); + clientmap &clmap = *p == 'o' ? chan->o : chan->v; + if (is_plus) + clmap.add(cl); + else + clmap.remove(cl); copy_strf(&databuf, ":%s!%s@%s MODE %s %s%s %s\r\n", n, u, h, chan->name, is_plus?"+":"-", which, cl->nick); sendbuf_chan(chan); @@ -1794,7 +1778,7 @@ struct client_data *findClient(const char *nickname) return NULL; memcpy(key, nickname, len+1); stringlower(key); - return (struct client_data *)map_get(&client_map, key); + return client_map.get(key); } struct channel *findChannel(const char *channel) { @@ -1804,5 +1788,5 @@ struct channel *findChannel(const char *channel) char key[33]; memcpy(key, channel, chanlen+1); stringlower(key); - return (struct channel *)map_get(&channel_map, key); + return channel_map.get(key); } diff --git a/src/main.cpp b/src/main.cpp index 6ae8fdb..2eb65c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -211,8 +211,7 @@ int main(int argc, char *argv[]) } else if(!strcmp(argv[i], "--autojoin")) { if (i + 1 >= argc) return fprintf(stderr, "not enough parameters\n"), 1; - if (map_set(&autojoin, argv[i+1], (void*)1, NULL) == -1) - abort(); + autojoin.add(argv[i+1]); i += 1; } else { return fprintf(stderr, "unknown flag '%s'\n", argv[i]), 1; @@ -383,7 +382,7 @@ int main(int argc, char *argv[]) #endif free(databuf.buf); /* free any persistent channels */ - for (struct channel *chan; (chan = (struct channel *)map_pop(&channel_map)); ) + for (auto chan : channel_map.pop_view()) channel_kill(chan); map_free(&client_map); map_free(&channel_map); diff --git a/src/map.cpp b/src/map.cpp index dbe039f..b5e0f4a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,3 +1,4 @@ +#include "common.h" #include "map.h" #include #include @@ -85,7 +86,3 @@ int map_foriter(map_iter_t *it, const char **outkey, void *outvalue) it->index++; return 1; } -void *map_pop(map_base *root) -{ - return root->len ? root->ptr[root->len-1].value : 0; -} diff --git a/src/map.h b/src/map.h index 5fe0c90..475311e 100644 --- a/src/map.h +++ b/src/map.h @@ -19,9 +19,6 @@ struct map_value { struct map_base { struct map_value *ptr; size_t len, cap; - size_t size() { - return len; - } }; typedef struct map_iter { map_base *root; @@ -45,11 +42,9 @@ void *map_get(map_base *root, const char *key); /* Associate a new value with a given key. * Returns 0 on success, -1 on allocation error. * If oldvalue is not NULL, it receives the old value of key. + * No allocation failures on value = NULL. * Dynarr complexity: O(n) */ NODISCARD int map_set(map_base *root, const char *key, const void *value, void **oldvalue); -/* Remove a key. - * Same as associating a key to NULL. No allocation failures. */ -#define map_unset(root, key, oldvalue) ((void)map_set((root), (key), 0, (oldvalue))) /* Removes all keys from the map and frees any associated memory. * The map_t is reset to zero-initialized state and can be used again. * Dynarr complexity: O(n) */ @@ -65,70 +60,95 @@ void map_free(map_base *root); * Returns 0 on end of iteration, 1 otherwise. */ NODISCARD int map_foriter(map_iter_t *f, const char **outkey, void *outvalue); -#define map_foreach(rootp, outkey, outvalue) \ - for (map_iter_t map_itertmp = { .root = rootp, .index = 0 }; map_foriter(&map_itertmp, outkey, outvalue); ) -/* Return a value from the map. - * No guarantees about which one! - * You don't get a key and the association is not removed. - * Returns 0 if the map is empty. - * - * Typical use: - * for (value_type *v; v = map_pop(&map); ) - * map_unset(&map, v->key, 0); - * free(v); - * } - * map_free(&map); - */ -NODISCARD void *map_pop(map_base *root); #include +#include #include -template -struct iterator_base { - T *ptr; - iterator_base &operator++() { ++ptr; return *this; } - bool operator!=(iterator_base other) { return ptr != other.ptr; } -}; struct client_data; struct channel; -struct clientmap : map_base { - struct iterator : iterator_base { - auto operator*() { return (struct client_data *)ptr->value; } +template +struct map_base2 : map_base { + using value_type = V; + struct iterator { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = V; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = V; + iterator &operator++() { ++ptr; return *this; } + iterator operator++(int) { auto it = *this; ++ptr; return it; } + iterator &operator--() { --ptr; return *this; } + iterator operator--(int) { auto it = *this; --ptr; return it; } + bool operator==(const iterator& other) const = default; + reference operator*() const { return T::to_value(ptr); } + map_value *ptr; }; + static_assert(std::bidirectional_iterator); auto begin() { return iterator{ptr}; } auto end() { return iterator{ptr+len}; } -}; -struct channelmap : map_base { - struct iterator : iterator_base { - auto operator*() { return (struct channel *)ptr->value; } + struct reverse { + map_base2 &obj; + auto begin() { return std::make_reverse_iterator(obj.end()); } + auto end() { return std::make_reverse_iterator(obj.begin()); } }; - auto begin() { return iterator{ptr}; } - auto end() { return iterator{ptr+len}; } -}; -struct stringset : map_base { + // Iterate over keys of the map in such a way that you can delete them. + auto pop_view() { + return reverse{*this}; + } void clear() { map_free(this); } - bool has(const char *key) { - return !!map_get(this, key); + size_t size() { + return len; + } +protected: + static const char *to_key(V v) { + return v->key; + } + static V to_value(map_value *v) { + return static_cast(v->value); } - bool add(const char *key) { +public: + bool has(V v) { + return !!map_get(this, T::to_key(v)); + } + bool add(V v) { void *old; - if (map_set(this, key, (void *)1, &old) == -1) + if (map_set(this, T::to_key(v), v, &old) == -1) abort(); return !old; } - bool remove(const char *key) { + bool remove(V v) { void *old; - map_unset(this, key, &old); + (void)map_set(this, T::to_key(v), 0, &old); return !!old; } - struct iterator : iterator_base { - auto operator*() { return (const char *)ptr->key; } - }; - auto begin() { return iterator{ptr}; } - auto end() { return iterator{ptr+len}; } + + V get(const char *k) { + return (V)map_get(this, k); + } +}; + +struct clientmap : map_base2 { + void rename(struct client_data *v, const char *newk) { + if (remove(v) && map_set(this, newk, v, 0) == -1) + abort(); + } +}; +struct channelmap : map_base2 { +}; +struct stringset : map_base2 { +protected: + friend map_base2; + static const char *to_key(const char *v) { + return v; + } + static const char *to_value(map_value *v) { + return v->key; + } +public: + const char *get(const char *k) = delete; }; #endif diff --git a/src/net.cpp b/src/net.cpp index d9d5b3d..f0ab00b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -66,18 +66,17 @@ void killConnection(struct client_data *cl, int byChoice) } update_poll_reg(cl); /* Part all channels */ - for (struct channel *chan; (chan = (struct channel *)map_pop(&cl->channels)); ) + for (auto chan : cl->channels.pop_view()) channel_part(cl, chan, NULL, NULL); /* Remove from client_map and get rid of the pending invites */ if (cl->key) { #ifdef HAS_LUA admin_hook("nickdel", cl, 0, 0, cl->key); #endif - map_unset(&client_map, cl->key, NULL); + client_map.remove(cl); rpl_monoffline(0, cl->key), sendbuf_monitor(cl->key, 0, 0, 0); - struct channel *chan; - map_foreach(&cl->invites, 0, &chan) - map_unset(&chan->invitees, cl->key, NULL); + for (auto chan : cl->invites) + chan->invitees.remove(cl); } map_free(&cl->channels); map_free(&cl->invites); -- 2.56.0