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