]> jfr.im git - irc/rqf/shadowircd.git/blame - src/hash.c
[svn] Remove hash_find_masked_server(), which made it possible
[irc/rqf/shadowircd.git] / src / hash.c
CommitLineData
212380e3 1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * hash.c: Maintains hashtables.
4 *
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
8 *
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.
13 *
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.
18 *
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
23 *
f42e9ceb 24 * $Id: hash.c 3177 2007-02-01 00:19:14Z jilles $
212380e3 25 */
26
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"
43
44dlink_list *clientTable;
45dlink_list *channelTable;
46dlink_list *idTable;
47dlink_list *resvTable;
48dlink_list *hostTable;
49dlink_list *helpTable;
50dlink_list *ndTable;
51
52/*
53 * look in whowas.c for the missing ...[WW_MAX]; entry
54 */
55
56/*
57 * Hashing.
58 *
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 * +-----+
76 *
77 * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
78 *
79 * The order shown above is just one instant of the server.
80 *
81 *
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
85 *
86 *
87 */
88
89/* init_hash()
90 *
91 * clears the various hashtables
92 */
93void
94init_hash(void)
95{
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);
103}
104
105#ifndef RICER_HASHING
106u_int32_t
107fnv_hash_upper(const unsigned char *s, int bits)
108{
109 u_int32_t h = FNV1_32_INIT;
110
111 while (*s)
112 {
113 h ^= ToUpper(*s++);
114 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
115 }
116 h = (h >> bits) ^ (h & ((2^bits)-1));
117 return h;
118}
119
120u_int32_t
121fnv_hash(const unsigned char *s, int bits)
122{
123 u_int32_t h = FNV1_32_INIT;
124
125 while (*s)
126 {
127 h ^= *s++;
128 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
129 }
130 h = (h >> bits) ^ (h & ((2^bits)-1));
131 return h;
132}
133
134u_int32_t
135fnv_hash_len(const unsigned char *s, int bits, int len)
136{
137 u_int32_t h = FNV1_32_INIT;
138 const unsigned char *x = s + len;
139 while (*s && s < x)
140 {
141 h ^= *s++;
142 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
143 }
144 h = (h >> bits) ^ (h & ((2^bits)-1));
145 return h;
146}
147
148u_int32_t
149fnv_hash_upper_len(const unsigned char *s, int bits, int len)
150{
151 u_int32_t h = FNV1_32_INIT;
152 const unsigned char *x = s + len;
153 while (*s && s < x)
154 {
155 h ^= ToUpper(*s++);
156 h += (h<<1) + (h<<4) + (h<<7) + (h << 8) + (h << 24);
157 }
158 h = (h >> bits) ^ (h & ((2^bits)-1));
159 return h;
160}
161#endif
162
163/* hash_nick()
164 *
165 * hashes a nickname, first converting to lowercase
166 */
167static u_int32_t
168hash_nick(const char *name)
169{
170 return fnv_hash_upper((const unsigned char *) name, U_MAX_BITS);
171}
172
173/* hash_id()
174 *
175 * hashes an id, case is kept
176 */
177static u_int32_t
178hash_id(const char *name)
179{
180 return fnv_hash((const unsigned char *) name, U_MAX_BITS);
181}
182
183/* hash_channel()
184 *
185 * hashes a channel name, based on first 30 chars only for efficiency
186 */
187static u_int32_t
188hash_channel(const char *name)
189{
190 return fnv_hash_upper_len((const unsigned char *) name, CH_MAX_BITS, 30);
191}
192
193/* hash_hostname()
194 *
195 * hashes a hostname, based on first 30 chars only, as thats likely to
196 * be more dynamic than rest.
197 */
198static u_int32_t
199hash_hostname(const char *name)
200{
201 return fnv_hash_upper_len((const unsigned char *) name, HOST_MAX_BITS, 30);
202}
203
204/* hash_resv()
205 *
206 * hashes a resv channel name, based on first 30 chars only
207 */
208static u_int32_t
209hash_resv(const char *name)
210{
211 return fnv_hash_upper_len((const unsigned char *) name, R_MAX_BITS, 30);
212}
213
214static unsigned int
215hash_help(const char *name)
216{
217 unsigned int h = 0;
218
219 while(*name)
220 {
221 h += (unsigned int) (ToLower(*name++) & 0xDF);
222 }
223
224 return (h % HELP_MAX);
225}
226
227/* add_to_id_hash()
228 *
229 * adds an entry to the id hash table
230 */
231void
232add_to_id_hash(const char *name, struct Client *client_p)
233{
234 unsigned int hashv;
235
236 if(EmptyString(name) || (client_p == NULL))
237 return;
238
239 hashv = hash_id(name);
240 dlinkAddAlloc(client_p, &idTable[hashv]);
241}
242
243/* add_to_client_hash()
244 *
245 * adds an entry (client/server) to the client hash table
246 */
247void
248add_to_client_hash(const char *name, struct Client *client_p)
249{
250 unsigned int hashv;
251
252 s_assert(name != NULL);
253 s_assert(client_p != NULL);
254 if(EmptyString(name) || (client_p == NULL))
255 return;
256
257 hashv = hash_nick(name);
258 dlinkAddAlloc(client_p, &clientTable[hashv]);
259}
260
261/* add_to_hostname_hash()
262 *
263 * adds a client entry to the hostname hash table
264 */
265void
266add_to_hostname_hash(const char *hostname, struct Client *client_p)
267{
268 unsigned int hashv;
269
270 s_assert(hostname != NULL);
271 s_assert(client_p != NULL);
272 if(EmptyString(hostname) || (client_p == NULL))
273 return;
274
275 hashv = hash_hostname(hostname);
276 dlinkAddAlloc(client_p, &hostTable[hashv]);
277}
278
279/* add_to_resv_hash()
280 *
281 * adds a resv channel entry to the resv hash table
282 */
283void
284add_to_resv_hash(const char *name, struct ConfItem *aconf)
285{
286 unsigned int hashv;
287
288 s_assert(!EmptyString(name));
289 s_assert(aconf != NULL);
290 if(EmptyString(name) || aconf == NULL)
291 return;
292
293 hashv = hash_resv(name);
294 dlinkAddAlloc(aconf, &resvTable[hashv]);
295}
296
297void
298add_to_help_hash(const char *name, struct cachefile *hptr)
299{
300 unsigned int hashv;
301
302 if(EmptyString(name) || hptr == NULL)
303 return;
304
305 hashv = hash_help(name);
306 dlinkAddAlloc(hptr, &helpTable[hashv]);
307}
308
309void
310add_to_nd_hash(const char *name, struct nd_entry *nd)
311{
312 nd->hashv = hash_nick(name);
313 dlinkAdd(nd, &nd->hnode, &ndTable[nd->hashv]);
314}
315
316/* del_from_id_hash()
317 *
318 * removes an id from the id hash table
319 */
320void
321del_from_id_hash(const char *id, struct Client *client_p)
322{
323 unsigned int hashv;
324
325 s_assert(id != NULL);
326 s_assert(client_p != NULL);
327 if(EmptyString(id) || client_p == NULL)
328 return;
329
330 hashv = hash_id(id);
331 dlinkFindDestroy(client_p, &idTable[hashv]);
332}
333
334/* del_from_client_hash()
335 *
336 * removes a client/server from the client hash table
337 */
338void
339del_from_client_hash(const char *name, struct Client *client_p)
340{
341 unsigned int hashv;
342
343 /* no s_asserts, this can happen when removing a client that
344 * is unregistered.
345 */
346 if(EmptyString(name) || client_p == NULL)
347 return;
348
349 hashv = hash_nick(name);
350 dlinkFindDestroy(client_p, &clientTable[hashv]);
351}
352
353/* del_from_channel_hash()
354 *
355 * removes a channel from the channel hash table
356 */
357void
358del_from_channel_hash(const char *name, struct Channel *chptr)
359{
360 unsigned int hashv;
361
362 s_assert(name != NULL);
363 s_assert(chptr != NULL);
364
365 if(EmptyString(name) || chptr == NULL)
366 return;
367
368 hashv = hash_channel(name);
369 dlinkFindDestroy(chptr, &channelTable[hashv]);
370}
371
372/* del_from_hostname_hash()
373 *
374 * removes a client entry from the hostname hash table
375 */
376void
377del_from_hostname_hash(const char *hostname, struct Client *client_p)
378{
379 unsigned int hashv;
380
381 if(hostname == NULL || client_p == NULL)
382 return;
383
384 hashv = hash_hostname(hostname);
385
386 dlinkFindDestroy(client_p, &hostTable[hashv]);
387}
388
389/* del_from_resv_hash()
390 *
391 * removes a resv entry from the resv hash table
392 */
393void
394del_from_resv_hash(const char *name, struct ConfItem *aconf)
395{
396 unsigned int hashv;
397
398 s_assert(name != NULL);
399 s_assert(aconf != NULL);
400 if(EmptyString(name) || aconf == NULL)
401 return;
402
403 hashv = hash_resv(name);
404
405 dlinkFindDestroy(aconf, &resvTable[hashv]);
406}
407
408void
409clear_help_hash(void)
410{
411 dlink_node *ptr;
412 dlink_node *next_ptr;
413 int i;
414
415 HASH_WALK_SAFE(i, HELP_MAX, ptr, next_ptr, helpTable)
416 {
417 free_cachefile(ptr->data);
418 dlinkDestroy(ptr, &helpTable[i]);
419 }
420 HASH_WALK_END
421}
422
423/* find_id()
424 *
425 * finds a client entry from the id hash table
426 */
427struct Client *
428find_id(const char *name)
429{
430 struct Client *target_p;
431 dlink_node *ptr;
432 unsigned int hashv;
433
434 if(EmptyString(name))
435 return NULL;
436
437 hashv = hash_id(name);
438
439 DLINK_FOREACH(ptr, idTable[hashv].head)
440 {
441 target_p = ptr->data;
442
443 if(strcmp(name, target_p->id) == 0)
444 return target_p;
445 }
446
447 return NULL;
448}
449
212380e3 450/* find_client()
451 *
452 * finds a client/server entry from the client hash table
453 */
454struct Client *
455find_client(const char *name)
456{
457 struct Client *target_p;
458 dlink_node *ptr;
459 unsigned int hashv;
460
461 s_assert(name != NULL);
462 if(EmptyString(name))
463 return NULL;
464
465 /* hunting for an id, not a nick */
466 if(IsDigit(*name))
467 return (find_id(name));
468
469 hashv = hash_nick(name);
470
471 DLINK_FOREACH(ptr, clientTable[hashv].head)
472 {
473 target_p = ptr->data;
474
475 if(irccmp(name, target_p->name) == 0)
476 return target_p;
477 }
478
479 return NULL;
480}
481
482/* find_named_client()
483 *
484 * finds a client/server entry from the client hash table
485 */
486struct Client *
487find_named_client(const char *name)
488{
489 struct Client *target_p;
490 dlink_node *ptr;
491 unsigned int hashv;
492
493 s_assert(name != NULL);
494 if(EmptyString(name))
495 return NULL;
496
497 hashv = hash_nick(name);
498
499 DLINK_FOREACH(ptr, clientTable[hashv].head)
500 {
501 target_p = ptr->data;
502
503 if(irccmp(name, target_p->name) == 0)
504 return target_p;
505 }
506
507 return NULL;
508}
509
510/* find_server()
511 *
512 * finds a server from the client hash table
513 */
514struct Client *
515find_server(struct Client *source_p, const char *name)
516{
517 struct Client *target_p;
518 dlink_node *ptr;
519 unsigned int hashv;
520
521 if(EmptyString(name))
522 return NULL;
523
524 if((source_p == NULL || !MyClient(source_p)) &&
525 IsDigit(*name) && strlen(name) == 3)
526 {
527 target_p = find_id(name);
528 return(target_p);
529 }
530
531 hashv = hash_nick(name);
532
533 DLINK_FOREACH(ptr, clientTable[hashv].head)
534 {
535 target_p = ptr->data;
536
537 if((IsServer(target_p) || IsMe(target_p)) &&
538 irccmp(name, target_p->name) == 0)
539 return target_p;
540 }
541
f42e9ceb 542 return NULL;
212380e3 543}
544
545/* find_hostname()
546 *
547 * finds a hostname dlink list from the hostname hash table.
548 * we return the full dlink list, because you can have multiple
549 * entries with the same hostname
550 */
551dlink_node *
552find_hostname(const char *hostname)
553{
554 unsigned int hashv;
555
556 if(EmptyString(hostname))
557 return NULL;
558
559 hashv = hash_hostname(hostname);
560
561 return hostTable[hashv].head;
562}
563
564/* find_channel()
565 *
566 * finds a channel from the channel hash table
567 */
568struct Channel *
569find_channel(const char *name)
570{
571 struct Channel *chptr;
572 dlink_node *ptr;
573 unsigned int hashv;
574
575 s_assert(name != NULL);
576 if(EmptyString(name))
577 return NULL;
578
579 hashv = hash_channel(name);
580
581 DLINK_FOREACH(ptr, channelTable[hashv].head)
582 {
583 chptr = ptr->data;
584
585 if(irccmp(name, chptr->chname) == 0)
586 return chptr;
587 }
588
589 return NULL;
590}
591
592/*
593 * get_or_create_channel
594 * inputs - client pointer
595 * - channel name
596 * - pointer to int flag whether channel was newly created or not
597 * output - returns channel block or NULL if illegal name
598 * - also modifies *isnew
599 *
600 * Get Channel block for chname (and allocate a new channel
601 * block, if it didn't exist before).
602 */
603struct Channel *
604get_or_create_channel(struct Client *client_p, const char *chname, int *isnew)
605{
606 struct Channel *chptr;
607 dlink_node *ptr;
608 unsigned int hashv;
609 int len;
610 const char *s = chname;
611
612 if(EmptyString(s))
613 return NULL;
614
615 len = strlen(s);
616 if(len > CHANNELLEN)
617 {
618 char *t;
619 if(IsServer(client_p))
620 {
621 sendto_realops_snomask(SNO_DEBUG, L_ALL,
622 "*** Long channel name from %s (%d > %d): %s",
623 client_p->name, len, CHANNELLEN, s);
624 }
625 len = CHANNELLEN;
626 t = LOCAL_COPY(s);
627 *(t + CHANNELLEN) = '\0';
628 s = t;
629 }
630
631 hashv = hash_channel(s);
632
633 DLINK_FOREACH(ptr, channelTable[hashv].head)
634 {
635 chptr = ptr->data;
636
637 if(irccmp(s, chptr->chname) == 0)
638 {
639 if(isnew != NULL)
640 *isnew = 0;
641 return chptr;
642 }
643 }
644
645 if(isnew != NULL)
646 *isnew = 1;
647
648 chptr = allocate_channel(s);
649
650 dlinkAdd(chptr, &chptr->node, &global_channel_list);
651
652 chptr->channelts = CurrentTime; /* doesn't hurt to set it here */
653
654 dlinkAddAlloc(chptr, &channelTable[hashv]);
655
656 return chptr;
657}
658
659/* hash_find_resv()
660 *
661 * hunts for a resv entry in the resv hash table
662 */
663struct ConfItem *
664hash_find_resv(const char *name)
665{
666 struct ConfItem *aconf;
667 dlink_node *ptr;
668 unsigned int hashv;
669
670 s_assert(name != NULL);
671 if(EmptyString(name))
672 return NULL;
673
674 hashv = hash_resv(name);
675
676 DLINK_FOREACH(ptr, resvTable[hashv].head)
677 {
678 aconf = ptr->data;
679
680 if(!irccmp(name, aconf->name))
681 {
682 aconf->port++;
683 return aconf;
684 }
685 }
686
687 return NULL;
688}
689
690struct cachefile *
691hash_find_help(const char *name, int flags)
692{
693 struct cachefile *hptr;
694 dlink_node *ptr;
695 unsigned int hashv;
696
697 if(EmptyString(name))
698 return NULL;
699
700 hashv = hash_help(name);
701
702 DLINK_FOREACH(ptr, helpTable[hashv].head)
703 {
704 hptr = ptr->data;
705
706 if((irccmp(name, hptr->name) == 0) &&
707 (hptr->flags & flags))
708 return hptr;
709 }
710
711 return NULL;
712}
713
714void
715clear_resv_hash(void)
716{
717 struct ConfItem *aconf;
718 dlink_node *ptr;
719 dlink_node *next_ptr;
720 int i;
721
722 HASH_WALK_SAFE(i, R_MAX, ptr, next_ptr, resvTable)
723 {
724 aconf = ptr->data;
725
726 /* skip temp resvs */
727 if(aconf->hold)
728 continue;
729
730 free_conf(ptr->data);
731 dlinkDestroy(ptr, &resvTable[i]);
732 }
733 HASH_WALK_END
734}
735
736struct nd_entry *
737hash_find_nd(const char *name)
738{
739 struct nd_entry *nd;
740 dlink_node *ptr;
741 unsigned int hashv;
742
743 if(EmptyString(name))
744 return NULL;
745
746 hashv = hash_nick(name);
747
748 DLINK_FOREACH(ptr, ndTable[hashv].head)
749 {
750 nd = ptr->data;
751
752 if(!irccmp(name, nd->name))
753 return nd;
754 }
755
756 return NULL;
757}
758
759static void
760output_hash(struct Client *source_p, const char *name, int length, int *counts, int deepest)
761{
762 unsigned long total = 0;
763 int i;
764
765 sendto_one_numeric(source_p, RPL_STATSDEBUG,
766 "B :%s Hash Statistics", name);
767
768 sendto_one_numeric(source_p, RPL_STATSDEBUG,
769 "B :Size: %d Empty: %d (%.3f%%)",
770 length, counts[0],
771 (float) ((counts[0]*100) / (float) length));
772
773 for(i = 1; i < 11; i++)
774 {
775 total += (counts[i] * i);
776 }
777
778 /* dont want to divide by 0! --fl */
779 if(counts[0] != length)
780 sendto_one_numeric(source_p, RPL_STATSDEBUG,
781 "B :Average depth: %.3f/%.3f Highest depth: %d",
782 (float) (total / (length - counts[0])),
783 (float) (total / length), deepest);
784
785 for(i = 0; i < 11; i++)
786 {
787 sendto_one_numeric(source_p, RPL_STATSDEBUG,
788 "B :Nodes with %d entries: %d",
789 i, counts[i]);
790 }
791}
792
793
794static void
795count_hash(struct Client *source_p, dlink_list *table, int length, const char *name)
796{
797 int counts[11];
798 int deepest = 0;
799 int i;
800
801 memset(counts, 0, sizeof(counts));
802
803 for(i = 0; i < length; i++)
804 {
805 if(dlink_list_length(&table[i]) >= 10)
806 counts[10]++;
807 else
808 counts[dlink_list_length(&table[i])]++;
809
810 if(dlink_list_length(&table[i]) > deepest)
811 deepest = dlink_list_length(&table[i]);
812 }
813
814 output_hash(source_p, name, length, counts, deepest);
815}
816
817void
818hash_stats(struct Client *source_p)
819{
820 count_hash(source_p, channelTable, CH_MAX, "Channel");
821 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
822 count_hash(source_p, clientTable, U_MAX, "Client");
823 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
824 count_hash(source_p, idTable, U_MAX, "ID");
825 sendto_one_numeric(source_p, RPL_STATSDEBUG, "B :--");
826 count_hash(source_p, hostTable, HOST_MAX, "Hostname");
827}