]> PatchyGit - patchyircd.git/commitdiff
the great findfirst refactoring
authorEgor <egor@patchysicp.xyz>
Mon, 23 Oct 2023 09:58:31 +0000 (12:58 +0300)
committerEgor <egor@patchysicp.xyz>
Mon, 23 Oct 2023 09:58:31 +0000 (12:58 +0300)
not only is the syntax markedly better now, but also it's no longer
nescessary to allocate any memory to do a traversal.

src/irc.c
src/main.c
src/map.h
src/map_dynarr.c
src/map_trie.c
src/net.c

index 559bcef..8fa0ecb 100644 (file)
--- a/src/irc.c
+++ b/src/irc.c
@@ -28,33 +28,27 @@ void chanlog() {
        }
 }
 void sendbuf_chan_caps(struct channel *chan, unsigned positive, unsigned negative) {
-       map_find_t f = {0};
-       if (map_findfirst(&f, &chan->members) == 1)
-               do {
-                       struct client_data *cl = f->value;
-                       if (!(~cl->caps & positive) && !(cl->caps & negative))
-                               sendbuf(cl);
-               } while (map_findnext(&f) == 1);
+       struct client_data *cl;
+       map_foreach(&chan->members, 0, &cl)
+               if (!(~cl->caps & positive) && !(cl->caps & negative))
+                       sendbuf(cl);
 }
 void sendbuf_chan(struct channel *chan) {
        sendbuf_chan_caps(chan, 0, 0);
 }
 void sendbuf_list_sans_self_caps(struct client_data *client, map_t *ls, int cookie, int positive, int negative) {
-       map_find_t f = {0};
-       if (map_findfirst(&f, ls) == 1)
-               do {
-                       struct client_data *cl = f->value;
-                       if (client != cl) {
-                               if (cookie) {
-                                       if (cookie != cl->cookie)
-                                               cl->cookie = cookie;
-                                       else
-                                               continue;
-                               }
-                               if (!(~cl->caps & positive) && !(cl->caps & negative))
-                                       sendbuf(cl);
+       struct client_data *cl;
+       map_foreach(ls, 0, &cl)
+               if (client != cl) {
+                       if (cookie) {
+                               if (cookie != cl->cookie)
+                                       cl->cookie = cookie;
+                               else
+                                       continue;
                        }
-               } while (map_findnext(&f) == 1);
+                       if (!(~cl->caps & positive) && !(cl->caps & negative))
+                               sendbuf(cl);
+               }
 }
 void sendbuf_list_sans_self(struct client_data *client, map_t *ls, int cookie) {
        sendbuf_list_sans_self_caps(client, ls, cookie, 0, 0);
@@ -63,24 +57,18 @@ int get_cookie() {
        static int cookie = 0;
        if (++cookie == 0) {
                // handle overflow
-               map_find_t f = {0};
-               if (map_findfirst(&f, &client_map) == 1)
-                       do {
-                               struct client_data *cl = f->value;
-                               cl->cookie = 0;
-                       } while (map_findnext(&f) == 1);
+               struct client_data *cl;
+               map_foreach(&client_map, 0, &cl)
+                       cl->cookie = 0;
                cookie++;
        }
        return cookie;
 }
 void sendbuf_visible_caps(struct client_data *client, int positive, int negative) {
        int cookie = get_cookie();
-       map_find_t f = {0};
-       if (map_findfirst(&f, &client->channels) == 1)
-               do {
-                       struct channel *chan = f->value;
-                       sendbuf_list_sans_self_caps(client, &chan->members, cookie, positive, negative);
-               } while (map_findnext(&f) == 1);
+       struct channel *chan;
+       map_foreach(&client->channels, 0, &chan)
+               sendbuf_list_sans_self_caps(client, &chan->members, cookie, positive, negative);
 }
 void sendbuf_visible(struct client_data *client) {
        sendbuf_visible_caps(client, 0, 0);
@@ -100,14 +88,10 @@ const char *format_umodes(struct client_data *cl) {
 }
 int match_list(struct client_data *cl, map_t *list)
 {
-       map_find_t f = {0};
-       if (map_findfirst(&f, list) == 1)
-               do {
-                       if (irc_glob(cl->nuh, f->key)) {
-                               map_findfree(&f);
-                               return 1;
-                       }
-               } while (map_findnext(&f) == 1);
+       const char *key;
+       map_foreach(list, &key, 0)
+               if (irc_glob(cl->nuh, key))
+                       return 1;
        return 0;
 }
 int parsePacket(struct client_data *client) {
@@ -365,13 +349,10 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                if (!(client->umode & UMODE_OPER))
                        return err_noprivileges(client), sendbuf(client), 0;
                msg_wallops(client, args[0]);
-               map_find_t f = {0};
-               if (map_findfirst(&f, &client_map) == 1)
-                       do {
-                               struct client_data *cl = f->value;
-                               if (cl->umode & UMODE_WALLOPS)
-                                       sendbuf(cl);
-                       } while (map_findnext(&f) == 1);
+               struct client_data *cl;
+               map_foreach(&client_map, 0, &cl)
+                       if (cl->umode & UMODE_WALLOPS)
+                               sendbuf(cl);
        } else if(strcmp(command, "KILL") == 0) {
                if (!is_registered(client))
                        return err_notregistered(client), sendbuf(client), 0;
@@ -385,13 +366,10 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                if (client == cl)
                        return 0; // FIXME: error message
                msg_kill(client, cl, args[1] ? args[1] : "(no reason)");
-               map_find_t f = {0};
-               if (map_findfirst(&f, &client_map) == 1)
-                       do {
-                               struct client_data *cl2 = f->value;
-                               if (cl2->umode & UMODE_OPER)
-                                       sendbuf(cl2);
-                       } while (map_findnext(&f) == 1);
+               struct client_data *cl2;
+               map_foreach(&client_map, 0, &cl2)
+                       if (cl2->umode & UMODE_OPER)
+                               sendbuf(cl2);
                // FIXME: add appropraite QUIT message
                killConnection(cl, 0);
        } else if(strcmp(command, "JEDGAR") == 0) {
@@ -416,12 +394,8 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                if (!args[0])
                        return err_needmoreparams(client, command), sendbuf(client), 0;
                if (!strcmp(args[0], "0")) {
-                       map_find_t f = {0};
-                       while (map_findfirst(&f, &client->channels) == 1) {
-                               struct channel *chan = f->value;
+                       for (struct channel *chan; (chan = map_pop(&client->channels)); )
                                channel_part(client, chan, "I fell for a classic prank", NULL);
-                       }
-                       map_findfree(&f);
                        return 0;
                }
                char *p = args[0];
@@ -487,12 +461,9 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                        return err_notregistered(client), sendbuf(client), 0;
                if (!args[0]) {
                        // List pending invites
-                       map_find_t f = {0};
-                       if (map_findfirst(&f, &client->invites) == 1)
-                               do {
-                                       struct channel *chan = f->value;
-                                       rpl_invitelist(client, chan), sendbuf(client);
-                               } while(map_findnext(&f) == 1);
+                       struct channel *chan;
+                       map_foreach(&client->invites, 0, &chan)
+                               rpl_invitelist(client, chan), sendbuf(client);
                        return rpl_endofinvitelist(client), sendbuf(client), 0;
                }
                if (!args[1])
@@ -736,7 +707,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                if (!map_get(&chan->o, client->key))
                                        return 0; // TODO: error
                                if (argc == 2) { // Type A channel modes
-                                       map_find_t f = {0};
+                                       const char *key;
                                        int all = !strcmp(args[1], "+0");
                                        int any = 0;
                                        if (all) {
@@ -745,46 +716,34 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                                any = 1;
                                        }
                                        if (all || !strcmp(args[1], "+b") || !strcmp(args[1], "b")) {
-                                               if (map_findfirst(&f, &chan->listb) == 1)
-                                                       do {
-                                                               rpl_banlist(client, chan, f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
+                                               map_foreach(&chan->listb, &key, 0)
+                                                       rpl_banlist(client, chan, key), sendbuf(client);
                                                rpl_endofbanlist(client, chan), sendbuf(client);
                                                any = 1;
                                        }
                                        if (all || !strcmp(args[1], "+e") || !strcmp(args[1], "e")) {
-                                               if (map_findfirst(&f, &chan->liste) == 1)
-                                                       do {
-                                                               rpl_exceptlist(client, chan, f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
+                                               map_foreach(&chan->liste, &key, 0)
+                                                       rpl_exceptlist(client, chan, key), sendbuf(client);
                                                rpl_endofexceptlist(client, chan), sendbuf(client);
                                                any = 1;
                                        }
                                        if (all || !strcmp(args[1], "+I") || !strcmp(args[1], "I")) {
-                                               if (map_findfirst(&f, &chan->listI) == 1)
-                                                       do {
-                                                               rpl_invexlist(client, chan, f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
+                                               map_foreach(&chan->listI, &key, 0)
+                                                       rpl_invexlist(client, chan, key), sendbuf(client);
                                                rpl_endofinvexlist(client, chan), sendbuf(client);
                                                any = 1;
                                        }
                                        if (all || !strcmp(args[1], "+q") || !strcmp(args[1], "q")) {
-                                               if (map_findfirst(&f, &chan->listq) == 1)
-                                                       do {
-                                                               rpl_quietlist(client, chan, f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
+                                               map_foreach(&chan->listq, &key, 0)
+                                                       rpl_quietlist(client, chan, key), sendbuf(client);
                                                rpl_endofquietlist(client, chan), sendbuf(client);
                                                any = 1;
                                        }
                                        if (all || !strcmp(args[1], "+w") || !strcmp(args[1], "w")) {
-                                               if (map_findfirst(&f, &chan->listwo) == 1)
-                                                       do {
-                                                               rpl_accesslist(client, chan, 'o', f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
-                                               if (map_findfirst(&f, &chan->listwv) == 1)
-                                                       do {
-                                                               rpl_accesslist(client, chan, 'v', f->key), sendbuf(client);
-                                                       } while(map_findnext(&f) == 1);
+                                               map_foreach(&chan->listwo, &key, 0)
+                                                       rpl_accesslist(client, chan, 'o', key), sendbuf(client);
+                                               map_foreach(&chan->listwv, &key, 0)
+                                                       rpl_accesslist(client, chan, 'v', key), sendbuf(client);
                                                rpl_endofaccesslist(client, chan), sendbuf(client);
                                                any = 1;
                                        }
@@ -966,13 +925,10 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                if (!is_registered(client))
                        return err_notregistered(client), sendbuf(client), 0;
                rpl_liststart(client), sendbuf(client);
-               map_find_t f = {0};
-               if (map_findfirst(&f, &channel_map) == 1)
-                       do {
-                               struct channel *chan = f->value;
-                               if (!(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key))
-                                       rpl_list(client, chan), sendbuf(client);
-                       } while(map_findnext(&f) == 1);
+               struct channel *chan;
+               map_foreach(&channel_map, 0, &chan)
+                       if (!(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key))
+                               rpl_list(client, chan), sendbuf(client);
                rpl_listend(client), sendbuf(client);
        } else if(strcmp(command, "NAMES") == 0) {
                if (!is_registered(client))
@@ -1018,12 +974,9 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                        struct channel *chan = findChannel(args[0]);
                        // TODO: handle chanmode +s, umode +i
                        if (chan) {
-                               map_find_t f = {0};
-                               if (map_findfirst(&f, &chan->members) == 1)
-                                       do {
-                                               struct client_data *cl = f->value;
-                                               rpl_whoreply(client, cl, chan), sendbuf(client);
-                                       } while(map_findnext(&f) == 1);
+                               struct client_data *cl;
+                               map_foreach(&chan->members, 0, &cl)
+                                       rpl_whoreply(client, cl, chan), sendbuf(client);
                        }
                } else {
                        struct client_data *cl = findClient(args[0]);
@@ -1053,14 +1006,12 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                        rpl_whoisidle(client, cl), sendbuf(client);
                        if (cl->away)
                                rpl_away(client, cl), sendbuf(client);
-                       map_find_t f = {0};
-                       if (map_findfirst(&f, &cl->channels) == 1)
-                               do {
-                                       struct channel *chan = f->value;
-                                       if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !map_get(&chan->members, client->key))
-                                               continue;
-                                       rpl_whoischannels(client, cl, chan), sendbuf(client);
-                               } while (map_findnext(&f) == 1);
+                       struct channel *chan;
+                       map_foreach(&cl->channels, 0, &chan) {
+                               if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !map_get(&chan->members, client->key))
+                                       continue;
+                               rpl_whoischannels(client, cl, chan), sendbuf(client);
+                       }
                        if (cl->umode & UMODE_REGISTERED)
                                rpl_whoisaccount(client, cl), sendbuf(client);
                        if (client == cl || client->umode & UMODE_OPER)
@@ -1131,11 +1082,9 @@ void newUser(struct client_data *client) {
        sendLUSERS(client);
        rpl_umodeis(client),  sendbuf(client);
        sendMOTD(client);
-       map_find_t f = {0};
-       if (map_findfirst(&f, &autojoin) == 1)
-               do {
-                       channel_join(client, f->key, NULL);
-               } while (map_findnext(&f) == 1);
+       const char *key;
+       map_foreach(&autojoin, &key, 0)
+               channel_join(client, key, NULL);
 }
 void sendLUSERS(struct client_data *client)
 {
@@ -1144,20 +1093,16 @@ void sendLUSERS(struct client_data *client)
        int invisible = 0;
        int ops = 0;
        int channels = 0;
-       map_find_t f = {0};
-       if (map_findfirst(&f, &client_map) == 1)
-               do {
-                       struct client_data *cl = f->value;
-                       registered++;
-                       if (cl->umode & UMODE_INVISIBLE)
-                               invisible++;
-                       if (cl->umode & UMODE_OPER)
-                               ops++;
-               } while (map_findnext(&f) == 1);
-       if (map_findfirst(&f, &client_map) == 1)
-               do {
-                       channels++;
-               } while (map_findnext(&f) == 1);
+       struct client_data *cl;
+       map_foreach(&client_map, 0, &cl) {
+               registered++;
+               if (cl->umode & UMODE_INVISIBLE)
+                       invisible++;
+               if (cl->umode & UMODE_OPER)
+                       ops++;
+       }
+       map_foreach(&channel_map, 0, 0)
+               channels++;
        for (struct client_data *cl = clients; cl; cl = cl->next)
                total++;
 
@@ -1218,23 +1163,21 @@ void setNick(struct client_data *client, char* nickname) {
                sendbuf(client);
                sendbuf_visible(client);
                map_unset(&client_map, client->key, NULL);
-               map_find_t f = {0};
-               if (map_findfirst(&f, &client->channels) == 1)
-                       do {
-                               struct channel *chan = f->value;
-                               map_unset(&chan->members, client->key, NULL);
-                               if (map_set(&chan->members, key, client, NULL) == -1)
+               struct channel *chan;
+               map_foreach(&client->channels, 0, &chan) {
+                       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();
-                               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();
-                       } while(map_findnext(&f) == 1);
+               }
                free(client->nick);
                free(client->key);
        }
@@ -1266,17 +1209,14 @@ void channel_sendtopic(struct client_data *client, struct channel *chan, int joi
 }
 void channel_sendnames(struct client_data *client, struct channel *chan)
 {
-       map_find_t f = {0};
        databuf.len = 0;
-       if (map_findfirst(&f, &chan->members) == 1)
-               do {
-                       struct client_data *cl = f->value;
-                       if (rpl_namreply(client, chan, cl)) {
-                               sendbuf(client);
-                               databuf.len = 0;
-                               rpl_namreply(client, chan, cl);
-                       }
-               } while(map_findnext(&f) == 1);
+       struct client_data *cl;
+       map_foreach(&chan->members, 0, &cl)
+               if (rpl_namreply(client, chan, cl)) {
+                       sendbuf(client);
+                       databuf.len = 0;
+                       rpl_namreply(client, chan, cl);
+               }
        if (rpl_namreply(client, chan, NULL))
                sendbuf(client);
        rpl_endofnames(client, chan->name);
@@ -1383,12 +1323,9 @@ void channel_part(struct client_data *client, struct channel *chan, const char *
 void channel_kill(struct channel *chan)
 {
        map_unset(&channel_map, chan->key, NULL);
-       map_find_t f = {0};
-       if (map_findfirst(&f, &chan->invitees) == 1)
-               do {
-                       struct client_data *cl = f->value;
-                       map_unset(&cl->invites, chan->key, NULL);
-               } while(map_findnext(&f) == 1);
+       struct client_data *cl;
+       map_foreach(&chan->invitees, 0, &cl)
+               map_unset(&cl->invites, chan->key, NULL);
        free(chan->key);
        free(chan->name);
        free(chan->topic);
index 1d90bb3..82fa512 100644 (file)
@@ -419,14 +419,8 @@ int main(int argc, char *argv[])
 #endif
        free(databuf.buf);
        /* free any persistent channels */
-       {
-               map_find_t f = {0};
-               while (map_findfirst(&f, &channel_map) == 1) {
-                       struct channel *chan = f->value;
-                       channel_kill(chan);
-               }
-               map_findfree(&f);
-       }
+       for (struct channel *chan; (chan = map_pop(&channel_map)); )
+               channel_kill(chan);
        map_free(&client_map);
        map_free(&channel_map);
        map_free(&autojoin);
index 0479bc5..751e8d2 100644 (file)
--- a/src/map.h
+++ b/src/map.h
 
 #ifdef USE_MAP_DYNARR
 /* all complexity is in terms of n = number of elements */
-struct map_dynarr_value {
-       char *key;
-       void *value;
-};
-struct map_dynarr {
-       struct map_dynarr_value *ptr;
+typedef struct map_dynarr {
+       struct map_dynarr_value {
+               char *key;
+               void *value;
+       } *ptr;
        size_t len, cap;
-};
-typedef struct map_dynarr map_t;
-struct map_dynarr_find {
-       char *key;
-       void *value;
-       map_t *_root;
-       size_t _index;
-};
-typedef struct map_dynarr_find *map_find_t;
-#define map_findfree(f) (free(*(f)), *(f) = 0)
+} map_t;
+typedef struct map_dynarr_iter {
+       map_t *root;
+       size_t index;
+} map_iter_t;
 #else
 /* all complexity is in terms of n = strlen(key) and s = size of the character set */
-struct trie_node {
+typedef struct map_trie_node {
        unsigned char key;
        unsigned char len;
        unsigned char cap;
        void *value;
-       struct trie_node *parent;
-       struct trie_node *data[1];
-};
-typedef struct trie_node *map_t;
+       struct map_trie_node *parent;
+       struct map_trie_node *data[1];
+} *map_t;
 #define TRIE_MAX_KEY 256
-struct trie_find {
-       struct trie_node *_n;
-       void *value;
-       size_t _len;
+typedef struct map_trie_iter {
+       map_t *root;
+       struct map_trie_node *n;
+       size_t len;
        char key[TRIE_MAX_KEY];
-};
-typedef struct trie_find *map_find_t;
-#define map_findfree(f) (free(*(f)), *(f) = 0)
+} map_iter_t;
 #endif
 
 /* A map abstract data type with following properties:
@@ -57,7 +48,6 @@ typedef struct trie_find *map_find_t;
  * - values are non-NULL pointers
  * - a NULL value is synonymous with absence of a key
  * - keys are owned by the map, values are not.
- * - traversal can fail with memory allocation errors (sadly)
  * - the map can't be modified during traversal
  *
  * A map is created by allocating and zero-initializing an object of type
@@ -82,37 +72,30 @@ NODISCARD int map_set(map_t *root, const char *key, const void *value, void **ol
  * Trie   complexity: O(ns)
  * Dynarr complexity: O(n) */
 void map_free(map_t *root);
-/* Traversal functions.
- * Return 1 on success, 0 on end, -1 on allocation failure.
- *
- * Some ground rules:
- * - map_find_t should be zero-initialized
- * - map_findfirst and map_findfree are idempotent
- * - when map_findfirst or map_findnext return 0 or -1, they automatically call
- *   map_findfree
+
+/* Iterate over a map.
  *
- * Non-modifying traversal:
- * map_find_t f = {0};
- * if (map_findfirst(&f, &map) == 1)
- *     do {
- *         value_type *v = f->value;
- *         // use f->key and v here
- *         if (gotta_break) {
- *             map_findfree(&f); // call this when breaking!
- *             break;
- *         }
- *     } while(map_findnext(&f) == 1);
+ * Do not modify the map while iterating.
+ * Don't modify *outkey or what it points to, and don't rely on it's lifetime
+ * being longer than one iteration.
+ * outvalue is actually a void**, but for implicit casting reasons it's void*.
+ * Both outkey and outvalue can be specified as NULL.
+ * 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 }; 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.
  *
- * Emptying traversal:
- * while (map_findfirst(&f, &map) == 1) {
- *     value_type *v = f->value;
- *     // use f->key and v here
- *     map_unset(&map, f->key, 0);
+ * Typical use:
+ * for (value_type *v; v = map_pop(&map); )
+ *     map_unset(&map, v->key, 0);
+ *     free(v);
  * }
- * map_findfree(&f);
- *
- * FIXME: yep, we're silently failing on allocation errors
+ * map_free(&map);
  */
-NODISCARD int map_findfirst(map_find_t *pf, map_t *root);
-NODISCARD int map_findnext(map_find_t *pf);
+NODISCARD void *map_pop(map_t *root);
 #endif
index 6e6cb1e..ed4e4e4 100644 (file)
@@ -74,33 +74,18 @@ void map_free(map_t *root)
        root->ptr = 0;
        root->len = root->cap = 0;
 }
-int map_findfirst(map_find_t *pf, map_t *root)
+int map_foriter(map_iter_t *it, const char **outkey, void *outvalue)
 {
-       if (!root->len)
+       if (it->index == it->root->len)
                return 0;
-       map_find_t f;
-       if (!*pf) {
-               f = malloc(sizeof(*f));
-               if (!f)
-                       return -1;
-               *pf = f;
-       } else {
-               f = *pf;
-       }
-       f->_root = root;
-       f->_index = -1;
-       return map_findnext(pf);
+       if (outkey)
+               *outkey = it->root->ptr[it->index].key;
+       if (outvalue)
+               *(void **)outvalue = it->root->ptr[it->index].value;
+       it->index++;
+       return 1;
 }
-int map_findnext(map_find_t *pf)
+void *map_pop(map_t *root)
 {
-       map_find_t f = *pf;
-       f->_index++;
-       if (f->_index == f->_root->len) {
-               *pf = 0;
-               free(f);
-               return 0;
-       }
-       f->key = f->_root->ptr[f->_index].key;
-       f->value = f->_root->ptr[f->_index].value;
-       return 1;
+       return root->len ? root->ptr[root->len-1].value : 0;
 }
index 2f2dc66..18c7705 100644 (file)
@@ -3,14 +3,14 @@
 #include <string.h>
 #define trie_is_end(e, n) ((e) >= (n)->len)
 #define trie_is_found(e, n, k) (!trie_is_end((e), (n)) && (n)->data[(e)]->key == (k))
-#define trie_node_size(cap) (sizeof(struct trie_node)+sizeof(struct trie_node*)*((cap)-1))
-#define trie_data_size(len) (sizeof(struct trie_node*)*(len))
+#define trie_node_size(cap) (sizeof(struct map_trie_node)+sizeof(struct map_trie_node*)*((cap)-1))
+#define trie_data_size(len) (sizeof(struct map_trie_node*)*(len))
 // finds the entry for a child node O(log s)
 // note that there are three cases to handle:
 // - past the end pointer (key doesn't exist, larger than any existing key)
 // - ent with different key (key doesn't exist, it should be inserted before this one)
 // - ent with matching key (key exists)
-static int ent_find(struct trie_node *n, int key)
+static int ent_find(struct map_trie_node *n, int key)
 {
        int lo = 0;
        int hi = n->len;
@@ -28,7 +28,7 @@ static int ent_find(struct trie_node *n, int key)
 // get value from trie O(n log s)
 void *map_get(map_t *root, const char *key)
 {
-       struct trie_node *n = *root;
+       struct map_trie_node *n = *root;
        if (!n)
                return NULL;
        while (*key) {
@@ -48,7 +48,7 @@ int map_set(map_t *root, const char *key, const void *value, void **oldvalue)
                return -1;
        if (oldvalue)
                *oldvalue = NULL;
-       struct trie_node *n = *root;
+       struct map_trie_node *n = *root;
        if (!n) { // create root node
                if (!value)
                        return 0;
@@ -65,7 +65,7 @@ int map_set(map_t *root, const char *key, const void *value, void **oldvalue)
                        if (!value) // ...unless we're deleting
                                return 0;
                        int cap = key[1] ? 1 : 0; // if it's a leaf, use capacity of 0
-                       struct trie_node *node = malloc(trie_node_size(cap));
+                       struct map_trie_node *node = malloc(trie_node_size(cap));
                        if (!node) {
                                rv = -1;
                                goto cleanup;
@@ -77,8 +77,8 @@ int map_set(map_t *root, const char *key, const void *value, void **oldvalue)
                                n->cap = n->cap*2 + 1; // 0 1 3 7 15 31 63 127 255
                                // we have to update parent's pointer, but ent_find needs that pointer to be valid.
                                // so we have to locate it *before* reallocing.
-                               struct trie_node **pent = n->parent ? &n->parent->data[ent_find(n->parent, n->key)] : root;
-                               struct trie_node *nn = realloc(n, trie_node_size(n->cap));
+                               struct map_trie_node **pent = n->parent ? &n->parent->data[ent_find(n->parent, n->key)] : root;
+                               struct map_trie_node *nn = realloc(n, trie_node_size(n->cap));
                                if (!nn) {
                                        free(node);
                                        rv = -1;
@@ -108,7 +108,7 @@ int map_set(map_t *root, const char *key, const void *value, void **oldvalue)
                while (!n->value && !n->len) { // free no longer used nodes
 cleanup:
                        if (n->parent) {
-                               struct trie_node *p = n->parent;
+                               struct map_trie_node *p = n->parent;
                                int ent = ent_find(p, n->key);
                                memmove(&p->data[ent], &p->data[ent+1], trie_data_size(--p->len - ent));
                                free(n);
@@ -124,60 +124,58 @@ cleanup:
 }
 // free a trie
 void map_free(map_t *root) {
-       struct trie_node *n = *root;
+       struct map_trie_node *n = *root;
        while (n) {
                if (n->len) {
                        n = n->data[--n->len];
                } else {
-                       struct trie_node *p = n->parent;
+                       struct map_trie_node *p = n->parent;
                        free(n);
                        n = p;
                }
        }
        *root = 0;
 }
-int map_findnext(map_find_t *pf) {
-       map_find_t f = *pf;
+int map_foriter(map_iter_t *f, const char **outkey, void *outvalue)
+{      
+       if (!f->n) {
+               if (!*f->root)
+                       return 0;
+               f->n = *f->root;
+               f->len = 0;
+               if (outkey)
+                       *outkey = f->key;
+               if (f->n->value) {
+                       f->key[f->len] = '\0';
+                       if (outvalue)
+                               *(void **)outvalue = f->n->value;
+                       return 1;
+               }
+       }
        int ent = 0; // go to the first child node
        for (;;) {
-               if (!trie_is_end(ent, f->_n)) {
-                       f->_n = f->_n->data[ent];
-                       f->key[f->_len++] = f->_n->key;
-                       if (f->_n->value) { // found a value
-                               f->key[f->_len] = '\0';
-                               f->value = f->_n->value;
+               if (!trie_is_end(ent, f->n)) {
+                       f->n = f->n->data[ent];
+                       f->key[f->len++] = f->n->key;
+                       if (f->n->value) { // found a value
+                               f->key[f->len] = '\0';
+                               if (outvalue)
+                                       *(void **)outvalue = f->n->value;
                                return 1;
                        }
                        ent = 0; // no value, go to the first child
                } else { // no more children, go to parent's next child
-                       f->_n = f->_n->parent;
-                       if (!f->_n) {
-                               *pf = NULL;
-                               free(f);
+                       f->n = f->n->parent;
+                       if (!f->n)
                                return 0;
-                       }
-                       f->_len--;
-                       ent = ent_find(f->_n, (unsigned char)f->key[f->_len] + 1);
+                       f->len--;
+                       ent = ent_find(f->n, (unsigned char)f->key[f->len] + 1);
                }
        }
 }
-int map_findfirst(map_find_t *pf, map_t *root) {
-       if (!*root)
-               return 0;
-       map_find_t f;
-       if (*pf) {
-               f = *pf;
-       } else {
-               f = *pf = malloc(sizeof(*f));
-               if (!f)
-                       return -1;
-       }
-       f->_n = *root;
-       f->_len = 0;
-       if (f->_n->value) {
-               f->key[f->_len] = '\0';
-               f->value = f->_n->value;
-               return 1;
-       }
-       return map_findnext(pf);
+void *map_pop(map_t *root)
+{
+       map_iter_t it = { .root = root };
+       void *value;
+       return map_foriter(&it, 0, &value) ? value : 0;
 }
index 2ebaed7..3e51cf4 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -64,21 +64,14 @@ void killConnection(struct client_data *cl, int byChoice)
        }
        update_poll_reg(cl);
        /* Part all channels */
-       map_find_t f = {0};
-       while (map_findfirst(&f, &cl->channels) == 1) {
-               struct channel *chan = f->value;
+       for (struct channel *chan; (chan = map_pop(&cl->channels)); )
                channel_part(cl, chan, NULL, NULL);
-       }
-       map_findfree(&f);
        /* Remove from client_map and get rid of the pending invites */
        if (cl->key) {
                map_unset(&client_map, cl->key, NULL);
-               map_find_t f = {0};
-               if (map_findfirst(&f, &cl->invites) == 1)
-                       do {
-                               struct channel *chan = f->value;
-                               map_unset(&chan->invitees, cl->key, NULL);
-                       } while(map_findnext(&f) == 1);
+               struct channel *chan;
+               map_foreach(&cl->invites, 0, &chan)
+                       map_unset(&chan->invitees, cl->key, NULL);
        }
        map_free(&cl->channels);
        map_free(&cl->invites);