This commit is contained in:
2023-12-26 16:40:53 -05:00
commit ab64084f63
93 changed files with 40857 additions and 0 deletions

755
ircd/hash.c Normal file
View File

@@ -0,0 +1,755 @@
/************************************************************************
* IRC - Internet Relay Chat, ircd/hash.c
* Copyright (C) 1991 Darren Reed
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char sccsid[] = "@(#)hash.c 2.10 7/3/93 (C) 1991 Darren Reed";
#endif
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "hash.h"
#include "h.h"
#ifdef DEBUGMODE
static aHashEntry *clientTable = NULL;
static aHashEntry *channelTable = NULL;
static int clhits, clmiss;
static int chhits, chmiss;
int HASHSIZE = 2003;
int CHANNELHASHSIZE = 607;
#else
static aHashEntry clientTable[HASHSIZE];
static aHashEntry channelTable[CHANNELHASHSIZE];
#endif
static int hash_mult[] = { 173, 179, 181, 191, 193, 197,
199, 211, 223, 227, 229, 233,
239, 241, 251, 257, 263, 269,
271, 277, 281, 293, 307, 311,
401, 409, 419, 421, 431, 433,
439, 443, 449, 457, 461, 463
};
/*
* Hashing.
*
* The server uses a chained hash table to provide quick and efficient
* hash table mantainence (providing the hash function works evenly over
* the input range). The hash table is thus not susceptible to problems
* of filling all the buckets or the need to rehash.
* It is expected that the hash table would look somehting like this
* during use:
* +-----+ +-----+ +-----+ +-----+
* ---| 224 |----| 225 |----| 226 |---| 227 |---
* +-----+ +-----+ +-----+ +-----+
* | | |
* +-----+ +-----+ +-----+
* | A | | C | | D |
* +-----+ +-----+ +-----+
* |
* +-----+
* | B |
* +-----+
*
* A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
*
* The order shown above is just one instant of the server. Each time a
* lookup is made on an entry in the hash table and it is found, the entry
* is moved to the top of the chain.
*/
/*
* hash_nick_name
*
* this function must be *quick*. Thus there should be no multiplication
* or division or modulus in the inner loop. subtraction and other bit
* operations allowed.
*/
int hash_nick_name(nname)
char *nname;
{
Reg1 u_char *name = (u_char *)nname;
Reg2 u_char ch;
Reg4 int hash = 1, *tab;
for (tab = hash_mult; (ch = *name); name++, tab++)
hash += tolower(ch) + *tab + hash;
if (hash < 0)
hash = -hash;
hash %= HASHSIZE;
return (hash);
}
/*
* hash_channel_name
*
* calculate a hash value on at most the first 30 characters of the channel
* name. Most names are short than this or dissimilar in this range. There
* is little or no point hashing on a full channel name which maybe 255 chars
* long.
*/
int hash_channel_name(hname)
char *hname;
{
Reg1 u_char *name = (u_char *)hname;
Reg2 u_char ch;
Reg3 int i = 30;
Reg4 int hash = 5, *tab;
for (tab = hash_mult; (ch = *name) && --i; name++, tab++)
hash += tolower(ch) + *tab + hash + i + i;
if (hash < 0)
hash = -hash;
hash %= CHANNELHASHSIZE;
return (hash);
}
/*
* clear_*_hash_table
*
* Nullify the hashtable and its contents so it is completely empty.
*/
void clear_client_hash_table()
{
#ifdef DEBUGMODE
clhits = 0;
clmiss = 0;
if (!clientTable)
clientTable = (aHashEntry *)MyMalloc(HASHSIZE *
sizeof(aHashEntry));
#endif
bzero((char *)clientTable, sizeof(aHashEntry) * HASHSIZE);
}
void clear_channel_hash_table()
{
#ifdef DEBUGMODE
chmiss = 0;
chhits = 0;
if (!channelTable)
channelTable = (aHashEntry *)MyMalloc(CHANNELHASHSIZE *
sizeof(aHashEntry));
#endif
bzero((char *)channelTable, sizeof(aHashEntry) * CHANNELHASHSIZE);
}
/*
* add_to_client_hash_table
*/
int add_to_client_hash_table(name, cptr)
char *name;
aClient *cptr;
{
Reg1 int hashv;
hashv = hash_nick_name(name);
cptr->hnext = (aClient *)clientTable[hashv].list;
clientTable[hashv].list = (void *)cptr;
clientTable[hashv].links++;
clientTable[hashv].hits++;
return 0;
}
/*
* add_to_channel_hash_table
*/
int add_to_channel_hash_table(name, chptr)
char *name;
aChannel *chptr;
{
Reg1 int hashv;
hashv = hash_channel_name(name);
chptr->hnextch = (aChannel *)channelTable[hashv].list;
channelTable[hashv].list = (void *)chptr;
channelTable[hashv].links++;
channelTable[hashv].hits++;
return 0;
}
/*
* del_from_client_hash_table
*/
int del_from_client_hash_table(name, cptr)
char *name;
aClient *cptr;
{
Reg1 aClient *tmp, *prev = NULL;
Reg2 int hashv;
hashv = hash_nick_name(name);
for (tmp = (aClient *)clientTable[hashv].list; tmp; tmp = tmp->hnext)
{
if (tmp == cptr)
{
if (prev)
prev->hnext = tmp->hnext;
else
clientTable[hashv].list = (void *)tmp->hnext;
tmp->hnext = NULL;
if (clientTable[hashv].links > 0)
{
clientTable[hashv].links--;
return 1;
}
else
/*
* Should never actually return from here and
* if we do it is an error/inconsistency in the
* hash table.
*/
return -1;
}
prev = tmp;
}
return 0;
}
/*
* del_from_channel_hash_table
*/
int del_from_channel_hash_table(name, chptr)
char *name;
aChannel *chptr;
{
Reg1 aChannel *tmp, *prev = NULL;
Reg2 int hashv;
hashv = hash_channel_name(name);
for (tmp = (aChannel *)channelTable[hashv].list; tmp;
tmp = tmp->hnextch)
{
if (tmp == chptr)
{
if (prev)
prev->hnextch = tmp->hnextch;
else
channelTable[hashv].list=(void *)tmp->hnextch;
tmp->hnextch = NULL;
if (channelTable[hashv].links > 0)
{
channelTable[hashv].links--;
return 1;
}
else
return -1;
}
prev = tmp;
}
return 0;
}
/*
* hash_find_client
*/
aClient *hash_find_client(name, cptr)
char *name;
aClient *cptr;
{
Reg1 aClient *tmp;
Reg2 aClient *prv = NULL;
Reg3 aHashEntry *tmp3;
int hashv;
#ifdef TESTNET
Reg4 aClient *tmp_fnd = NULL, *prv_fnd = NULL;
#endif
hashv = hash_nick_name(name);
tmp3 = &clientTable[hashv];
/*
* Got the bucket, now search the chain.
*/
for (tmp = (aClient *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
{
if (IsPing(tmp))
continue;
#ifdef TESTNET
if (mycmp(name, tmp->name) == 0)
{
if (tmp_fnd)
sendto_ops("*** DEBUG ERROR *** : Found %s TWICE !",
tmp->name);
else
{
tmp_fnd=tmp;
prv_fnd=prv;
}
}
#else
if (mycmp(name, tmp->name) == 0)
goto c_move_to_top;
#endif
}
#ifdef TESTNET
if (tmp_fnd)
{
tmp=tmp_fnd;
prv=prv_fnd;
goto c_move_to_top;
}
#endif
#ifdef DEBUGMODE
clmiss++;
#endif
return (cptr);
c_move_to_top:
#ifdef DEBUGMODE
clhits++;
#endif
/*
* If the member of the hashtable we found isnt at the top of its
* chain, put it there. This builds a most-frequently used order into
* the chains of the hash table, giving speadier lookups on those nicks
* which are being used currently. This same block of code is also
* used for channels and servers for the same performance reasons.
*/
if (prv)
{
aClient *tmp2;
tmp2 = (aClient *)tmp3->list;
tmp3->list = (void *)tmp;
prv->hnext = tmp->hnext;
tmp->hnext = tmp2;
}
return (tmp);
}
/*
* hash_find_nickserver
*/
aClient *hash_find_nickserver(name, cptr)
char *name;
aClient *cptr;
{
Reg1 aClient *tmp;
Reg2 aClient *prv = NULL;
Reg3 aHashEntry *tmp3;
int hashv;
char *serv;
serv = index(name, '@');
*serv++ = '\0';
hashv = hash_nick_name(name);
tmp3 = &clientTable[hashv];
/*
* Got the bucket, now search the chain.
*/
for (tmp = (aClient *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
if (mycmp(name, tmp->name) == 0 && tmp->user &&
mycmp(serv, tmp->user->server->name) == 0)
goto c_move_to_top;
#ifdef DEBUGMODE
clmiss++;
#endif
*--serv = '\0';
return (cptr);
c_move_to_top:
#ifdef DEBUGMODE
clhits++;
#endif
/*
* If the member of the hashtable we found isnt at the top of its
* chain, put it there. This builds a most-frequently used order into
* the chains of the hash table, giving speadier lookups on those nicks
* which are being used currently. This same block of code is also
* used for channels and servers for the same performance reasons.
*/
if (prv)
{
aClient *tmp2;
tmp2 = (aClient *)tmp3->list;
tmp3->list = (void *)tmp;
prv->hnext = tmp->hnext;
tmp->hnext = tmp2;
}
*--serv = '\0';
return (tmp);
}
/*
* hash_find_server
*/
aClient *hash_find_server(server, cptr)
char *server;
aClient *cptr;
{
Reg1 aClient *tmp, *prv = NULL;
Reg2 char *t;
Reg3 char ch;
aHashEntry *tmp3;
int hashv;
hashv = hash_nick_name(server);
tmp3 = &clientTable[hashv];
for (tmp = (aClient *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
{
if (!IsServer(tmp) && !IsMe(tmp))
continue;
if (mycmp(server, tmp->name) == 0)
goto s_move_to_top;
}
t = ((char *)server + strlen(server));
/*
* Whats happening in this next loop ? Well, it takes a name like
* foo.bar.edu and proceeds to search for *.edu and then *.bar.edu.
* This is for checking full server names against masks although
* it isnt often done this way in lieu of using matches().
*/
for (;;)
{
t--;
for (; t > server; t--)
if (*(t+1) == '.')
break;
if (t <= server || *t == '*')
break;
ch = *t;
*t = '*';
/*
* Dont need to check IsServer() here since nicknames cant
*have *'s in them anyway.
*/
if (((tmp = hash_find_client(t, cptr))) != cptr)
{
*t = ch;
return (tmp);
}
*t = ch;
}
#ifdef DEBUGMODE
clmiss++;
#endif
return (cptr);
s_move_to_top:
#ifdef DEBUGMODE
clhits++;
#endif
if (prv)
{
aClient *tmp2;
tmp2 = (aClient *)tmp3->list;
tmp3->list = (void *)tmp;
prv->hnext = tmp->hnext;
tmp->hnext = tmp2;
}
return (tmp);
}
/*
* hash_find_channel
*/
aChannel *hash_find_channel(name, chptr)
char *name;
aChannel *chptr;
{
int hashv;
Reg1 aChannel *tmp, *prv = NULL;
aHashEntry *tmp3;
hashv = hash_channel_name(name);
tmp3 = &channelTable[hashv];
for (tmp = (aChannel *)tmp3->list; tmp; prv = tmp, tmp = tmp->hnextch)
if (mycmp(name, tmp->chname) == 0)
goto c_move_to_top;
#ifdef DEBUGMODE
chmiss++;
#endif
return chptr;
c_move_to_top:
#ifdef DEBUGMODE
chhits++;
#endif
if (prv)
{
register aChannel *tmp2;
tmp2 = (aChannel *)tmp3->list;
tmp3->list = (void *)tmp;
prv->hnextch = tmp->hnextch;
tmp->hnextch = tmp2;
}
return (tmp);
}
/*
* NOTE: this command is not supposed to be an offical part of the ircd
* protocol. It is simply here to help debug and to monitor the
* performance of the hash functions and table, enabling a better
* algorithm to be sought if this one becomes troublesome.
* -avalon
*/
int m_hash(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
#ifdef DEBUGMODE
register int l, i;
register aHashEntry *tab;
int deepest = 0, deeplink = 0, showlist = 0, tothits = 0;
int mosthit = 0, mosthits = 0, used = 0, used_now = 0, totlink = 0;
int link_pop[10], size = HASHSIZE;
char ch;
aHashEntry *table;
if (parc > 1) {
ch = *parv[1];
if (islower(ch))
table = clientTable;
else {
table = channelTable;
size = CHANNELHASHSIZE;
}
if (ch == 'L' || ch == 'l')
showlist = 1;
} else {
ch = '\0';
table = clientTable;
}
for (i = 0; i < 10; i++)
link_pop[i] = 0;
for (i = 0; i < size; i++) {
tab = &table[i];
l = tab->links;
if (showlist)
sendto_one(sptr,
"NOTICE %s :Hash Entry:%6d Hits:%7d Links:%6d",
parv[0], i, tab->hits, l);
if (l > 0) {
if (l < 10)
link_pop[l]++;
else
link_pop[9]++;
used_now++;
totlink += l;
if (l > deepest) {
deepest = l;
deeplink = i;
}
}
else
link_pop[0]++;
l = tab->hits;
if (l) {
used++;
tothits += l;
if (l > mosthits) {
mosthits = l;
mosthit = i;
}
}
}
switch((int)ch)
{
case 'V' : case 'v' :
{
register aClient *acptr;
int bad = 0, listlength = 0;
for (acptr = client; acptr; acptr = acptr->next) {
if (hash_find_client(acptr->name,acptr) != acptr) {
if (ch == 'V')
sendto_one(sptr, "NOTICE %s :Bad hash for %s",
parv[0], acptr->name);
bad++;
}
listlength++;
}
sendto_one(sptr,"NOTICE %s :List Length: %d Bad Hashes: %d",
parv[0], listlength, bad);
}
case 'P' : case 'p' :
for (i = 0; i < 10; i++)
sendto_one(sptr,"NOTICE %s :Entires with %d links : %d",
parv[0], i, link_pop[i]);
return (0);
case 'r' :
{
Reg1 aClient *acptr;
sendto_one(sptr,"NOTICE %s :Rehashing Client List.", parv[0]);
clear_client_hash_table();
for (acptr = client; acptr; acptr = acptr->next)
if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
!IsPing(acptr))
(void)add_to_client_hash_table(acptr->name, acptr);
break;
}
case 'R' :
{
Reg1 aChannel *acptr;
sendto_one(sptr,"NOTICE %s :Rehashing Channel List.", parv[0]);
clear_channel_hash_table();
for (acptr = channel; acptr; acptr = acptr->nextch)
(void)add_to_channel_hash_table(acptr->chname, acptr);
break;
}
case 'H' :
if (parc > 2)
sendto_one(sptr,"NOTICE %s :%s hash to entry %d",
parv[0], parv[2],
hash_channel_name(parv[2]));
return (0);
case 'h' :
if (parc > 2)
sendto_one(sptr,"NOTICE %s :%s hash to entry %d",
parv[0], parv[2],
hash_nick_name(parv[2]));
return (0);
case 'n' :
{
aClient *tmp;
int max;
if (parc <= 2)
return (0);
l = atoi(parv[2]) % HASHSIZE;
if (parc > 3)
max = atoi(parv[3]) % HASHSIZE;
else
max = l;
for (;l <= max; l++)
for (i = 0, tmp = (aClient *)clientTable[l].list; tmp;
i++, tmp = tmp->hnext)
{
if (parv[1][2] == '1' && tmp != tmp->from)
continue;
sendto_one(sptr,"NOTICE %s :Node: %d #%d %s",
parv[0], l, i, tmp->name);
}
return (0);
}
case 'N' :
{
aChannel *tmp;
int max;
if (parc <= 2)
return (0);
l = atoi(parv[2]) % CHANNELHASHSIZE;
if (parc > 3)
max = atoi(parv[3]) % CHANNELHASHSIZE;
else
max = l;
for (;l <= max; l++)
for (i = 0, tmp = (aChannel *)channelTable[l].list; tmp;
i++, tmp = tmp->hnextch)
sendto_one(sptr,"NOTICE %s :Node: %d #%d %s",
parv[0], l, i, tmp->chname);
return (0);
}
case 'S' :
#else
if (parc>1&&!strcmp(parv[1],"sums")){
#endif
sendto_one(sptr, "NOTICE %s :SUSER SSERV", parv[0]);
sendto_one(sptr, "NOTICE %s :SBSDC IRCDC", parv[0]);
sendto_one(sptr, "NOTICE %s :CHANC SMISC", parv[0]);
sendto_one(sptr, "NOTICE %s :HASHC VERSH", parv[0]);
sendto_one(sptr, "NOTICE %s :MAKEF HOSTID", parv[0]);
#ifndef DEBUGMODE
}
#endif
return 0;
#ifdef DEBUGMODE
case 'z' :
{
Reg1 aClient *acptr;
if (parc <= 2)
return 0;
l = atoi(parv[2]);
if (l < 256)
return 0;
(void)free((char *)clientTable);
clientTable = (aHashEntry *)malloc(sizeof(aHashEntry) * l);
HASHSIZE = l;
clear_client_hash_table();
for (acptr = client; acptr; acptr = acptr->next)
if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
!IsPing(acptr))
{
acptr->hnext = NULL;
(void)add_to_client_hash_table(acptr->name, acptr);
}
sendto_one(sptr, "NOTICE %s :HASHSIZE now %d", parv[0], l);
break;
}
case 'Z' :
{
Reg1 aChannel *acptr;
if (parc <= 2)
return 0;
l = atoi(parv[2]);
if (l < 256)
return 0;
(void)free((char *)channelTable);
channelTable = (aHashEntry *)malloc(sizeof(aHashEntry) * l);
CHANNELHASHSIZE = l;
clear_channel_hash_table();
for (acptr = channel; acptr; acptr = acptr->nextch)
{
acptr->hnextch = NULL;
(void)add_to_channel_hash_table(acptr->chname, acptr);
}
sendto_one(sptr, "NOTICE %s :CHANNELHASHSIZE now %d",
parv[0], l);
break;
}
default :
break;
}
sendto_one(sptr,"NOTICE %s :Entries Hashed: %d NonEmpty: %d of %d",
parv[0], totlink, used_now, size);
if (!used_now)
used_now = 1;
sendto_one(sptr,"NOTICE %s :Hash Ratio (av. depth): %f %Full: %f",
parv[0], (float)((1.0 * totlink) / (1.0 * used_now)),
(float)((1.0 * used_now) / (1.0 * size)));
sendto_one(sptr,"NOTICE %s :Deepest Link: %d Links: %d",
parv[0], deeplink, deepest);
if (!used)
used = 1;
sendto_one(sptr,"NOTICE %s :Total Hits: %d Unhit: %d Av Hits: %f",
parv[0], tothits, size-used,
(float)((1.0 * tothits) / (1.0 * used)));
sendto_one(sptr,"NOTICE %s :Entry Most Hit: %d Hits: %d",
parv[0], mosthit, mosthits);
sendto_one(sptr,"NOTICE %s :Client hits %d miss %d",
parv[0], clhits, clmiss);
sendto_one(sptr,"NOTICE %s :Channel hits %d miss %d",
parv[0], chhits, chmiss);
return 0;
#endif
}