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