}
}
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);
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);
}
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) {
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;
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) {
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];
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])
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) {
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;
}
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))
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]);
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)
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)
{
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++;
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);
}
}
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);
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);
#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:
* - 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
* 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
#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;
// 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) {
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;
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;
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;
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);
}
// 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;
}