ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/hash.c
Revision: 5545
Committed: Thu Feb 12 14:13:34 2015 UTC (10 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 19972 byte(s)
Log Message:
- Fixed style in several places

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2015 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;
49 static mp_pool_t *namehost_pool;
50
51 static unsigned int hashf_xor_key;
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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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
444 if (hashv >= HASHSIZE)
445 return NULL;
446
447 switch (type)
448 {
449 case HASH_TYPE_ID:
450 return idTable[hashv];
451 break;
452 case HASH_TYPE_CHANNEL:
453 return channelTable[hashv];
454 break;
455 case HASH_TYPE_CLIENT:
456 return clientTable[hashv];
457 break;
458 case HASH_TYPE_USERHOST:
459 return userhostTable[hashv];
460 break;
461 default:
462 assert(0);
463 }
464
465 return NULL;
466 }
467
468 struct UserHost *
469 hash_find_userhost(const char *host)
470 {
471 const unsigned int hashv = strhash(host);
472 struct UserHost *userhost;
473
474 if ((userhost = userhostTable[hashv]))
475 {
476 if (irccmp(host, userhost->host))
477 {
478 struct UserHost *prev;
479
480 while (prev = userhost, (userhost = userhost->next))
481 {
482 if (!irccmp(host, userhost->host))
483 {
484 prev->next = userhost->next;
485 userhost->next = userhostTable[hashv];
486 userhostTable[hashv] = userhost;
487 break;
488 }
489 }
490 }
491 }
492
493 return userhost;
494 }
495
496 /* count_user_host()
497 *
498 * inputs - user name
499 * - hostname
500 * - int flag 1 if global, 0 if local
501 * - pointer to where global count should go
502 * - pointer to where local count should go
503 * - pointer to where identd count should go (local clients only)
504 * output - none
505 * side effects -
506 */
507 void
508 count_user_host(const char *user, const char *host, unsigned int *global_p,
509 unsigned int *local_p, unsigned int *icount_p)
510 {
511 dlink_node *node = NULL;
512 struct UserHost *found_userhost;
513
514 if ((found_userhost = hash_find_userhost(host)) == NULL)
515 return;
516
517 DLINK_FOREACH(node, found_userhost->list.head)
518 {
519 struct NameHost *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 nameh->gcount = 1;
603 strlcpy(nameh->name, user, sizeof(nameh->name));
604
605 if (!global)
606 {
607 if (hasident)
608 nameh->icount = 1;
609
610 nameh->lcount = 1;
611 }
612
613 dlinkAdd(nameh, &nameh->node, &found_userhost->list);
614 }
615
616 /* delete_user_host()
617 *
618 * inputs - user name
619 * - hostname
620 * - int flag 1 if global, 0 if local
621 * output - none
622 * side effects - delete given user@host to hash tables
623 */
624 void
625 delete_user_host(const char *user, const char *host, int global)
626 {
627 dlink_node *node = NULL;
628 struct UserHost *found_userhost = NULL;
629 unsigned int hasident = 1;
630
631 if (*user == '~')
632 {
633 hasident = 0;
634 ++user;
635 }
636
637 if ((found_userhost = hash_find_userhost(host)) == NULL)
638 return;
639
640 DLINK_FOREACH(node, found_userhost->list.head)
641 {
642 struct NameHost *nameh = node->data;
643
644 if (!irccmp(user, nameh->name))
645 {
646 if (nameh->gcount > 0)
647 nameh->gcount--;
648
649 if (!global)
650 {
651 if (nameh->lcount > 0)
652 nameh->lcount--;
653
654 if (hasident && nameh->icount > 0)
655 nameh->icount--;
656 }
657
658 if (nameh->gcount == 0 && nameh->lcount == 0)
659 {
660 dlinkDelete(&nameh->node, &found_userhost->list);
661 mp_pool_release(nameh);
662 }
663
664 if (dlink_list_length(&found_userhost->list) == 0)
665 {
666 hash_del_userhost(found_userhost);
667 mp_pool_release(found_userhost);
668 }
669
670 return;
671 }
672 }
673 }
674
675 /*
676 * Safe list code.
677 *
678 * The idea is really quite simple. As the link lists pointed to in
679 * each "bucket" of the channel hash table are traversed atomically
680 * there is no locking needed. Overall, yes, inconsistent reported
681 * state can still happen, but normally this isn't a big deal.
682 * I don't like sticking the code into hash.c but oh well. Moreover,
683 * if a hash isn't used in future, oops.
684 *
685 * - Dianora
686 */
687
688 /* exceeding_sendq()
689 *
690 * inputs - pointer to client to check
691 * output - 1 if client is in danger of blowing its sendq
692 * 0 if it is not.
693 * side effects -
694 *
695 * Sendq limit is fairly conservative at 1/2 (In original anyway)
696 */
697 static int
698 exceeding_sendq(const struct Client *to)
699 {
700 if (dbuf_length(&to->connection->buf_sendq) > (get_sendq(&to->connection->confs) / 2))
701 return 1;
702 else
703 return 0;
704 }
705
706 void
707 free_list_task(struct Client *source_p)
708 {
709 struct ListTask *const lt = source_p->connection->list_task;
710 dlink_node *node = NULL, *node_next = NULL;
711
712 if ((node = dlinkFindDelete(&listing_client_list, source_p)))
713 free_dlink_node(node);
714
715 DLINK_FOREACH_SAFE(node, node_next, lt->show_mask.head)
716 {
717 MyFree(node->data);
718 free_dlink_node(node);
719 }
720
721 DLINK_FOREACH_SAFE(node, node_next, lt->hide_mask.head)
722 {
723 MyFree(node->data);
724 free_dlink_node(node);
725 }
726
727 MyFree(lt);
728 source_p->connection->list_task = NULL;
729 }
730
731 /* list_allow_channel()
732 *
733 * inputs - channel name
734 * - pointer to a list task
735 * output - 1 if the channel is to be displayed
736 * 0 otherwise
737 * side effects -
738 */
739 static int
740 list_allow_channel(const char *name, const struct ListTask *lt)
741 {
742 const dlink_node *node = NULL;
743
744 DLINK_FOREACH(node, lt->show_mask.head)
745 if (match(node->data, name) != 0)
746 return 0;
747
748 DLINK_FOREACH(node, lt->hide_mask.head)
749 if (match(node->data, name) == 0)
750 return 0;
751
752 return 1;
753 }
754
755 /* list_one_channel()
756 *
757 * inputs - client pointer to return result to
758 * - pointer to channel to list
759 * - pointer to ListTask structure
760 * output - none
761 * side effects -
762 */
763 static void
764 list_one_channel(struct Client *source_p, struct Channel *chptr)
765 {
766 const struct ListTask *const lt = source_p->connection->list_task;
767 char listbuf[MODEBUFLEN] = "";
768 char modebuf[MODEBUFLEN] = "";
769 char parabuf[MODEBUFLEN] = "";
770
771 if (SecretChannel(chptr) &&
772 !(HasUMode(source_p, UMODE_ADMIN) || IsMember(source_p, chptr)))
773 return;
774
775 if (dlink_list_length(&chptr->members) < lt->users_min ||
776 dlink_list_length(&chptr->members) > lt->users_max ||
777 (chptr->creationtime != 0 &&
778 ((unsigned int)chptr->creationtime < lt->created_min ||
779 (unsigned int)chptr->creationtime > lt->created_max)) ||
780 (unsigned int)chptr->topic_time < lt->topicts_min ||
781 (chptr->topic_time ? (unsigned int)chptr->topic_time : UINT_MAX) >
782 lt->topicts_max)
783 return;
784
785 if (lt->topic[0] && match(lt->topic, chptr->topic))
786 return;
787
788 if (!list_allow_channel(chptr->name, lt))
789 return;
790
791 channel_modes(chptr, source_p, modebuf, parabuf);
792
793 if (chptr->topic[0])
794 snprintf(listbuf, sizeof(listbuf), "[%s] ", modebuf);
795 else
796 snprintf(listbuf, sizeof(listbuf), "[%s]", modebuf);
797
798 sendto_one_numeric(source_p, &me, RPL_LIST, chptr->name,
799 dlink_list_length(&chptr->members),
800 listbuf, chptr->topic);
801 }
802
803 /* safe_list_channels()
804 *
805 * inputs - pointer to client requesting list
806 * output - 0/1
807 * side effects - safely list all channels to source_p
808 *
809 * Walk the channel buckets, ensure all pointers in a bucket are
810 * traversed before blocking on a sendq. This means, no locking is needed.
811 *
812 * - Dianora
813 */
814 void
815 safe_list_channels(struct Client *source_p, int only_unmasked_channels)
816 {
817 struct ListTask *const lt = source_p->connection->list_task;
818 struct Channel *chptr = NULL;
819
820 if (!only_unmasked_channels)
821 {
822 for (unsigned int i = lt->hash_index; i < HASHSIZE; ++i)
823 {
824 if (exceeding_sendq(source_p))
825 {
826 lt->hash_index = i;
827 return; /* Still more to do */
828 }
829
830 for (chptr = channelTable[i]; chptr; chptr = chptr->hnextch)
831 list_one_channel(source_p, chptr);
832 }
833 }
834 else
835 {
836 dlink_node *node = NULL;
837
838 DLINK_FOREACH(node, lt->show_mask.head)
839 if ((chptr = hash_find_channel(node->data)))
840 list_one_channel(source_p, chptr);
841 }
842
843 free_list_task(source_p);
844 sendto_one_numeric(source_p, &me, RPL_LISTEND);
845 }

Properties

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