]> PatchyGit - patchyircd.git/commitdiff
add alternative map impl, fix some memory leaks
authorEgor <egor@patchysicp.xyz>
Sun, 22 Oct 2023 15:46:21 +0000 (18:46 +0300)
committerEgor <egor@patchysicp.xyz>
Sun, 22 Oct 2023 15:46:21 +0000 (18:46 +0300)
12 files changed:
Makefile
libtls.suppressions [new file with mode: 0644]
src/common.h
src/irc.c
src/irc.h
src/main.c
src/map_dynarr.c [new file with mode: 0644]
src/map_dynarr.h [new file with mode: 0644]
src/net.c
src/trie.c
src/trie.h
vg [new file with mode: 0755]

index fe50f38..a964cf9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,25 @@
 TARGET := ircd
 SOURCES := \
-       src/main.c    \
-       src/irc.c     \
-       src/ircfmt.c  \
-       src/net.c     \
-       src/netpoll.c \
-       src/netsock.c \
-       src/passwd.c  \
-       src/trie.c    \
+       src/main.c       \
+       src/irc.c        \
+       src/ircfmt.c     \
+       src/net.c        \
+       src/netpoll.c    \
+       src/netsock.c    \
+       src/passwd.c     \
        src/util.c
+SOURCES += src/trie.c
 HEADERS := \
-       src/common.h  \
-       src/config.h  \
-       src/irc.h     \
-       src/ircfmt.h  \
-       src/net.h     \
-       src/netpoll.h \
-       src/netsock.h \
-       src/passwd.h  \
-       src/trie.h    \
+       src/common.h     \
+       src/config.h     \
+       src/irc.h        \
+       src/ircfmt.h     \
+       src/net.h        \
+       src/netpoll.h    \
+       src/netsock.h    \
+       src/passwd.h     \
+       src/trie.h       \
+       src/map_dynarr.h \
        src/util.h
 
 -include config.mk
diff --git a/libtls.suppressions b/libtls.suppressions
new file mode 100644 (file)
index 0000000..21be759
--- /dev/null
@@ -0,0 +1,8 @@
+{
+   <tls_init>
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   ...
+   fun:tls_init
+   fun:main
+}
index b7209c6..12da8c8 100644 (file)
@@ -3,7 +3,11 @@
 #include "netsock.h"
 #include "netpoll.h"
 #include "config.h"
+#ifdef USE_MAP_DYNARR
+#include "map_dynarr.h"
+#else
 #include "trie.h"
+#endif
 #include <time.h>
 #ifdef HAS_TLS
 #include <tls.h>
