ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/hash.c
Revision: 4890
Committed: Wed Nov 19 17:10:46 2014 UTC (10 years, 9 months ago) by michael
Content type: text/x-csrc
File size: 19922 byte(s)
Log Message:
- Style corrections

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2014 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file hash.c
23 * \brief Hash table management.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "conf.h"
30 #include "channel.h"
31 #include "channel_mode.h"
32 #include "client.h"
33 #include "modules.h"
34 #include "hash.h"
35 #include "resv.h"
36 #include "rng_mt.h"
37 #include "userhost.h"
38 #include "irc_string.h"
39 #include "ircd.h"
40 #include "numeric.h"
41 #include "send.h"
42 #include "memory.h"
43 #include "mempool.h"
44 #include "dbuf.h"
45 #include "user.h"
46
47
48 static mp_pool_t *userhost_pool = NULL;
49 static mp_pool_t *namehost_pool = NULL;
50
51 static unsigned int hashf_xor_key = 0;
52
53 /* The actual hash tables, They MUST be of the same HASHSIZE, variable
54 * size tables could be supported but the rehash routine should also
55 * rebuild the transformation maps, I kept the tables of equal size
56 * so that I can use one hash function.
57 */
58 static struct Client *idTable[HASHSIZE];
59 static struct Client *clientTable[HASHSIZE];
60 static struct Channel *channelTable[HASHSIZE];
61 static struct UserHost *userhostTable[HASHSIZE];
62
63
64 /* init_hash()
65 *
66 * inputs - NONE
67 * output - NONE
68 * side effects - Initialize the maps used by hash
69 * functions and clear the tables
70 */
71 void
72 hash_init(void)
73 {
74 userhost_pool = mp_pool_new(sizeof(struct UserHost), MP_CHUNK_SIZE_USERHOST);
75 namehost_pool = mp_pool_new(sizeof(struct NameHost), MP_CHUNK_SIZE_NAMEHOST);
76
77 hashf_xor_key = genrand_int32() % 256; /* better than nothing --adx */
78 }
79
80 /*
81 * New hash function based on the Fowler/Noll/Vo (FNV) algorithm from
82 * http://www.isthe.com/chongo/tech/comp/fnv/
83 *
84 * Here, we use the FNV-1 method, which gives slightly better results
85 * than FNV-1a. -Michael
86 */
87 unsigned int
88 strhash(const char *name)
89 {
90 const unsigned char *p = (const unsigned char *)name;
91 unsigned int hval = FNV1_32_INIT;
92
93 if (EmptyString(p))
94 return 0;
95
96 for (; *p; ++p)
97 {
98 hval += (hval << 1) + (hval << 4) + (hval << 7) +
99 (hval << 8) + (hval << 24);
100 hval ^= (ToLower(*p) ^ hashf_xor_key);
101 }
102
103 return (hval >> FNV1_32_BITS) ^ (hval & ((1 << FNV1_32_BITS) - 1));
104 }
105
106 /************************** Externally visible functions ********************/
107
108 /* Optimization note: in these functions I supposed that the CSE optimization
109 * (Common Subexpression Elimination) does its work decently, this means that
110 * I avoided introducing new variables to do the work myself and I did let
111 * the optimizer play with more free registers, actual tests proved this
112 * solution to be faster than doing things like tmp2=tmp->hnext... and then
113 * use tmp2 myself which would have given less freedom to the optimizer.
114 */
115
116 /* hash_add_client()
117 *
118 * inputs - pointer to client
119 * output - NONE
120 * side effects - Adds a client's name in the proper hash linked
121 * list, can't fail, client_p must have a non-null
122 * name or expect a coredump, the name is infact
123 * taken from client_p->name
124 */
125 void
126 hash_add_client(struct Client *client_p)
127 {
128 unsigned int hashv = strhash(client_p->name);
129
130 client_p->hnext = clientTable[hashv];
131 clientTable[hashv] = client_p;
132 }
133
134 /* hash_add_channel()
135 *
136 * inputs - pointer to channel
137 * output - NONE
138 * side effects - Adds a channel's name in the proper hash linked
139 * list, can't fail. chptr must have a non-null name
140 * or expect a coredump. As before the name is taken
141 * from chptr->name, we do hash its entire lenght
142 * since this proved to be statistically faster
143 */
144 void
145 hash_add_channel(struct Channel *chptr)
146 {
147 unsigned int hashv = strhash(chptr->name);
148
149 chptr->hnextch = channelTable[hashv];
150 channelTable[hashv] = chptr;
151 }
152
153 void
154 hash_add_userhost(struct UserHost *userhost)
155 {
156 unsigned int hashv = strhash(userhost->host);
157
158 userhost->next = userhostTable[hashv];
159 userhostTable[hashv] = userhost;
160 }
161
162 void
163 hash_add_id(struct Client *client_p)
164 {
165 unsigned int hashv = strhash(client_p->id);
166
167 client_p->idhnext = idTable[hashv];
168 idTable[hashv] = client_p;
169 }
170
171 /* hash_del_id()
172 *
173 * inputs - pointer to client
174 * output - NONE
175 * side effects - Removes an ID from the hash linked list
176 */
177 void
178 hash_del_id(struct Client *client_p)
179 {
180 unsigned int hashv = strhash(client_p->id);
181 struct Client *tmp = idTable[hashv];
182
183 if (tmp)
184 {
185 if (tmp == client_p)
186 {
187 idTable[hashv] = client_p->idhnext;
188 client_p->idhnext = client_p;
189 }
190 else
191 {
192 while (tmp->idhnext != client_p)
193 if ((tmp = tmp->idhnext) == NULL)
194 return;
195
196 tmp->idhnext = tmp->idhnext->idhnext;
197 client_p->idhnext = client_p;
198 }
199 }
200 }
201
202 /* hash_del_client()
203 *
204 * inputs - pointer to client
205 * output - NONE
206 * side effects - Removes a Client's name from the hash linked list
207 */
208 void
209 hash_del_client(struct Client *client_p)
210 {
211 unsigned int hashv = strhash(client_p->name);
212 struct Client *tmp = clientTable[hashv];
213
214 if (tmp)
215 {
216 if (tmp == client_p)
217 {
218 clientTable[hashv] = client_p->hnext;
219 client_p->hnext = client_p;
220 }
221 else
222 {
223 while (tmp->hnext != client_p)
224 if ((tmp = tmp->hnext) == NULL)
225 return;
226
227 tmp->hnext = tmp->hnext->hnext;
228 client_p->hnext = client_p;
229 }
230 }
231 }
232
233 /* hash_del_userhost()
234 *
235 * inputs - pointer to userhost
236 * output - NONE
237 * side effects - Removes a userhost from the hash linked list
238 */
239 void
240 hash_del_userhost(struct UserHost *userhost)
241 {
242 unsigned int hashv = strhash(userhost->host);
243 struct UserHost *tmp = userhostTable[hashv];
244
245 if (tmp)
246 {
247 if (tmp == userhost)
248 {
249 userhostTable[hashv] = userhost->next;
250 userhost->next = userhost;
251 }
252 else
253 {
254 while (tmp->next != userhost)
255 if ((tmp = tmp->next) == NULL)
256 return;
257
258 tmp->next = tmp->next->next;
259 userhost->next = userhost;
260 }
261 }
262 }
263
264 /* hash_del_channel()
265 *
266 * inputs - pointer to client
267 * output - NONE
268 * side effects - Removes the channel's name from the corresponding
269 * hash linked list
270 */
271 void
272 hash_del_channel(struct Channel *chptr)
273 {
274 unsigned int hashv = strhash(chptr->name);
275 struct Channel *tmp = channelTable[hashv];
276
277 if (tmp)
278 {
279 if (tmp == chptr)
280 {
281 channelTable[hashv] = chptr->hnextch;
282 chptr->hnextch = chptr;
283 }
284 else
285 {
286 while (tmp->hnextch != chptr)
287 if ((tmp = tmp->hnextch) == NULL)
288 return;
289
290 tmp->hnextch = tmp->hnextch->hnextch;
291 chptr->hnextch = chptr;
292 }
293 }
294 }
295
296 /* hash_find_client()
297 *
298 * inputs - pointer to name
299 * output - NONE
300 * side effects - New semantics: finds a client whose name is 'name'
301 * if can't find one returns NULL. If it finds one moves
302 * it to the top of the list and returns it.
303 */
304 struct Client *
305 hash_find_client(const char *name)
306 {
307 unsigned int hashv = strhash(name);
308 struct Client *client_p;
309
310 if ((client_p = clientTable[hashv]))
311 {
312 if (irccmp(name, client_p->name))
313 {
314 struct Client *prev;
315
316 while (prev = client_p, (client_p = client_p->hnext))
317 {
318 if (!irccmp(name, client_p->name))
319 {
320 prev->hnext = client_p->hnext;
321 client_p->hnext = clientTable[hashv];
322 clientTable[hashv] = client_p;
323 break;
324 }
325 }
326 }
327 }
328
329 return client_p;
330 }
331
332 struct Client *
333 hash_find_id(const char *name)
334 {
335 unsigned int hashv = strhash(name);
336 struct Client *client_p;
337
338 if ((client_p = idTable[hashv]))
339 {
340 if (strcmp(name, client_p->id))
341 {
342 struct Client *prev;
343
344 while (prev = client_p, (client_p = client_p->idhnext))
345 {
346 if (!strcmp(name, client_p->id))
347 {
348 prev->idhnext = client_p->idhnext;
349 client_p->idhnext = idTable[hashv];
350 idTable[hashv] = client_p;
351 break;
352 }
353 }
354 }
355 }
356
357 return client_p;
358 }
359
360 struct Client *
361 hash_find_server(const char *name)
362 {
363 unsigned int hashv = strhash(name);
364 struct Client *client_p = NULL;
365
366 if (IsDigit(*name) && strlen(name) == IRC_MAXSID)
367 return hash_find_id(name);
368
369 if ((client_p = clientTable[hashv]))
370 {
371 if ((!IsServer(client_p) && !IsMe(client_p)) ||
372 irccmp(name, client_p->name))
373 {
374 struct Client *prev;
375
376 while (prev = client_p, (client_p = client_p->hnext))
377 {
378 if ((IsServer(client_p) || IsMe(client_p)) &&
379 !irccmp(name, client_p->name))
380 {
381 prev->hnext = client_p->hnext;
382 client_p->hnext = clientTable[hashv];
383 clientTable[hashv] = client_p;
384 break;
385 }
386 }
387 }
388 }
389
390 return client_p;
391 }
392
393 /* hash_find_channel()
394 *
395 * inputs - pointer to name
396 * output - NONE
397 * side effects - New semantics: finds a channel whose name is 'name',
398 * if can't find one returns NULL, if can find it moves
399 * it to the top of the list and returns it.
400 */
401 struct Channel *
402 hash_find_channel(const char *name)
403 {
404 unsigned int hashv = strhash(name);
405 struct Channel *chptr = NULL;
406
407 if ((chptr = channelTable[hashv]))
408 {
409 if (irccmp(name, chptr->name))
410 {
411 struct Channel *prev;
412
413 while (prev = chptr, (chptr = chptr->hnextch))
414 {
415 if (!irccmp(name, chptr->name))
416 {
417 prev->hnextch = chptr->hnextch;
418 chptr->hnextch = channelTable[hashv];
419 channelTable[hashv] = chptr;
420 break;
421 }
422 }
423 }
424 }
425
426 return chptr;
427 }
428
429 /* hash_get_bucket(int type, unsigned int hashv)
430 *
431 * inputs - hash value (must be between 0 and HASHSIZE - 1)
432 * output - NONE
433 * returns - pointer to first channel in channelTable[hashv]
434 * if that exists;
435 * NULL if there is no channel in that place;
436 * NULL if hashv is an invalid number.
437 * side effects - NONE
438 */
439 void *
440 hash_get_bucket(int type, unsigned int hashv)
441 {
442 assert(hashv < HASHSIZE);
443 if (hashv >= HASHSIZE)
444 return NULL;
445
446 switch (type)
447 {
448 case HASH_TYPE_ID:
449 return idTable[hashv];
450 break;
451 case HASH_TYPE_CHANNEL:
452 return channelTable[hashv];
453 break;
454 case HASH_TYPE_CLIENT:
455 return clientTable[hashv];
456 break;
457 case HASH_TYPE_USERHOST:
458 return userhostTable[hashv];
459 break;
460 default:
461 assert(0);
462 }
463
464 return NULL;
465 }
466
467 struct UserHost *
468 hash_find_userhost(const char *host)
469 {
470 unsigned int hashv = strhash(host);
471 struct UserHost *userhost;
472
473 if ((userhost = userhostTable[hashv]))
474 {
475 if (irccmp(host, userhost->host))
476 {
477 struct UserHost *prev;
478
479 while (prev = userhost, (userhost = userhost->next))
480 {
481 if (!irccmp(host, userhost->host))
482 {
483 prev->next = userhost->next;
484 userhost->next = userhostTable[hashv];
485 userhostTable[hashv] = userhost;
486 break;
487 }
488 }
489 }
490 }
491
492 return userhost;
493 }
494
495 /* count_user_host()
496 *
497 * inputs - user name
498 * - hostname
499 * - int flag 1 if global, 0 if local
500 * - pointer to where global count should go
501 * - pointer to where local count should go
502 * - pointer to where identd count should go (local clients only)
503 * output - none
504 * side effects -
505 */
506 void
507 count_user_host(const char *user, const char *host, unsigned int *global_p,
508 unsigned int *local_p, unsigned int *icount_p)
509 {
510 dlink_node *node = NULL;
511 struct UserHost *found_userhost;
512 struct NameHost *nameh;
513
514 if ((found_userhost = hash_find_userhost(host)) == NULL)
515 return;
516
517 DLINK_FOREACH(node, found_userhost->list.head)
518 {
519 nameh = node->data;
520
521 if (!irccmp(user, nameh->name))
522 {
523 if (global_p)
524 *global_p = nameh->gcount;
525 if (local_p)
526 *local_p = nameh->lcount;
527 if (icount_p)
528 *icount_p = nameh->icount;
529
530 return;
531 }
532 }
533 }
534
535 /* find_or_add_userhost()
536 *
537 * inputs - host name
538 * output - none
539 * side effects - find UserHost * for given host name
540 */
541 static struct UserHost *
542 find_or_add_userhost(const char *host)
543 {
544 struct UserHost *userhost = NULL;
545
546 if ((userhost = hash_find_userhost(host)))
547 return userhost;
548
549 userhost = mp_pool_get(userhost_pool);
550
551 strlcpy(userhost->host, host, sizeof(userhost->host));
552 hash_add_userhost(userhost);
553
554 return userhost;
555 }
556
557 /* add_user_host()
558 *
559 * inputs - user name
560 * - hostname
561 * - int flag 1 if global, 0 if local
562 * output - none
563 * side effects - add given user@host to hash tables
564 */
565 void
566 add_user_host(const char *user, const char *host, int global)
567 {
568 dlink_node *node = NULL;
569 struct UserHost *found_userhost;
570 struct NameHost *nameh;
571 unsigned int hasident = 1;
572
573 if (*user == '~')
574 {
575 hasident = 0;
576 ++user;
577 }
578
579 if ((found_userhost = find_or_add_userhost(host)) == NULL)
580 return;
581
582 DLINK_FOREACH(node, found_userhost->list.head)
583 {
584 nameh = node->data;
585
586 if (!irccmp(user, nameh->name))
587 {
588 nameh->gcount++;
589
590 if (!global)
591 {
592 if (hasident)
593 nameh->icount++;
594 nameh->lcount++;
595 }
596
597 return;
598 }
599 }
600
601 nameh = mp_pool_get(namehost_pool);
602
603 strlcpy(nameh->name, user, sizeof(nameh->name));
604
605 nameh->gcount = 1;
606
607 if (!global)
608 {
609 if (hasident)
610 nameh->icount = 1;
611
612 nameh->lcount = 1;
613 }
614
615 dlinkAdd(nameh, &nameh->node, &found_userhost->list);
616 }
617
618 /* delete_user_host()
619 *
620 * inputs - user name
621 * - hostname
622 * - int flag 1 if global, 0 if local
623 * output - none
624 * side effects - delete given user@host to hash tables
625 */
626 void
627 delete_user_host(const char *user, const char *host, int global)
628 {
629 dlink_node *node = NULL;
630 struct UserHost *found_userhost = NULL;
631 unsigned int hasident = 1;
632
633 if (*user == '~')
634 {
635 hasident = 0;
636 ++user;
637 }
638
639 if ((found_userhost = hash_find_userhost(host)) == NULL)
640 return;
641
642 DLINK_FOREACH(node, found_userhost->list.head)
643 {
644 struct NameHost *nameh = node->data;
645
646 if (!irccmp(user, nameh->name))
647 {
648 if (nameh->gcount > 0)
649 nameh->gcount--;
650
651 if (!global)
652 {
653 if (nameh->lcount > 0)
654 nameh->lcount--;
655
656 if (hasident && nameh->icount > 0)
657 nameh->icount--;
658 }
659
660 if (nameh->gcount == 0 && nameh->lcount == 0)
661 {
662 dlinkDelete(&nameh->node, &found_userhost->list);
663 mp_pool_release(nameh);
664 }
665
666 if (dlink_list_length(&found_userhost->list) == 0)
667 {
668 hash_del_userhost(found_userhost);
669 mp_pool_release(found_userhost);
670 }
671
672 return;
673 }
674 }
675 }
676
677 /*
678 * Safe list code.
679 *
680 * The idea is really quite simple. As the link lists pointed to in
681 * each "bucket" of the channel hash table are traversed atomically
682 * there is no locking needed. Overall, yes, inconsistent reported
683 * state can still happen, but normally this isn't a big deal.
684 * I don't like sticking the code into hash.c but oh well. Moreover,
685 * if a hash isn't used in future, oops.
686 *
687 * - Dianora
688 */
689
690 /* exceeding_sendq()
691 *
692 * inputs - pointer to client to check
693 * output - 1 if client is in danger of blowing its sendq
694 * 0 if it is not.
695 * side effects -
696 *
697 * Sendq limit is fairly conservative at 1/2 (In original anyway)
698 */
699 static int
700 exceeding_sendq(const struct Client *to)
701 {
702 if (dbuf_length(&to->connection->buf_sendq) > (get_sendq(&to->connection->confs) / 2))
703 return 1;
704 else
705 return 0;
706 }
707
708 void
709 free_list_task(struct Client *source_p)
710 {
711 struct ListTask *const lt = source_p->connection->list_task;
712 dlink_node *node = NULL, *node_next = NULL;
713
714 if ((node = dlinkFindDelete(&listing_client_list, source_p)))
715 free_dlink_node(node);
716
717 DLINK_FOREACH_SAFE(node, node_next, lt->show_mask.head)
718 {
719 MyFree(node->data);
720 free_dlink_node(node);
721 }
722
723 DLINK_FOREACH_SAFE(node, node_next, lt->hide_mask.head)
724 {
725 MyFree(node->data);
726 free_dlink_node(node);
727 }
728
729 MyFree(lt);
730 source_p->connection->list_task = NULL;
731 }
732
733 /* list_allow_channel()
734 *
735 * inputs - channel name
736 * - pointer to a list task
737 * output - 1 if the channel is to be displayed
738 * 0 otherwise
739 * side effects -
740 */
741 static int
742 list_allow_channel(const char *name, const struct ListTask *lt)
743 {
744 const dlink_node *node = NULL;
745
746 DLINK_FOREACH(node, lt->show_mask.head)
747 if (match(node->data, name) != 0)
748 return 0;
749
750 DLINK_FOREACH(node, lt->hide_mask.head)
751 if (match(node->data, name) == 0)
752 return 0;
753
754 return 1;
755 }
756
757 /* list_one_channel()
758 *
759 * inputs - client pointer to return result to
760 * - pointer to channel to list
761 * - pointer to ListTask structure
762 * output - none
763 * side effects -
764 */
765 static void
766 list_one_channel(struct Client *source_p, struct Channel *chptr)
767 {
768 const struct ListTask *const lt = source_p->connection->list_task;
769 char listbuf[MODEBUFLEN] = "";
770 char modebuf[MODEBUFLEN] = "";
771 char parabuf[MODEBUFLEN] = "";
772
773 if (SecretChannel(chptr) &&
774 !(HasUMode(source_p, UMODE_ADMIN) || IsMember(source_p, chptr)))
775 return;
776
777 if (dlink_list_length(&chptr->members) < lt->users_min ||
778 dlink_list_length(&chptr->members) > lt->users_max ||
779 (chptr->creationtime != 0 &&
780 ((unsigned int)chptr->creationtime < lt->created_min ||
781 (unsigned int)chptr->creationtime > lt->created_max)) ||
782 (unsigned int)chptr->topic_time < lt->topicts_min ||
783 (chptr->topic_time ? (unsigned int)chptr->topic_time : UINT_MAX) >
784 lt->topicts_max)
785 return;
786
787 if (lt->topic[0] && match(lt->topic, chptr->topic))
788 return;
789
790 if (!list_allow_channel(chptr->name, lt))
791 return;
792
793 channel_modes(chptr, source_p, modebuf, parabuf);
794
795 if (chptr->topic[0])
796 snprintf(listbuf, sizeof(listbuf), "[%s] ", modebuf);
797 else
798 snprintf(listbuf, sizeof(listbuf), "[%s]", modebuf);
799
800 sendto_one_numeric(source_p, &me, RPL_LIST, chptr->name,
801 dlink_list_length(&chptr->members),
802 listbuf, chptr->topic);
803 }
804
805 /* safe_list_channels()
806 *
807 * inputs - pointer to client requesting list
808 * output - 0/1
809 * side effects - safely list all channels to source_p
810 *
811 * Walk the channel buckets, ensure all pointers in a bucket are
812 * traversed before blocking on a sendq. This means, no locking is needed.
813 *
814 * - Dianora
815 */
816 void
817 safe_list_channels(struct Client *source_p, int only_unmasked_channels)
818 {
819 struct ListTask *const lt = source_p->connection->list_task;
820 struct Channel *chptr = NULL;
821
822 if (!only_unmasked_channels)
823 {
824 for (unsigned int i = lt->hash_index; i < HASHSIZE; ++i)
825 {
826 if (exceeding_sendq(source_p))
827 {
828 lt->hash_index = i;
829 return; /* Still more to do */
830 }
831
832 for (chptr = channelTable[i]; chptr; chptr = chptr->hnextch)
833 list_one_channel(source_p, chptr);
834 }
835 }
836 else
837 {
838 dlink_node *node = NULL;
839
840 DLINK_FOREACH(node, lt->show_mask.head)
841 if ((chptr = hash_find_channel(node->data)))
842 list_one_channel(source_p, chptr);
843 }
844
845 free_list_task(source_p);
846 sendto_one_numeric(source_p, &me, RPL_LISTEND);
847 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision