Add back shared{} module flag which I managed to lose in a git-fubar...
[seven-1.x.git] / src / hash.c
blob548b206dcae571466f31299f29850aef341764fe
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * hash.c: Maintains hashtables.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
24 * $Id: hash.c 108 2006-10-14 18:12:28Z spb $
27 #include "stdinc.h"
28 #include "ircd_defs.h"
29 #include "tools.h"
30 #include "s_conf.h"
31 #include "channel.h"
32 #include "client.h"
33 #include "common.h"
34 #include "hash.h"
35 #include "irc_string.h"
36 #include "ircd.h"
37 #include "numeric.h"
38 #include "send.h"
39 #include "memory.h"
40 #include "msg.h"
41 #include "cache.h"
42 #include "s_newconf.h"
44 dlink_list *clientTable;
45 dlink_list *channelTable;
46 dlink_list *idTable;
47 dlink_list *resvTable;
48 dlink_list *hostTable;
49 dlink_list *helpTable;
50 dlink_list *ndTable;
53 * look in whowas.c for the missing ...[WW_MAX]; entry
57 * Hashing.
59 * The server uses a chained hash table to provide quick and efficient
60 * hash table maintenance (providing the hash function works evenly over
61 * the input range). The hash table is thus not susceptible to problems
62 * of filling all the buckets or the need to rehash.
63 * It is expected that the hash table would look something like this
64 * during use:
65 * +-----+ +-----+ +-----+ +-----+
66 * ---| 224 |----| 225 |----| 226 |---| 227 |---
67 * +-----+ +-----+ +-----+ +-----+
68 * | | |
69 * +-----+ +-----+ +-----+
70 * | A | | C | | D |
71 * +-----+ +-----+ +-----+
72 * |
73 * +-----+
74 * | B |
75 * +-----+
77 * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
79 * The order shown above is just one instant of the server.
82 * The hash functions currently used are based Fowler/Noll/Vo hashes
83 * which work amazingly well and have a extremely low collision rate
84 * For more info see http://www.isthe.com/chongo/tech/comp/fnv/index.html
89 /* init_hash()
91 * clears the various hashtables
93 void
94 init_hash(void)
96 clientTable = MyMalloc(sizeof(dlink_list) * U_MAX);
97 idTable = MyMalloc(sizeof(dlink_list) * U_MAX);
98 ndTable = MyMalloc(sizeof(dlink_list) * U_MAX);
99 channelTable = MyMalloc(sizeof(dlink_list) * CH_MAX);
100 hostTable = MyMalloc(sizeof(dlink_list) * HOST_MAX);
101 resvTable = MyMalloc(sizeof(dlink_list) * R_MAX);
102 helpTable = MyMalloc(sizeof(dlink_list) * HELP_MAX);
105 #ifndef RICER_HASHING
106 u_int32_t
107 fnv_hash_upper(const unsigned char *s, int bits)
109 u_int32_t h = FNV1_32_INIT;
111 while (*s)
113 h ^= ToUpper(*s++);
114 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
116 h = (h >> bits) ^ (h & ((2^bits)-1));
117 return h;
120 u_int32_t
121 fnv_hash(const unsigned char *s, int bits)
123 u_int32_t h = FNV1_32_INIT;
125 while (*s)
127 h ^= *s++;
128 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
130 h = (h >> bits) ^ (h & ((2^bits)-1));
131 return h;
134 u_int32_t
135 fnv_hash_len(const unsigned char *s, int bits, int len)
137 u_int32_t h = FNV1_32_INIT;
138 const unsigned char *x = s + len;
139 while (*s && s < x)
141 h ^= *s++;
142 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
144 h = (h >> bits) ^ (h & ((2^bits)-1));
145 return h;
148 u_int32_t
149 fnv_hash_upper_len(const unsigned char *s, int bits, int len)
151 u_int32_t h = FNV1_32_INIT;
152 const unsigned char *x = s + len;
153 while (*s && s < x)
155 h ^= ToUpper(*s++);
156 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
158 h = (h >> bits) ^ (h & ((2^bits)-1));
159 return h;
161 #endif
163 /* hash_nick()
165 * hashes a nickname, first converting to lowercase
167 static u_int32_t
168 hash_nick(const char *name)
170 return fnv_hash_upper((const unsigned char *) name, U_MAX_BITS);
173 /* hash_id()
175 * hashes an id, case is kept
177 static u_int32_t
178 hash_id(const char *name)
180 return fnv_hash((const unsigned char *) name, U_MAX_BITS);
183 /* hash_channel()
185 * hashes a channel name, based on first 30 chars only for efficiency
187 static u_int32_t
188 hash_channel(const char *name)
190 return fnv_hash_upper_len((const unsigned char *) name, CH_MAX_BITS, 30);
193 /* hash_hostname()
195 * hashes a hostname, based on first 30 chars only, as thats likely to
196 * be more dynamic than rest.
198 static u_int32_t
199 hash_hostname(const char *name)
201 return fnv_hash_upper_len((const unsigned char *) name, HOST_MAX_BITS, 30);
204 /* hash_resv()
206 * hashes a resv channel name, based on first 30 chars only
208 static u_int32_t
209 hash_resv(const char *name)
211 return fnv_hash_upper_len((const unsigned char *) name, R_MAX_BITS, 30);
214 static unsigned int
215 hash_help(const char *name)
217 unsigned int h = 0;
219 while(*name)
221 h += (unsigned int) (ToLower(*name++) & 0xDF);
224 return (h % HELP_MAX);
227 /* add_to_id_hash()
229 * adds an entry to the id hash table
231 void
232 add_to_id_hash(const char *name, struct Client *client_p)
234 unsigned int hashv;
236 if(EmptyString(name) || (client_p == NULL))
237 return;
239 hashv = hash_id(name);
240 dlinkAddAlloc(client_p, &idTable[hashv]);
243 /* add_to_client_hash()
245 * adds an entry (client/server) to the client hash table
247 void
248 add_to_client_hash(const char *name, struct Client *client_p)
250 unsigned int hashv;
252 s_assert(name != NULL);
253 s_assert(client_p != NULL);
254 if(EmptyString(name) || (client_p == NULL))
255 return;
257 hashv = hash_nick(name);
258 dlinkAddAlloc(client_p, &clientTable[hashv]);
261 /* add_to_hostname_hash()
263 * adds a client entry to the hostname hash table
265 void
266 add_to_hostname_hash(const char *hostname, struct Client *client_p)
268 unsigned int hashv;
270 s_assert(hostname != NULL);
271 s_assert(client_p != NULL);
272 if(EmptyString(hostname) || (client_p == NULL))
273 return;
275 hashv = hash_hostname(hostname);
276 dlinkAddAlloc(client_p, &hostTable[hashv]);
279 /* add_to_resv_hash()
281 * adds a resv channel entry to the resv hash table
283 void
284 add_to_resv_hash(const char *name, struct ConfItem *aconf)
286 unsigned int hashv;
288 s_assert(!EmptyString(name));
289 s_assert(aconf != NULL);
290 if(EmptyString(name) || aconf == NULL)
291 return;
293 hashv = hash_resv(name);
294 dlinkAddAlloc(aconf, &resvTable[hashv]);
297 void
298 add_to_help_hash(const char *name, struct cachefile *hptr)
300 unsigned int hashv;
302 if(EmptyString(name) || hptr == NULL)
303 return;
305 hashv = hash_help(name);
306 dlinkAddAlloc(hptr, &helpTable[hashv]);
309 void
310 add_to_nd_hash(const char *name, struct nd_entry *nd)
312 nd->hashv = hash_nick(name);
313 dlinkAdd(nd, &nd->hnode, &ndTable[nd->hashv]);
316 /* del_from_id_hash()
318 * removes an id from the id hash table
320 void
321 del_from_id_hash(const char *id, struct Client *client_p)
323 unsigned int hashv;
325 s_assert(id != NULL);
326 s_assert(client_p != NULL);
327 if(EmptyString(id) || client_p == NULL)
328 return;
330 hashv = hash_id(id);
331 dlinkFindDestroy(client_p, &idTable[hashv]);
334 /* del_from_client_hash()
336 * removes a client/server from the client hash table
338 void
339 del_from_client_hash(const char *name, struct Client *client_p)
341 unsigned int hashv;
343 /* no s_asserts, this can happen when removing a client that
344 * is unregistered.
346 if(EmptyString(name) || client_p == NULL)
347 return;
349 hashv = hash_nick(name);
350 dlinkFindDestroy(client_p, &clientTable[hashv]);
353 /* del_from_channel_hash()
355 * removes a channel from the channel hash table
357 void
358 del_from_channel_hash(const char *name, struct Channel *chptr)
360 unsigned int hashv;
362 s_assert(name != NULL);
363 s_assert(chptr != NULL);
365 if(EmptyString(name) || chptr == NULL)
366 return;
368 hashv = hash_channel(name);
369 dlinkFindDestroy(chptr, &channelTable[hashv]);
372 /* del_from_hostname_hash()
374 * removes a client entry from the hostname hash table
376 void
377 del_from_hostname_hash(const char *hostname, struct Client *client_p)
379 unsigned int hashv;
381 if(hostname == NULL || client_p == NULL)
382 return;
384 hashv = hash_hostname(hostname);
386 dlinkFindDestroy(client_p, &hostTable[hashv]);
389 /* del_from_resv_hash()
391 * removes a resv entry from the resv hash table
393 void
394 del_from_resv_hash(const char *name, struct ConfItem *aconf)
396 unsigned int hashv;
398 s_assert(name != NULL);
399 s_assert(aconf != NULL);
400 if(EmptyString(name) || aconf == NULL)
401 return;
403 hashv = hash_resv(name);
405 dlinkFindDestroy(aconf, &resvTable[hashv]);
408 void
409 clear_help_hash(void)
411 dlink_node *ptr;
412 dlink_node *next_ptr;
413 int i;
415 HASH_WALK_SAFE(i, HELP_MAX, ptr, next_ptr, helpTable)
417 free_cachefile(ptr->data);
418 dlinkDestroy(ptr, &helpTable[i]);
420 HASH_WALK_END
423 /* find_id()
425 * finds a client entry from the id hash table
427 struct Client *
428 find_id(const char *name)
430 struct Client *target_p;
431 dlink_node *ptr;
432 unsigned int hashv;
434 if(EmptyString(name))
435 return NULL;
437 hashv = hash_id(name);
439 DLINK_FOREACH(ptr, idTable[hashv].head)
441 target_p = ptr->data;
443 if(strcmp(name, target_p->id) == 0)
444 return target_p;
447 return NULL;
450 /* hash_find_masked_server()
452 * Whats happening in this next loop ? Well, it takes a name like
453 * foo.bar.edu and proceeds to earch for *.edu and then *.bar.edu.
454 * This is for checking full server names against masks although
455 * it isnt often done this way in lieu of using matches().
457 * Rewrote to do *.bar.edu first, which is the most likely case,
458 * also made const correct
459 * --Bleep
461 static struct Client *
462 hash_find_masked_server(struct Client *source_p, const char *name)
464 char buf[HOSTLEN + 1];
465 char *p = buf;
466 char *s;
467 struct Client *server;
469 if('*' == *name || '.' == *name)
470 return NULL;
472 /* copy it across to give us a buffer to work on */
473 strlcpy(buf, name, sizeof(buf));
475 while ((s = strchr(p, '.')) != 0)
477 *--s = '*';
479 * Dont need to check IsServer() here since nicknames cant
480 * have *'s in them anyway.
482 if((server = find_server(source_p, s)))
483 return server;
484 p = s + 2;
487 return NULL;
490 /* find_any_client()
492 * finds a client/server/masked server entry from the hash
494 struct Client *
495 find_any_client(const char *name)
497 struct Client *target_p;
498 dlink_node *ptr;
499 unsigned int hashv;
501 s_assert(name != NULL);
502 if(EmptyString(name))
503 return NULL;
505 /* hunting for an id, not a nick */
506 if(IsDigit(*name))
507 return (find_id(name));
509 hashv = hash_nick(name);
511 DLINK_FOREACH(ptr, clientTable[hashv].head)
513 target_p = ptr->data;
515 if(irccmp(name, target_p->name) == 0)
516 return target_p;
519 /* wasnt found, look for a masked server */
520 return hash_find_masked_server(NULL, name);
523 /* find_client()
525 * finds a client/server entry from the client hash table
527 struct Client *
528 find_client(const char *name)
530 struct Client *target_p;
531 dlink_node *ptr;
532 unsigned int hashv;
534 s_assert(name != NULL);
535 if(EmptyString(name))
536 return NULL;
538 /* hunting for an id, not a nick */
539 if(IsDigit(*name))
540 return (find_id(name));
542 hashv = hash_nick(name);
544 DLINK_FOREACH(ptr, clientTable[hashv].head)
546 target_p = ptr->data;
548 if(irccmp(name, target_p->name) == 0)
549 return target_p;
552 return NULL;
555 /* find_named_client()
557 * finds a client/server entry from the client hash table
559 struct Client *
560 find_named_client(const char *name)
562 struct Client *target_p;
563 dlink_node *ptr;
564 unsigned int hashv;
566 s_assert(name != NULL);
567 if(EmptyString(name))
568 return NULL;
570 hashv = hash_nick(name);
572 DLINK_FOREACH(ptr, clientTable[hashv].head)
574 target_p = ptr->data;
576 if(irccmp(name, target_p->name) == 0)
577 return target_p;
580 return NULL;
583 /* find_server()
585 * finds a server from the client hash table
587 struct Client *
588 find_server(struct Client *source_p, const char *name)
590 struct Client *target_p;
591 dlink_node *ptr;
592 unsigned int hashv;
594 if(EmptyString(name))
595 return NULL;
597 if((source_p == NULL || !MyClient(source_p)) &&
598 IsDigit(*name) && strlen(name) == 3)
600 target_p = find_id(name);
601 return(target_p);
604 hashv = hash_nick(name);
606 DLINK_FOREACH(ptr, clientTable[hashv].head)
608 target_p = ptr->data;
610 if((IsServer(target_p) || IsMe(target_p)) &&
611 irccmp(name, target_p->name) == 0)
612 return target_p;
615 /* wasnt found, look for a masked server */
616 return hash_find_masked_server(source_p, name);
619 /* find_hostname()
621 * finds a hostname dlink list from the hostname hash table.
622 * we return the full dlink list, because you can have multiple
623 * entries with the same hostname
625 dlink_node *
626 find_hostname(const char *hostname)
628 unsigned int hashv;
630 if(EmptyString(hostname))
631 return NULL;
633 hashv = hash_hostname(hostname);
635 return hostTable[hashv].head;
638 /* find_channel()
640 * finds a channel from the channel hash table
642 struct Channel *
643 find_channel(const char *name)
645 struct Channel *chptr;
646 dlink_node *ptr;
647 unsigned int hashv;
649 s_assert(name != NULL);
650 if(EmptyString(name))
651 return NULL;
653 hashv = hash_channel(name);
655 DLINK_FOREACH(ptr, channelTable[hashv].head)
657 chptr = ptr->data;
659 if(irccmp(name, chptr->chname) == 0)
660 return chptr;
663 return NULL;
667 * get_or_create_channel
668 * inputs - client pointer
669 * - channel name
670 * - pointer to int flag whether channel was newly created or not
671 * output - returns channel block or NULL if illegal name
672 * - also modifies *isnew
674 * Get Channel block for chname (and allocate a new channel
675 * block, if it didn't exist before).
677 struct Channel *
678 get_or_create_channel(struct Client *client_p, const char *chname, int *isnew)
680 struct Channel *chptr;
681 dlink_node *ptr;
682 unsigned int hashv;
683 int len;
684 const char *s = chname;
686 if(EmptyString(s))
687 return NULL;
689 len = strlen(s);
690 if(len > CHANNELLEN)
692 char *t;
693 if(IsServer(client_p))
695 sendto_realops_snomask(SNO_DEBUG, L_NETWIDE,
696 "*** Long channel name from %s (%d > %d): %s",
697 client_p->name, len, CHANNELLEN, s);
699 len = CHANNELLEN;
700 t = LOCAL_COPY(s);
701 *(t + CHANNELLEN) = '\0';
702 s = t;
705 hashv = hash_channel(s);
707 DLINK_FOREACH(ptr, channelTable[hashv].head)
709 chptr = ptr->data;
711 if(irccmp(s, chptr->chname) == 0)
713 if(isnew != NULL)
714 *isnew = 0;
715 return chptr;
719 if(isnew != NULL)
720 *isnew = 1;
722 chptr = allocate_channel(s);
724 dlinkAdd(chptr, &chptr->node, &global_channel_list);
726 chptr->channelts = CurrentTime; /* doesn't hurt to set it here */
728 dlinkAddAlloc(chptr, &channelTable[hashv]);
730 return chptr;
733 /* hash_find_resv()
735 * hunts for a resv entry in the resv hash table
737 struct ConfItem *
738 hash_find_resv(const char *name)
740 struct ConfItem *aconf;
741 dlink_node *ptr;
742 unsigned int hashv;
744 s_assert(name != NULL);
745 if(EmptyString(name))
746 return NULL;
748 hashv = hash_resv(name);
750 DLINK_FOREACH(ptr, resvTable[hashv].head)
752 aconf = ptr->data;
754 if(!irccmp(name, aconf->name))
756 aconf->port++;
757 return aconf;
761 return NULL;
764 struct cachefile *
765 hash_find_help(const char *name, int flags)
767 struct cachefile *hptr;
768 dlink_node *ptr;
769 unsigned int hashv;
771 if(EmptyString(name))
772 return NULL;
774 hashv = hash_help(name);
776 DLINK_FOREACH(ptr, helpTable[hashv].head)
778 hptr = ptr->data;
780 if((irccmp(name, hptr->name) == 0) &&
781 (hptr->flags & flags))
782 return hptr;
785 return NULL;
788 void
789 clear_resv_hash(void)
791 struct ConfItem *aconf;
792 dlink_node *ptr;
793 dlink_node *next_ptr;
794 int i;
796 HASH_WALK_SAFE(i, R_MAX, ptr, next_ptr, resvTable)
798 aconf = ptr->data;
800 /* skip temp resvs */
801 if(aconf->hold)
802 continue;
804 free_conf(ptr->data);
805 dlinkDestroy(ptr, &resvTable[i]);
807 HASH_WALK_END
810 struct nd_entry *
811 hash_find_nd(const char *name)
813 struct nd_entry *nd;
814 dlink_node *ptr;
815 unsigned int hashv;
817 if(EmptyString(name))
818 return NULL;
820 hashv = hash_nick(name);
822 DLINK_FOREACH(ptr, ndTable[hashv].head)
824 nd = ptr->data;
826 if(!irccmp(name, nd->name))
827 return nd;
830 return NULL;
833 static void
834 output_hash(struct Client *source_p, const char *name, int length, int *counts, int deepest)
836 unsigned long total = 0;
837 int i;
839 sendto_one_numeric(source_p, RPL_STATSDEBUG,
840 "B :%s Hash Statistics", name);
842 sendto_one_numeric(source_p, RPL_STATSDEBUG,
843 "B :Size: %d Empty: %d (%.3f%%)",
844 length, counts[0],
845 (float) ((counts[0]*100) / (float) length));
847 for(i = 1; i < 11; i++)
849 total += (counts[i] * i);
852 /* dont want to divide by 0! --fl */
853 if(counts[0] != length)
854 sendto_one_numeric(source_p, RPL_STATSDEBUG,
855 "B :Average depth: %.3f/%.3f Highest depth: %d",
856 (float) (total / (length - counts[0])),
857 (float) (total / length), deepest);
859 for(i = 0; i < 11; i++)
861 sendto_one_numeric(source_p, RPL_STATSDEBUG,
862 "B :Nodes with %d entries: %d",
863 i, counts[i]);
868 static void
869 count_hash(struct Client *source_p, dlink_list *table, int length, const char *name)
871 int counts[11];
872 int deepest = 0;
873 int i;
875 memset(counts, 0, sizeof(counts));
877 for(i = 0; i < length; i++)
879 if(dlink_list_length(&table[i]) >= 10)
880 counts[10]++;
881 else
882 counts[dlink_list_length(&table[i])]++;
884 if(dlink_list_length(&table[i]) > deepest)
885 deepest = dlink_list_length(&table[i]);
888 output_hash(source_p, name, length, counts, deepest);
891 void
892 hash_stats(struct Client *source_p)
894 count_hash(source_p, channelTable, CH_MAX, "Channel");
895 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
896 count_hash(source_p, clientTable, U_MAX, "Client");
897 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
898 count_hash(source_p, idTable, U_MAX, "ID");
899 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
900 count_hash(source_p, hostTable, HOST_MAX, "Hostname");