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

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2016 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 "id.h"
36 #include "resv.h"
37 #include "rng_mt.h"
38 #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 #include "mempool.h"
45 #include "dbuf.h"
46 #include "user.h"
47
48
49 static unsigned int hashf_xor_key;
50
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 * rebuild the transformation maps, I kept the tables of equal size
54 * 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 /* hash_init()
63 *
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 hash_init(void)
71 {
72 do
73 hashf_xor_key = genrand_int32() % 256; /* better than nothing --adx */
74 while (!hashf_xor_key);
75 }
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 if (EmptyString(p))
91 return 0;
92
93 for (; *p; ++p)
94 {
95 hval += (hval << 1) + (hval << 4) + (hval << 7) +
96 (hval << 8) + (hval << 24);
97 hval ^= (ToLower(*p) ^ hashf_xor_key);
98 }
99
100 return (hval >> FNV1_32_BITS) ^ (hval & ((1 << FNV1_32_BITS) - 1));
101 }
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 const unsigned int hashv = strhash(client_p->name);
126
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 const unsigned int hashv = strhash(chptr->name);
145
146 chptr->hnextch = channelTable[hashv];
147 channelTable[hashv] = chptr;
148 }
149
150 void
151 hash_add_userhost(struct UserHost *userhost)
152 {
153 const unsigned int hashv = strhash(userhost->host);
154
155 userhost->next = userhostTable[hashv];
156 userhostTable[hashv] = userhost;
157 }
158
159 void
160 hash_add_id(struct Client *client_p)
161 {
162 const unsigned int hashv = strhash(client_p->id);
163
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 const unsigned int hashv = strhash(client_p->id);
178 struct Client *tmp = idTable[hashv];
179
180 if (tmp)
181 {
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 const unsigned int hashv = strhash(client_p->name);
209 struct Client *tmp = clientTable[hashv];
210
211 if (tmp)
212 {
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 const unsigned int hashv = strhash(userhost->host);
240 struct UserHost *tmp = userhostTable[hashv];
241
242 if (tmp)
243 {
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 const unsigned int hashv = strhash(chptr->name);
272 struct Channel *tmp = channelTable[hashv];
273
274 if (tmp)
275 {
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 /* hash_find_client()
294 *
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 hash_find_client(const char *name)
303 {
304 const unsigned int hashv = strhash(name);
305 struct Client *client_p;
306
307 if ((client_p = clientTable[hashv]))
308 {
309 if (irccmp(name, client_p->name))
310 {
311 struct Client *prev;
312
313 while (prev = client_p, (client_p = client_p->hnext))
314 {
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 const unsigned int hashv = strhash(name);
333 struct Client *client_p;
334
335 if ((client_p = idTable[hashv]))
336 {
337 if (strcmp(name, client_p->id))
338 {
339 struct Client *prev;
340
341 while (prev = client_p, (client_p = client_p->idhnext))
342 {
343 if (!strcmp(name, client_p->id))
344 {
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 hash_find_server(const char *name)
359 {
360 const unsigned int hashv = strhash(name);
361 struct Client *client_p = NULL;
362
363 if (IsDigit(*name) && strlen(name) == IRC_MAXSID)
364 return hash_find_id(name);
365
366 if ((client_p = clientTable[hashv]))
367 {
368 if ((!IsServer(client_p) && !IsMe(client_p)) ||
369 irccmp(name, client_p->name))
370 {
371 struct Client *prev;
372
373 while (prev = client_p, (client_p = client_p->hnext))
374 {
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 return client_p;
388 }
389
390 /* hash_find_channel()
391 *
392 * inputs - pointer to name
393 * output - NONE
394 * side effects - New semantics: finds a channel whose name is 'name',
395 * 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 const unsigned int hashv = strhash(name);
402 struct Channel *chptr = NULL;
403
404 if ((chptr = channelTable[hashv]))
405 {
406 if (irccmp(name, chptr->name))
407 {
408 struct Channel *prev;
409
410 while (prev = chptr, (chptr = chptr->hnextch))
411 {
412 if (!irccmp(name, chptr->name))
413 {
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 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 /* 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
469 if (hashv >= HASHSIZE)
470 return NULL;
471
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 exceeding_sendq(const struct Client *to)
517 {
518 if (dbuf_length(&to->connection->buf_sendq) > (get_sendq(&to->connection->confs) / 2))
519 return 1;
520 else
521 return 0;
522 }
523
524 void
525 free_list_task(struct Client *source_p)
526 {
527 struct ListTask *const lt = source_p->connection->list_task;
528 dlink_node *node = NULL, *node_next = NULL;
529
530 dlinkDelete(&lt->node, &listing_client_list);
531
532 DLINK_FOREACH_SAFE(node, node_next, lt->show_mask.head)
533 {
534 xfree(node->data);
535 free_dlink_node(node);
536 }
537
538 DLINK_FOREACH_SAFE(node, node_next, lt->hide_mask.head)
539 {
540 xfree(node->data);
541 free_dlink_node(node);
542 }
543
544 xfree(lt);
545 source_p->connection->list_task = NULL;
546 }
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 list_allow_channel(const char *name, const struct ListTask *lt)
558 {
559 const dlink_node *node = NULL;
560
561 DLINK_FOREACH(node, lt->show_mask.head)
562 if (match(node->data, name) != 0)
563 return 0;
564
565 DLINK_FOREACH(node, lt->hide_mask.head)
566 if (match(node->data, name) == 0)
567 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 list_one_channel(struct Client *source_p, struct Channel *chptr)
582 {
583 const struct ListTask *const lt = source_p->connection->list_task;
584 char listbuf[MODEBUFLEN] = "";
585 char modebuf[MODEBUFLEN] = "";
586 char parabuf[MODEBUFLEN] = "";
587
588 if (SecretChannel(chptr) &&
589 !(HasUMode(source_p, UMODE_ADMIN) || IsMember(source_p, chptr)))
590 return;
591
592 if (dlink_list_length(&chptr->members) < lt->users_min ||
593 dlink_list_length(&chptr->members) > lt->users_max ||
594 (chptr->creationtime != 0 &&
595 ((unsigned int)chptr->creationtime < lt->created_min ||
596 (unsigned int)chptr->creationtime > lt->created_max)) ||
597 (unsigned int)chptr->topic_time < lt->topicts_min ||
598 (chptr->topic_time ? (unsigned int)chptr->topic_time : UINT_MAX) >
599 lt->topicts_max)
600 return;
601
602 if (lt->topic[0] && match(lt->topic, chptr->topic))
603 return;
604
605 if (!list_allow_channel(chptr->name, lt))
606 return;
607
608 channel_modes(chptr, source_p, modebuf, parabuf);
609
610 if (chptr->topic[0])
611 snprintf(listbuf, sizeof(listbuf), "[%s] ", modebuf);
612 else
613 snprintf(listbuf, sizeof(listbuf), "[%s]", modebuf);
614
615 sendto_one_numeric(source_p, &me, RPL_LIST, chptr->name,
616 dlink_list_length(&chptr->members),
617 listbuf, chptr->topic);
618 }
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 safe_list_channels(struct Client *source_p, int only_unmasked_channels)
633 {
634 struct ListTask *const lt = source_p->connection->list_task;
635 struct Channel *chptr = NULL;
636
637 if (!only_unmasked_channels)
638 {
639 for (unsigned int i = lt->hash_index; i < HASHSIZE; ++i)
640 {
641 if (exceeding_sendq(source_p))
642 {
643 lt->hash_index = i;
644 return; /* Still more to do */
645 }
646
647 for (chptr = channelTable[i]; chptr; chptr = chptr->hnextch)
648 list_one_channel(source_p, chptr);
649 }
650 }
651 else
652 {
653 dlink_node *node = NULL;
654
655 DLINK_FOREACH(node, lt->show_mask.head)
656 if ((chptr = hash_find_channel(node->data)))
657 list_one_channel(source_p, chptr);
658 }
659
660 free_list_task(source_p);
661 sendto_one_numeric(source_p, &me, RPL_LISTEND);
662 }

Properties

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