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