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