@@ -48,8 +52,8 @@ struct client_data {
        size_t wbufLen;
        char   umode;
        char   regstate;
-       struct trie_node *channels;
-       struct trie_node *invites;
+       map_t  channels;
+       map_t  invites;
        int    cookie;
        time_t signon;
        time_t lastactivity;
@@ -70,18 +74,18 @@ struct channel {
        char *topic_who;
        time_t topic_when;
        time_t creation;
-       struct trie_node *members;
-       struct trie_node *o;
-       struct trie_node *v;
+       map_t members;
+       map_t o;
+       map_t v;
        int member_count;
        char dmode;
-       struct trie_node *listb;
-       struct trie_node *liste;
-       struct trie_node *listI;
-       struct trie_node *listq;
-       struct trie_node *listwo;
-       struct trie_node *listwv;
-       struct trie_node *invitees;
+       map_t listb;
+       map_t liste;
+       map_t listI;
+       map_t listq;
+       map_t listwo;
+       map_t listwv;
+       map_t invitees;
        char *chankey;
        int member_limit;
 };
@@ -93,9 +97,9 @@ struct channel {
 #define DMODE_TOPICPROT 32
 #define is_chanop(chan, client) (!!trie_get(&(chan)->o, (client)->key))
 #define is_chanvoice(chan, client) (!!trie_get(&(chan)->v, (client)->key))
-extern struct trie_node *client_map;
-extern struct trie_node *channel_map;
-extern struct trie_node *autojoin;
+extern map_t client_map;
+extern map_t channel_map;
+extern map_t autojoin;
 extern struct client_data  *clients;
 extern struct client_data  *dyingclients;
 
index dc03c14..dd4f3c7 100644 (file)
--- a/src/irc.c
+++ b/src/irc.c
@@ -4,9 +4,9 @@
 
 struct dynarr databuf;
 
-struct trie_node *client_map = NULL;
-struct trie_node *channel_map = NULL;
-struct trie_node *autojoin = NULL;
+map_t client_map = {0};
+map_t channel_map = {0};
+map_t autojoin = {0};
 
 const char *hostname = IRCDHOSTNAME;
 #define is_registered(cl) ((cl)->regstate == REG_REGISTERED)
@@ -39,7 +39,7 @@ void sendbuf_chan_caps(struct channel *chan, unsigned positive, unsigned negativ
 void sendbuf_chan(struct channel *chan) {
        sendbuf_chan_caps(chan, 0, 0);
 }
-void sendbuf_list_sans_self_caps(struct client_data *client, struct trie_node **ls, int cookie, int positive, int negative) {
+void sendbuf_list_sans_self_caps(struct client_data *client, map_t *ls, int cookie, int positive, int negative) {
        struct trie_find *f = NULL;
        if (trie_findfirst(&f, ls) == 1)
                do {
@@ -56,7 +56,7 @@ void sendbuf_list_sans_self_caps(struct client_data *client, struct trie_node **
                        }
                } while (trie_findnext(&f) == 1);
 }
-void sendbuf_list_sans_self(struct client_data *client, struct trie_node **ls, int cookie) {
+void sendbuf_list_sans_self(struct client_data *client, map_t *ls, int cookie) {
        sendbuf_list_sans_self_caps(client, ls, cookie, 0, 0);
 }
 int get_cookie() {
@@ -98,7 +98,7 @@ const char *format_umodes(struct client_data *cl) {
        *p++ = '\0';
        return umodes;
 }
-int match_list(struct client_data *cl, struct trie_node **list)
+int match_list(struct client_data *cl, map_t *list)
 {
        struct trie_find *f = NULL;
        if (trie_findfirst(&f, list) == 1)
@@ -796,7 +796,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a
                                        case '-': is_plus = 0; break;
                                        default: goto unknown;
                                }
-                               struct trie_node **list;
+                               map_t *list;
                                char **argp = &args[2];
                                while (*p) {
                                        switch (*p) {
@@ -1370,31 +1370,34 @@ void channel_part(struct client_data *client, struct channel *chan, const char *
        trie_set(&chan->o, client->key, NULL, NULL);
        trie_set(&chan->v, client->key, NULL, NULL);
        chan->member_count--;
-       if (!chan->member_count && !(chan->dmode & DMODE_PERSIST)) {
-               trie_set(&channel_map, chan->key, NULL, NULL);
-               struct trie_find *f = NULL;
-               if (trie_findfirst(&f, &chan->invitees) == 1)
-                       do {
-                               struct client_data *cl = f->n->value;
-                               trie_set(&cl->invites, chan->key, NULL, NULL);
-                       } while(trie_findnext(&f) == 1);
-               free(chan->key);
-               free(chan->name);
-               free(chan->topic);
-               free(chan->topic_who);
-               trie_free(&chan->members);
-               trie_free(&chan->o);
-               trie_free(&chan->v);
-               trie_free(&chan->listb);
-               trie_free(&chan->liste);
-               trie_free(&chan->listI);
-               trie_free(&chan->listq);
-               trie_free(&chan->listwo);
-               trie_free(&chan->listwv);
-               trie_free(&chan->invitees);
-               free(chan->chankey);
-               free(chan);
-       }
+       if (!chan->member_count && !(chan->dmode & DMODE_PERSIST))
+               channel_kill(chan);
+}
+void channel_kill(struct channel *chan)
+{
+       trie_set(&channel_map, chan->key, NULL, NULL);
+       struct trie_find *f = NULL;
+       if (trie_findfirst(&f, &chan->invitees) == 1)
+               do {
+                       struct client_data *cl = f->n->value;
+                       trie_set(&cl->invites, chan->key, NULL, NULL);
+               } while(trie_findnext(&f) == 1);
+       free(chan->key);
+       free(chan->name);
+       free(chan->topic);
+       free(chan->topic_who);
+       trie_free(&chan->members);
+       trie_free(&chan->o);
+       trie_free(&chan->v);
+       trie_free(&chan->listb);
+       trie_free(&chan->liste);
+       trie_free(&chan->listI);
+       trie_free(&chan->listq);
+       trie_free(&chan->listwo);
+       trie_free(&chan->listwv);
+       trie_free(&chan->invitees);
+       free(chan->chankey);
+       free(chan);
 }
 struct client_data *findClient(const char *nickname)
 {
index 0a3ada2..9272c82 100644 (file)
--- a/src/irc.h
+++ b/src/irc.h
@@ -17,5 +17,6 @@ void channel_sendtopic(struct client_data *client, struct channel *chan, int joi
 void channel_sendnames(struct client_data *client, struct channel *chan);
 void channel_join(struct client_data *client, const char *channel, const char *chankey);
 void channel_part(struct client_data *client, struct channel *chan, const char *reason, struct client_data *kicker);
+void channel_kill(struct channel *chan);
 extern struct dynarr databuf;
 #endif
index 3a36e1f..60c0a10 100644 (file)
@@ -417,6 +417,19 @@ int main(int argc, char *argv[])
        tls_free(tls_ctx);
 #endif
        free(databuf.buf);
+       /* free any persisten channels */
+       {
+               struct trie_find *f = NULL;
+               while (trie_findfirst(&f, &channel_map) == 1) {
+                       struct channel *chan = f->n->value;
+                       channel_kill(chan);
+               }
+               free(f);
+       }
+       trie_free(&client_map);
+       trie_free(&channel_map);
        trie_free(&autojoin);
+       if (fchanlog)
+               fclose(fchanlog);
        return 0;
 }
diff --git a/src/map_dynarr.c b/src/map_dynarr.c
new file mode 100644 (file)
index 0000000..06fa881
--- /dev/null
@@ -0,0 +1,130 @@
+#include "map_dynarr.h"
+#include <stdlib.h>
+#include <string.h>
+static int ent_find(map_t *root, const char *key, int *found)
+{
+       int lo = 0;
+       int hi = root->len;
+       while (lo != hi) {
+               int mid = (lo+hi)/2;
+               int cmp = strcmp(root->ptr[mid].key, key);
+               if (cmp == 0) {
+                       *found = 1;
+                       return mid;
+               } if (cmp > 0) {
+                       hi = mid;
+               } else {
+                       lo = mid+1;
+               }
+       }
+       *found = 0;
+       return lo;
+}
+void *trie_get(map_t *root, const char *key)
+{
+       int found;
+       int i = ent_find(root, key, &found);
+       return found ? root->ptr[i].value : 0;
+}
+int trie_set(map_t *root, const char *key, const void *value, void **oldvalue)
+{
+       int found;
+       int i = ent_find(root, key, &found);
+       if (found) {
+               if (oldvalue)
+                       *oldvalue = root->ptr[i].value;
+               if (!value) {
+                       free(root->ptr[i].key);
+                       memmove(root->ptr+i, root->ptr+i+1, sizeof(struct map_dynarr_value)*(root->len-(i+1)));
+                       root->len--;
+               } else {
+                       root->ptr[i].value = (void *)value;
+               }
+       } else {
+               if (oldvalue)
+                       *oldvalue = 0;
+               if (value) {
+                       if (root->len == root->cap) {
+                               int ncap = root->cap ? root->cap*2 : 2;
+                               struct map_dynarr_value *nptr = realloc(root->ptr, sizeof(struct map_dynarr_value)*ncap);
+                               if (!nptr)
+                                       return -1;
+                               root->ptr = nptr;
+                               root->cap = ncap;
+                       }
+                       int slen = strlen(key)+1;
+                       char *nkey = malloc(slen);
+                       if (!nkey)
+                               return -1;
+                       memcpy(nkey, key, slen);
+                       memmove(root->ptr+i+1, root->ptr+i, sizeof(struct map_dynarr_value)*(root->len-i));
+                       root->len++;
+                       root->ptr[i].key = nkey;
+                       root->ptr[i].value = (void *)value;
+               }
+       }
+       return 0;
+}
+void trie_free(map_t *root)
+{
+       while (root->len) {
+               free(root->ptr[--root->len].key);
+       }
+       free(root->ptr);
+       root->ptr = 0;
+       root->len = root->cap = 0;
+}
+static int fill(struct trie_find **pf, int index)
+{
+       struct trie_find *f = *pf;
+       int keylen = strlen(f->_root->ptr[index].key)+1;
+       if (keylen > f->_cap) {
+               while (keylen > f->_cap)
+                       f->_cap *= 2;
+               struct trie_find *nf = realloc(f, sizeof(struct trie_find)+f->_cap);
+               if (!nf) {
+                       *pf = 0;
+                       free(f);
+                       return -1;
+               }
+               *pf = f = nf;
+               f->key = (char*)&f[1];
+               f->n = &f->_n;
+       }
+       memcpy(f->key, f->_root->ptr[index].key, keylen);
+       f->n->value = f->_root->ptr[index].value;
+       return 1;
+}
+int trie_findfirst(struct trie_find **pf, map_t *root)
+{
+       if (!root->len)
+               return 0;
+       struct trie_find *f;
+       if (!*pf) {
+               f = malloc(sizeof(struct trie_find)+32);
+               if (!f)
+                       return -1;
+               f->key = (char*)&f[1];
+               f->key[0] = 0;
+               f->n = &f->_n;
+               f->_cap = 32;
+               *pf = f;
+       } else {
+               f = *pf;
+       }
+       f->_root = root;
+       return fill(pf, 0);
+}
+int trie_findnext(struct trie_find **pf)
+{
+       struct trie_find *f = *pf;
+       int found;
+       int i = ent_find(f->_root, f->key, &found);
+       i += found;
+       if (f->_root->len == (size_t)i) {
+               *pf = 0;
+               free(f);
+               return 0;
+       }
+       return fill(pf, i);
+}
diff --git a/src/map_dynarr.h b/src/map_dynarr.h
new file mode 100644 (file)
index 0000000..eba2332
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef MAP_DYNARR_H_
+#define MAP_DYNARR_H_
+#include <stddef.h>
+struct map_dynarr_value {
+       char *key;
+       void *value;
+};
+struct map_dynarr {
+       struct map_dynarr_value *ptr;
+       size_t len, cap;
+};
+typedef struct map_dynarr map_t;
+void *trie_get(map_t *root, const char *key);
+int trie_set(map_t *root, const char *key, const void *value, void **oldvalue);
+void trie_free(map_t *root);
+struct trie_find {
+       char *key;
+       struct { void *value; } *n, _n;
+       map_t *_root;
+       int _cap;
+};
+int trie_findfirst(struct trie_find **pf, map_t *root);
+int trie_findnext(struct trie_find **pf);
+#endif
index 347dfe6..83d90db 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -65,12 +65,12 @@ void killConnection(struct client_data *cl, int byChoice)
        update_poll_reg(cl);
        /* Part all channels */
        struct trie_find *f = NULL;
-       if (trie_findfirst(&f, &cl->channels) == 1)
-               do {
-                       struct channel *chan = f->n->value;
-                       channel_part(cl, chan, NULL, NULL);
-               } while(trie_findfirst(&f, &cl->channels) == 1);
-       /* Get rid of the pending invites */
+       while (trie_findfirst(&f, &cl->channels) == 1) {
+               struct channel *chan = f->n->value;
+               channel_part(cl, chan, NULL, NULL);
+       }
+       free(f);
+       /* Remove from client_map and get rid of the pending invites */
        if (cl->key) {
                trie_set(&client_map, cl->key, NULL, NULL);
                struct trie_find *f = NULL;
@@ -80,6 +80,8 @@ void killConnection(struct client_data *cl, int byChoice)
                                trie_set(&chan->invitees, cl->key, NULL, NULL);
                        } while(trie_findnext(&f) == 1);
        }
+       trie_free(&cl->channels);
+       trie_free(&cl->invites);
        killConnectionPart2(cl);
 }
 void killConnectionPart2(struct client_data *cl)
index efb7fa9..630a172 100644 (file)
@@ -26,7 +26,7 @@ static int ent_find(struct trie_node *n, int key)
        return lo;
 }
 // get value from trie O(n log s)
-void *trie_get(struct trie_node **root, const char *key)
+void *trie_get(map_t *root, const char *key)
 {
        struct trie_node *n = *root;
        if (!n)
@@ -41,7 +41,7 @@ void *trie_get(struct trie_node **root, const char *key)
        return n->value;
 }
 // set value in trie O(ns)
-int trie_set(struct trie_node **root, const char *key, const void *value, void **oldvalue)
+int trie_set(map_t *root, const char *key, const void *value, void **oldvalue)
 {
        int rv = 0;
        if (oldvalue)
@@ -119,7 +119,7 @@ cleanup:
        return rv;
 }
 // free a trie
-void trie_free(struct trie_node **root) {
+void trie_free(map_t *root) {
        struct trie_node *n = *root;
        while (n) {
                if (n->len) {
@@ -165,7 +165,7 @@ int trie_findnext(struct trie_find **pf) {
                }
        }
 }
-int trie_findfirst(struct trie_find **pf, struct trie_node **root) {
+int trie_findfirst(struct trie_find **pf, map_t *root) {
        if (!*root)
                return 0;
        struct trie_find *f;
index 513f496..ef21dbd 100644 (file)
@@ -13,19 +13,20 @@ struct trie_node {
        struct trie_node *parent;
        struct trie_node *data[1];
 };
+typedef struct trie_node *map_t;
 // get value from trie O(n log s)
-void *trie_get(struct trie_node **root, const char *key);
+void *trie_get(map_t *root, const char *key);
 // set value in trie O(ns)
-int trie_set(struct trie_node **root, const char *key, const void *value, void **oldvalue);
+int trie_set(map_t *root, const char *key, const void *value, void **oldvalue);
 // free a trie
-void trie_free(struct trie_node **root);
+void trie_free(map_t *root);
 struct trie_find {
        struct trie_node *n;
        size_t len;
        size_t cap;
        char key[1];
 };
-int trie_findfirst(struct trie_find **pf, struct trie_node **root);
+int trie_findfirst(struct trie_find **pf, map_t *root);
 int trie_findnext(struct trie_find **pf);
 #ifdef __cplusplus
 }
diff --git a/vg b/vg
new file mode 100755 (executable)
index 0000000..8136e72
--- /dev/null
+++ b/vg
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec valgrind --leak-check=full --show-leak-kinds=all -s --suppressions=libtls.suppressions ./ircd