ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/hash.c
Revision: 7032
Committed: Sun Jan 3 14:34:39 2016 UTC (9 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 16163 byte(s)
Log Message:
- Renamed MyCalloc to xcalloc

File Contents

# User Rev Content
1 adx 30 /*
2 michael 2916 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 adx 30 *
4 michael 7006 * Copyright (c) 1997-2016 ircd-hybrid development team
5 adx 30 *
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 michael 4565 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 adx 30 * USA
20     */
21    
22 michael 2916 /*! \file hash.c
23     * \brief Hash table management.
24     * \version $Id$
25     */
26    
27 adx 30 #include "stdinc.h"
28 michael 1011 #include "list.h"
29 michael 1309 #include "conf.h"
30 adx 30 #include "channel.h"
31     #include "channel_mode.h"
32     #include "client.h"
33     #include "modules.h"
34     #include "hash.h"
35 michael 6161 #include "id.h"
36 adx 30 #include "resv.h"
37 michael 982 #include "rng_mt.h"
38 adx 30 #include "userhost.h"
39     #include "irc_string.h"
40     #include "ircd.h"
41     #include "numeric.h"
42     #include "send.h"
43     #include "memory.h"
44 michael 1654 #include "mempool.h"
45 adx 30 #include "dbuf.h"
46 michael 3347 #include "user.h"
47 adx 30
48    
49 michael 5460 static unsigned int hashf_xor_key;
50 adx 30
51     /* The actual hash tables, They MUST be of the same HASHSIZE, variable
52     * size tables could be supported but the rehash routine should also
53 michael 2916 * rebuild the transformation maps, I kept the tables of equal size
54 adx 30 * so that I can use one hash function.
55     */
56     static struct Client *idTable[HASHSIZE];
57     static struct Client *clientTable[HASHSIZE];
58     static struct Channel *channelTable[HASHSIZE];
59     static struct UserHost *userhostTable[HASHSIZE];
60    
61    
62 michael 6393 /* hash_init()
63 adx 30 *
64     * inputs - NONE
65     * output - NONE
66     * side effects - Initialize the maps used by hash
67     * functions and clear the tables
68     */
69     void
70 michael 1798 hash_init(void)
71 adx 30 {
72 michael 7031 do
73     hashf_xor_key = genrand_int32() % 256; /* better than nothing --adx */
74     while (!hashf_xor_key);
75 adx 30 }
76    
77     /*
78     * New hash function based on the Fowler/Noll/Vo (FNV) algorithm from
79     * http://www.isthe.com/chongo/tech/comp/fnv/
80     *
81     * Here, we use the FNV-1 method, which gives slightly better results
82     * than FNV-1a. -Michael
83     */
84     unsigned int
85     strhash(const char *name)
86     {
87     const unsigned char *p = (const unsigned char *)name;
88     unsigned int hval = FNV1_32_INIT;
89    
90 michael 1826 if (EmptyString(p))
91 adx 30 return 0;
92 michael 4890
93     for (; *p; ++p)
94 adx 30 {
95     hval += (hval << 1) + (hval << 4) + (hval << 7) +
96     (hval << 8) + (hval << 24);
97 michael 982 hval ^= (ToLower(*p) ^ hashf_xor_key);
98 adx 30 }
99    
100 michael 982 return (hval >> FNV1_32_BITS) ^ (hval & ((1 << FNV1_32_BITS) - 1));
101 adx 30 }
102    
103     /************************** Externally visible functions ********************/
104    
105     /* Optimization note: in these functions I supposed that the CSE optimization
106     * (Common Subexpression Elimination) does its work decently, this means that
107     * I avoided introducing new variables to do the work myself and I did let
108     * the optimizer play with more free registers, actual tests proved this
109     * solution to be faster than doing things like tmp2=tmp->hnext... and then
110     * use tmp2 myself which would have given less freedom to the optimizer.
111     */
112    
113     /* hash_add_client()
114     *
115     * inputs - pointer to client
116     * output - NONE
117     * side effects - Adds a client's name in the proper hash linked
118     * list, can't fail, client_p must have a non-null
119     * name or expect a coredump, the name is infact
120     * taken from client_p->name
121     */
122     void
123     hash_add_client(struct Client *client_p)
124     {
125 michael 4977 const unsigned int hashv = strhash(client_p->name);
126 adx 30
127     client_p->hnext = clientTable[hashv];
128     clientTable[hashv] = client_p;
129     }
130    
131     /* hash_add_channel()
132     *
133     * inputs - pointer to channel
134     * output - NONE
135     * side effects - Adds a channel's name in the proper hash linked
136     * list, can't fail. chptr must have a non-null name
137     * or expect a coredump. As before the name is taken
138     * from chptr->name, we do hash its entire lenght
139     * since this proved to be statistically faster
140     */
141     void
142     hash_add_channel(struct Channel *chptr)
143     {
144 michael 4977 const unsigned int hashv = strhash(chptr->name);
145 adx 30
146     chptr->hnextch = channelTable[hashv];
147     channelTable[hashv] = chptr;
148     }
149    
150     void
151     hash_add_userhost(struct UserHost *userhost)
152     {
153 michael 4977 const unsigned int hashv = strhash(userhost->host);
154 adx 30
155     userhost->next = userhostTable[hashv];
156     userhostTable[hashv] = userhost;
157     }
158    
159     void
160     hash_add_id(struct Client *client_p)
161     {
162 michael 4977 const unsigned int hashv = strhash(client_p->id);
163 adx 30
164     client_p->idhnext = idTable[hashv];
165     idTable[hashv] = client_p;
166     }
167    
168     /* hash_del_id()
169     *
170     * inputs - pointer to client
171     * output - NONE
172     * side effects - Removes an ID from the hash linked list
173     */
174     void
175     hash_del_id(struct Client *client_p)
176     {
177 michael 4977 const unsigned int hashv = strhash(client_p->id);
178 adx 30 struct Client *tmp = idTable[hashv];
179    
180 michael 3241 if (tmp)
181 adx 30 {
182     if (tmp == client_p)
183     {
184     idTable[hashv] = client_p->idhnext;
185     client_p->idhnext = client_p;
186     }
187     else
188     {
189     while (tmp->idhnext != client_p)
190     if ((tmp = tmp->idhnext) == NULL)
191     return;
192    
193     tmp->idhnext = tmp->idhnext->idhnext;
194     client_p->idhnext = client_p;
195     }
196     }
197     }
198    
199     /* hash_del_client()
200     *
201     * inputs - pointer to client
202     * output - NONE
203     * side effects - Removes a Client's name from the hash linked list
204     */
205     void
206     hash_del_client(struct Client *client_p)
207     {
208 michael 4977 const unsigned int hashv = strhash(client_p->name);
209 adx 30 struct Client *tmp = clientTable[hashv];
210    
211 michael 3241 if (tmp)
212 adx 30 {
213     if (tmp == client_p)
214     {
215     clientTable[hashv] = client_p->hnext;
216     client_p->hnext = client_p;
217     }
218     else
219     {
220     while (tmp->hnext != client_p)
221     if ((tmp = tmp->hnext) == NULL)
222     return;
223    
224     tmp->hnext = tmp->hnext->hnext;
225     client_p->hnext = client_p;
226     }
227     }
228     }
229    
230     /* hash_del_userhost()
231     *
232     * inputs - pointer to userhost
233     * output - NONE
234     * side effects - Removes a userhost from the hash linked list
235     */
236     void
237     hash_del_userhost(struct UserHost *userhost)
238     {
239 michael 4977 const unsigned int hashv = strhash(userhost->host);
240 adx 30 struct UserHost *tmp = userhostTable[hashv];
241    
242 michael 3241 if (tmp)
243 adx 30 {
244     if (tmp == userhost)
245     {
246     userhostTable[hashv] = userhost->next;
247     userhost->next = userhost;
248     }
249     else
250     {
251     while (tmp->next != userhost)
252     if ((tmp = tmp->next) == NULL)
253     return;
254    
255     tmp->next = tmp->next->next;
256     userhost->next = userhost;
257     }
258     }
259     }
260    
261     /* hash_del_channel()
262     *
263     * inputs - pointer to client
264     * output - NONE
265     * side effects - Removes the channel's name from the corresponding
266     * hash linked list
267     */
268     void
269     hash_del_channel(struct Channel *chptr)
270     {
271 michael 4977 const unsigned int hashv = strhash(chptr->name);
272 adx 30 struct Channel *tmp = channelTable[hashv];
273    
274 michael 3241 if (tmp)
275 adx 30 {
276     if (tmp == chptr)
277     {
278     channelTable[hashv] = chptr->hnextch;
279     chptr->hnextch = chptr;
280     }
281     else
282     {
283     while (tmp->hnextch != chptr)
284     if ((tmp = tmp->hnextch) == NULL)
285     return;
286    
287     tmp->hnextch = tmp->hnextch->hnextch;
288     chptr->hnextch = chptr;
289     }
290     }
291     }
292    
293 michael 1169 /* hash_find_client()
294 adx 30 *
295     * inputs - pointer to name
296     * output - NONE
297     * side effects - New semantics: finds a client whose name is 'name'
298     * if can't find one returns NULL. If it finds one moves
299     * it to the top of the list and returns it.
300     */
301     struct Client *
302 michael 1169 hash_find_client(const char *name)
303 adx 30 {
304 michael 4977 const unsigned int hashv = strhash(name);
305 adx 30 struct Client *client_p;
306    
307 michael 3241 if ((client_p = clientTable[hashv]))
308 adx 30 {
309     if (irccmp(name, client_p->name))
310     {
311     struct Client *prev;
312    
313 michael 3241 while (prev = client_p, (client_p = client_p->hnext))
314 adx 30 {
315     if (!irccmp(name, client_p->name))
316     {
317     prev->hnext = client_p->hnext;
318     client_p->hnext = clientTable[hashv];
319     clientTable[hashv] = client_p;
320     break;
321     }
322     }
323     }
324     }
325    
326     return client_p;
327     }
328    
329     struct Client *
330     hash_find_id(const char *name)
331     {
332 michael 4977 const unsigned int hashv = strhash(name);
333 adx 30 struct Client *client_p;
334    
335 michael 3242 if ((client_p = idTable[hashv]))
336 adx 30 {
337 michael 880 if (strcmp(name, client_p->id))
338 adx 30 {
339     struct Client *prev;
340    
341 michael 3242 while (prev = client_p, (client_p = client_p->idhnext))
342 adx 30 {
343 michael 880 if (!strcmp(name, client_p->id))
344 adx 30 {
345     prev->idhnext = client_p->idhnext;
346     client_p->idhnext = idTable[hashv];
347     idTable[hashv] = client_p;
348     break;
349     }
350     }
351     }
352     }
353    
354     return client_p;
355     }
356    
357     struct Client *
358 michael 1169 hash_find_server(const char *name)
359 adx 30 {
360 michael 4977 const unsigned int hashv = strhash(name);
361 adx 30 struct Client *client_p = NULL;
362    
363     if (IsDigit(*name) && strlen(name) == IRC_MAXSID)
364 michael 1397 return hash_find_id(name);
365 adx 30
366 michael 3242 if ((client_p = clientTable[hashv]))
367 adx 30 {
368     if ((!IsServer(client_p) && !IsMe(client_p)) ||
369     irccmp(name, client_p->name))
370     {
371     struct Client *prev;
372    
373 michael 3242 while (prev = client_p, (client_p = client_p->hnext))
374 adx 30 {
375     if ((IsServer(client_p) || IsMe(client_p)) &&
376     !irccmp(name, client_p->name))
377     {
378     prev->hnext = client_p->hnext;
379     client_p->hnext = clientTable[hashv];
380     clientTable[hashv] = client_p;
381     break;
382     }
383     }
384     }
385     }
386    
387 michael 1118 return client_p;
388 adx 30 }
389    
390     /* hash_find_channel()
391     *
392     * inputs - pointer to name
393     * output - NONE
394 michael 2916 * side effects - New semantics: finds a channel whose name is 'name',
395 adx 30 * if can't find one returns NULL, if can find it moves
396     * it to the top of the list and returns it.
397     */
398     struct Channel *
399     hash_find_channel(const char *name)
400     {
401 michael 4977 const unsigned int hashv = strhash(name);
402 adx 30 struct Channel *chptr = NULL;
403    
404 michael 3242 if ((chptr = channelTable[hashv]))
405 adx 30 {
406 michael 4618 if (irccmp(name, chptr->name))
407 adx 30 {
408     struct Channel *prev;
409    
410 michael 3242 while (prev = chptr, (chptr = chptr->hnextch))
411 adx 30 {
412 michael 4618 if (!irccmp(name, chptr->name))
413 adx 30 {
414     prev->hnextch = chptr->hnextch;
415     chptr->hnextch = channelTable[hashv];
416     channelTable[hashv] = chptr;
417     break;
418     }
419     }
420     }
421     }
422    
423     return chptr;
424     }
425    
426 michael 6393 struct UserHost *
427     hash_find_userhost(const char *host)
428     {
429     const unsigned int hashv = strhash(host);
430     struct UserHost *userhost;
431    
432     if ((userhost = userhostTable[hashv]))
433     {
434     if (irccmp(host, userhost->host))
435     {
436     struct UserHost *prev;
437    
438     while (prev = userhost, (userhost = userhost->next))
439     {
440     if (!irccmp(host, userhost->host))
441     {
442     prev->next = userhost->next;
443     userhost->next = userhostTable[hashv];
444     userhostTable[hashv] = userhost;
445     break;
446     }
447     }
448     }
449     }
450    
451     return userhost;
452     }
453    
454 adx 30 /* hash_get_bucket(int type, unsigned int hashv)
455     *
456     * inputs - hash value (must be between 0 and HASHSIZE - 1)
457     * output - NONE
458     * returns - pointer to first channel in channelTable[hashv]
459     * if that exists;
460     * NULL if there is no channel in that place;
461     * NULL if hashv is an invalid number.
462     * side effects - NONE
463     */
464     void *
465     hash_get_bucket(int type, unsigned int hashv)
466     {
467     assert(hashv < HASHSIZE);
468 michael 4977
469 adx 30 if (hashv >= HASHSIZE)
470 michael 6393 return NULL;
471 adx 30
472     switch (type)
473     {
474     case HASH_TYPE_ID:
475     return idTable[hashv];
476     break;
477     case HASH_TYPE_CHANNEL:
478     return channelTable[hashv];
479     break;
480     case HASH_TYPE_CLIENT:
481     return clientTable[hashv];
482     break;
483     case HASH_TYPE_USERHOST:
484     return userhostTable[hashv];
485     break;
486     default:
487     assert(0);
488     }
489    
490     return NULL;
491     }
492    
493     /*
494     * Safe list code.
495     *
496     * The idea is really quite simple. As the link lists pointed to in
497     * each "bucket" of the channel hash table are traversed atomically
498     * there is no locking needed. Overall, yes, inconsistent reported
499     * state can still happen, but normally this isn't a big deal.
500     * I don't like sticking the code into hash.c but oh well. Moreover,
501     * if a hash isn't used in future, oops.
502     *
503     * - Dianora
504     */
505    
506     /* exceeding_sendq()
507     *
508     * inputs - pointer to client to check
509     * output - 1 if client is in danger of blowing its sendq
510     * 0 if it is not.
511     * side effects -
512     *
513     * Sendq limit is fairly conservative at 1/2 (In original anyway)
514     */
515     static int
516 michael 2757 exceeding_sendq(const struct Client *to)
517 adx 30 {
518 michael 4588 if (dbuf_length(&to->connection->buf_sendq) > (get_sendq(&to->connection->confs) / 2))
519 adx 30 return 1;
520     else
521     return 0;
522     }
523    
524     void
525 michael 3289 free_list_task(struct Client *source_p)
526 adx 30 {
527 michael 4870 struct ListTask *const lt = source_p->connection->list_task;
528 michael 4815 dlink_node *node = NULL, *node_next = NULL;
529 adx 30
530 michael 6385 dlinkDelete(&lt->node, &listing_client_list);
531 adx 30
532 michael 4815 DLINK_FOREACH_SAFE(node, node_next, lt->show_mask.head)
533 adx 30 {
534 michael 7032 xfree(node->data);
535 michael 4815 free_dlink_node(node);
536 adx 30 }
537    
538 michael 4815 DLINK_FOREACH_SAFE(node, node_next, lt->hide_mask.head)
539 adx 30 {
540 michael 7032 xfree(node->data);
541 michael 4815 free_dlink_node(node);
542 adx 30 }
543    
544 michael 7032 xfree(lt);
545 michael 4868 source_p->connection->list_task = NULL;
546 adx 30 }
547    
548     /* list_allow_channel()
549     *
550     * inputs - channel name
551     * - pointer to a list task
552     * output - 1 if the channel is to be displayed
553     * 0 otherwise
554     * side effects -
555     */
556     static int
557 michael 4618 list_allow_channel(const char *name, const struct ListTask *lt)
558 adx 30 {
559 michael 4815 const dlink_node *node = NULL;
560 adx 30
561 michael 4815 DLINK_FOREACH(node, lt->show_mask.head)
562     if (match(node->data, name) != 0)
563 adx 30 return 0;
564    
565 michael 4815 DLINK_FOREACH(node, lt->hide_mask.head)
566     if (match(node->data, name) == 0)
567 adx 30 return 0;
568    
569     return 1;
570     }
571    
572     /* list_one_channel()
573     *
574     * inputs - client pointer to return result to
575     * - pointer to channel to list
576     * - pointer to ListTask structure
577     * output - none
578     * side effects -
579     */
580     static void
581 michael 3288 list_one_channel(struct Client *source_p, struct Channel *chptr)
582 adx 30 {
583 michael 4870 const struct ListTask *const lt = source_p->connection->list_task;
584 michael 2616 char listbuf[MODEBUFLEN] = "";
585     char modebuf[MODEBUFLEN] = "";
586     char parabuf[MODEBUFLEN] = "";
587    
588 michael 2495 if (SecretChannel(chptr) &&
589 michael 3428 !(HasUMode(source_p, UMODE_ADMIN) || IsMember(source_p, chptr)))
590 adx 30 return;
591 michael 4890
592 michael 3288 if (dlink_list_length(&chptr->members) < lt->users_min ||
593     dlink_list_length(&chptr->members) > lt->users_max ||
594 michael 4818 (chptr->creationtime != 0 &&
595     ((unsigned int)chptr->creationtime < lt->created_min ||
596     (unsigned int)chptr->creationtime > lt->created_max)) ||
597 michael 3288 (unsigned int)chptr->topic_time < lt->topicts_min ||
598 adx 30 (chptr->topic_time ? (unsigned int)chptr->topic_time : UINT_MAX) >
599 michael 3288 lt->topicts_max)
600 adx 30 return;
601    
602 michael 4489 if (lt->topic[0] && match(lt->topic, chptr->topic))
603     return;
604    
605 michael 4618 if (!list_allow_channel(chptr->name, lt))
606 adx 30 return;
607 michael 2616
608 michael 4486 channel_modes(chptr, source_p, modebuf, parabuf);
609 michael 2616
610 michael 4486 if (chptr->topic[0])
611     snprintf(listbuf, sizeof(listbuf), "[%s] ", modebuf);
612     else
613     snprintf(listbuf, sizeof(listbuf), "[%s]", modebuf);
614 michael 2616
615 michael 4618 sendto_one_numeric(source_p, &me, RPL_LIST, chptr->name,
616 michael 3109 dlink_list_length(&chptr->members),
617     listbuf, chptr->topic);
618 adx 30 }
619    
620     /* safe_list_channels()
621     *
622     * inputs - pointer to client requesting list
623     * output - 0/1
624     * side effects - safely list all channels to source_p
625     *
626     * Walk the channel buckets, ensure all pointers in a bucket are
627     * traversed before blocking on a sendq. This means, no locking is needed.
628     *
629     * - Dianora
630     */
631     void
632 michael 3288 safe_list_channels(struct Client *source_p, int only_unmasked_channels)
633 adx 30 {
634 michael 4870 struct ListTask *const lt = source_p->connection->list_task;
635 adx 30 struct Channel *chptr = NULL;
636    
637     if (!only_unmasked_channels)
638     {
639 michael 3288 for (unsigned int i = lt->hash_index; i < HASHSIZE; ++i)
640 adx 30 {
641 michael 3291 if (exceeding_sendq(source_p))
642 adx 30 {
643 michael 3288 lt->hash_index = i;
644     return; /* Still more to do */
645 adx 30 }
646    
647     for (chptr = channelTable[i]; chptr; chptr = chptr->hnextch)
648 michael 3288 list_one_channel(source_p, chptr);
649 adx 30 }
650     }
651     else
652     {
653 michael 4815 dlink_node *node = NULL;
654 adx 30
655 michael 4815 DLINK_FOREACH(node, lt->show_mask.head)
656     if ((chptr = hash_find_channel(node->data)))
657 michael 3288 list_one_channel(source_p, chptr);
658 adx 30 }
659    
660 michael 3289 free_list_task(source_p);
661 michael 3109 sendto_one_numeric(source_p, &me, RPL_LISTEND);
662 adx 30 }

Properties

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