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