2 * IRC - Internet Relay Chat, ircd/hash.c
3 * Copyright (C) 1998 Andrea Cocito, completely rewritten version.
4 * Previous version was Copyright (C) 1991 Darren Reed, the concept
5 * of linked lists for each hash bucket and the move-to-head
6 * optimization has been borrowed from there.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 1, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "ircd_chattr.h"
28 #include "ircd_string.h"
43 * @brief Hash table management.
46 * This file used to use some very complicated hash function. Now it
47 * uses CRC-32, but effectively remaps each input byte according to a
48 * table initialized at startup.
51 /** Hash table for clients. */
52 static struct Client *clientTable[HASHSIZE];
53 /** Hash table for channels. */
54 static struct Channel *channelTable[HASHSIZE];
55 /** CRC-32 update table. */
56 static uint32_t crc32hash[256];
58 /** Initialize the map used by the hash function. */
61 unsigned int ii, jj, rand, poly;
63 /* First calculate a normal CRC-32 table. */
64 for (ii = 0, poly = 0xedb88320; ii < 256; ii++)
67 for (jj = 0; jj < 8; jj++)
68 rand = (rand & 1) ? poly ^ (rand >> 1) : rand >> 1;
72 /* Now reorder the hash table. */
73 for (ii = 0, rand = 0; ii < 256; ii++)
77 poly = ii + rand % (256 - ii);
79 crc32hash[ii] = crc32hash[poly];
85 /** Output type of hash function. */
86 typedef unsigned int HASHREGS;
88 /** Calculate hash value for a string.
89 * @param[in] n String to hash.
90 * @return Hash value for string.
92 static HASHREGS strhash(const char *n)
94 HASHREGS hash = crc32hash[ToLower(*n++) & 255];
96 hash = (hash >> 8) ^ crc32hash[(hash ^ ToLower(*n++)) & 255];
97 return hash % HASHSIZE;
100 /************************** Externally visible functions ********************/
102 /* Optimization note: in these functions I supposed that the CSE optimization
103 * (Common Subexpression Elimination) does its work decently, this means that
104 * I avoided introducing new variables to do the work myself and I did let
105 * the optimizer play with more free registers, actual tests proved this
106 * solution to be faster than doing things like tmp2=tmp->hnext... and then
107 * use tmp2 myself wich would have given less freedom to the optimizer.
110 /** Prepend a client to the appropriate hash bucket.
111 * @param[in] cptr Client to add to hash table.
114 int hAddClient(struct Client *cptr)
116 HASHREGS hashv = strhash(cli_name(cptr));
118 cli_hnext(cptr) = clientTable[hashv];
119 clientTable[hashv] = cptr;
124 /** Prepend a channel to the appropriate hash bucket.
125 * @param[in] chptr Channel to add to hash table.
128 int hAddChannel(struct Channel *chptr)
130 HASHREGS hashv = strhash(chptr->chname);
132 chptr->hnext = channelTable[hashv];
133 channelTable[hashv] = chptr;
138 /** Remove a client from its hash bucket.
139 * @param[in] cptr Client to remove from hash table.
140 * @return Zero if the client is found and removed, -1 if not found.
142 int hRemClient(struct Client *cptr)
144 HASHREGS hashv = strhash(cli_name(cptr));
145 struct Client *tmp = clientTable[hashv];
148 clientTable[hashv] = cli_hnext(cptr);
149 cli_hnext(cptr) = cptr;
154 if (cli_hnext(tmp) == cptr) {
155 cli_hnext(tmp) = cli_hnext(cli_hnext(tmp));
156 cli_hnext(cptr) = cptr;
159 tmp = cli_hnext(tmp);
164 /** Rename a client in the hash table.
165 * @param[in] cptr Client whose nickname is changing.
166 * @param[in] newname New nickname for client.
169 int hChangeClient(struct Client *cptr, const char *newname)
171 HASHREGS newhash = strhash(newname);
176 cli_hnext(cptr) = clientTable[newhash];
177 clientTable[newhash] = cptr;
181 /** Remove a channel from its hash bucket.
182 * @param[in] chptr Channel to remove from hash table.
183 * @return Zero if the channel is found and removed, -1 if not found.
185 int hRemChannel(struct Channel *chptr)
187 HASHREGS hashv = strhash(chptr->chname);
188 struct Channel *tmp = channelTable[hashv];
191 channelTable[hashv] = chptr->hnext;
192 chptr->hnext = chptr;
197 if (tmp->hnext == chptr) {
198 tmp->hnext = tmp->hnext->hnext;
199 chptr->hnext = chptr;
208 /** Find a client by name, filtered by status mask.
209 * If a client is found, it is moved to the top of its hash bucket.
210 * @param[in] name Client name to search for.
211 * @param[in] TMask Bitmask of status bits, any of which are needed to match.
212 * @return Matching client, or NULL if none.
214 struct Client* hSeekClient(const char *name, int TMask)
216 HASHREGS hashv = strhash(name);
217 struct Client *cptr = clientTable[hashv];
220 if (0 == (cli_status(cptr) & TMask) || 0 != ircd_strcmp(name, cli_name(cptr))) {
222 while (prev = cptr, cptr = cli_hnext(cptr)) {
223 if ((cli_status(cptr) & TMask) && (0 == ircd_strcmp(name, cli_name(cptr)))) {
224 cli_hnext(prev) = cli_hnext(cptr);
225 cli_hnext(cptr) = clientTable[hashv];
226 clientTable[hashv] = cptr;
235 /** Find a channel by name.
236 * If a channel is found, it is moved to the top of its hash bucket.
237 * @param[in] name Channel name to search for.
238 * @return Matching channel, or NULL if none.
240 struct Channel* hSeekChannel(const char *name)
242 HASHREGS hashv = strhash(name);
243 struct Channel *chptr = channelTable[hashv];
246 if (0 != ircd_strcmp(name, chptr->chname)) {
247 struct Channel* prev;
248 while (prev = chptr, chptr = chptr->hnext) {
249 if (0 == ircd_strcmp(name, chptr->chname)) {
250 prev->hnext = chptr->hnext;
251 chptr->hnext = channelTable[hashv];
252 channelTable[hashv] = chptr;
262 /* I will add some useful(?) statistics here one of these days,
263 but not for DEBUGMODE: just to let the admins play with it,
264 coders are able to SIGCORE the server and look into what goes
267 /** Report hash table statistics to a client.
268 * @param[in] cptr Client that sent us this message.
269 * @param[in] sptr Client that originated the message.
270 * @param[in] parc Number of arguments.
271 * @param[in] parv Argument array.
274 int m_hash(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
283 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Hash Table Statistics", sptr);
285 for (i = 0; i < HASHSIZE; ++i) {
286 if ((cl = clientTable[i])) {
289 for ( ; cl; cl = cli_hnext(cl))
297 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Client: entries: %d buckets: %d "
298 "max chain: %d", sptr, count, buckets, max_chain);
304 for (i = 0; i < HASHSIZE; ++i) {
305 if ((ch = channelTable[i])) {
308 for ( ; ch; ch = ch->hnext)
316 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Channel: entries: %d buckets: %d "
317 "max chain: %d", sptr, count, buckets, max_chain);
321 /* Nick jupe utilities, these are in a static hash table with entry/bucket
322 ratio of one, collision shift up and roll in a circular fashion, the
323 lowest 12 bits of the hash value are used, deletion is not supported,
324 only addition, test for existence and cleanup of the table are.. */
326 /** Number of bits in jupe hash value. */
327 #define JUPEHASHBITS 12 /* 4096 entries, 64 nick jupes allowed */
328 /** Size of jupe hash table. */
329 #define JUPEHASHSIZE (1<<JUPEHASHBITS)
330 /** Bitmask to select into jupe hash table. */
331 #define JUPEHASHMASK (JUPEHASHSIZE-1)
332 /** Maximum number of jupes allowed. */
333 #define JUPEMAX (1<<(JUPEHASHBITS-6))
335 /** Hash table for jupes. */
336 static char jupeTable[JUPEHASHSIZE][NICKLEN + 1]; /* About 40k */
337 /** Count of jupes. */
338 static int jupesCount;
340 /** Check whether a nickname is juped.
341 * @param[in] nick Nickname to check.
342 * @return Non-zero of the nickname is juped, zero if not.
344 int isNickJuped(const char *nick)
349 for (pos = strhash(nick); (pos &= JUPEHASHMASK), jupeTable[pos][0]; pos++) {
350 if (0 == ircd_strcmp(nick, jupeTable[pos]))
354 return 0; /* A bogus pointer is NOT a juped nick, right ? :) */
357 /** Add a comma-separated list of nick jupes.
358 * @param[in] nicks List of nicks to jupe, separated by commas.
359 * @return Zero on success, non-zero on error.
361 int addNickJupes(const char *nicks)
363 static char temp[BUFSIZE + 1];
370 ircd_strncpy(temp, nicks, BUFSIZE);
371 temp[BUFSIZE] = '\0';
373 for (one = ircd_strtok(&p, temp, ","); one; one = ircd_strtok(&p, NULL, ","))
380 if (!jupeTable[pos][0])
382 if (jupesCount == JUPEMAX)
383 return 1; /* Error: Jupe table is full ! */
385 ircd_strncpy(jupeTable[pos], one, NICKLEN);
386 jupeTable[pos][NICKLEN] = '\000'; /* Better safe than sorry :) */
389 if (0 == ircd_strcmp(one, jupeTable[pos]))
398 /** Empty the table of juped nicknames. */
399 void clearNickJupes(void)
403 for (i = 0; i < JUPEHASHSIZE; i++)
404 jupeTable[i][0] = '\000';