mirror of
https://github.com/electronicarts/CnC_Red_Alert.git
synced 2025-12-15 23:21:40 -05:00
Initial commit of Command & Conquer Red Alert source code.
This commit is contained in:
586
LAUNCHER/DICTIONARY.H
Normal file
586
LAUNCHER/DICTIONARY.H
Normal file
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
** Command & Conquer Red Alert(tm)
|
||||
** Copyright 2025 Electronic Arts Inc.
|
||||
**
|
||||
** 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/****************************************************************************\
|
||||
* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
|
||||
******************************************************************************
|
||||
Project Name: Carpenter (The RedAlert ladder creator)
|
||||
File Name : dictionary.h
|
||||
Author : Neal Kettler
|
||||
Start Date : June 1, 1997
|
||||
Last Update : June 17, 1997
|
||||
|
||||
This template file implements a hash dictionary. A hash dictionary is
|
||||
used to quickly match a value with a key. This works well for very
|
||||
large sets of data. A table is constructed that has some power of two
|
||||
number of pointers in it. Any value to be put in the table has a hashing
|
||||
function applied to the key. That key/value pair is then put in the
|
||||
linked list at the slot the hashing function specifies. If everything
|
||||
is working well, this is much faster than a linked list, but only if
|
||||
your hashing function is good.
|
||||
\****************************************************************************/
|
||||
|
||||
#ifndef DICTIONARY_HEADER
|
||||
#define DICTIONARY_HEADER
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "wstypes.h"
|
||||
#include "wdebug.h"
|
||||
|
||||
// Every entry in the hash dictionary must be an instance of the DNode
|
||||
// template. 'K' and 'V' denote Key and Value.
|
||||
template <class K,class V>
|
||||
class DNode
|
||||
{
|
||||
public:
|
||||
K key;
|
||||
V value;
|
||||
DNode<K,V> *hashNext;
|
||||
};
|
||||
|
||||
template <class K,class V>
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
Dictionary(uint32 (* hashFn)(K &key));
|
||||
~Dictionary();
|
||||
|
||||
void clear(void);
|
||||
bit8 add(IN K &key,IN V &value);
|
||||
bit8 getValue(IN K &key, OUT V &value);
|
||||
void print(IN FILE *out) const;
|
||||
uint32 getSize(void) const;
|
||||
uint32 getEntries(void) const;
|
||||
bit8 contains(IN K &key);
|
||||
bit8 updateValue(IN K &key,IN V &value);
|
||||
bit8 remove(IN K &key,OUT V &value);
|
||||
bit8 remove(IN K &key);
|
||||
bit8 removeAny(OUT K &key,OUT V &value);
|
||||
bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) const;
|
||||
|
||||
private:
|
||||
void shrink(void); // halve the number of slots
|
||||
void expand(void); // double the number of slots
|
||||
|
||||
|
||||
DNode<K,V> **table; // This stores the lists at each slot
|
||||
|
||||
uint32 entries; // number of entries
|
||||
uint32 size; // size of table
|
||||
uint32 tableBits; // table is 2^tableBits big
|
||||
uint32 log2Size; // Junk variable
|
||||
bit8 keepSize; // If true don't shrink or expand
|
||||
|
||||
uint32 (* hashFunc)(K &key); // User provided hash function
|
||||
uint32 keyHash(IN K &key); // This will reduce to correct range
|
||||
|
||||
|
||||
// See initilizer list of constructor for values
|
||||
const double SHRINK_THRESHOLD; // When table is this % full shrink it
|
||||
const double EXPAND_THRESHOLD; // When table is this % full grow it
|
||||
const int MIN_TABLE_SIZE; // must be a power of 2
|
||||
};
|
||||
|
||||
|
||||
//Create the empty hash dictionary
|
||||
template <class K,class V>
|
||||
Dictionary<K,V>::Dictionary(uint32 (* hashFn)(K &key)) :
|
||||
SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it
|
||||
EXPAND_THRESHOLD(0.80), // When table is 80% full grow it
|
||||
MIN_TABLE_SIZE(32) // must be a power of 2
|
||||
{
|
||||
log2Size=MIN_TABLE_SIZE;
|
||||
size=MIN_TABLE_SIZE;
|
||||
assert(size>=4);
|
||||
tableBits=0;
|
||||
while(log2Size) { tableBits++; log2Size>>=1; }
|
||||
tableBits--;
|
||||
size=1<<tableBits; //Just in case MIN_TABLE_SIZE wasn't a power of 2
|
||||
entries=0;
|
||||
keepSize=FALSE;
|
||||
|
||||
//Table is a pointer to a list of pointers (the hash table)
|
||||
table=(DNode<K,V> **)new DNode<K,V>* [size];
|
||||
assert(table!=NULL);
|
||||
|
||||
memset((void *)table,0,size*sizeof(void *));
|
||||
hashFunc=hashFn;
|
||||
}
|
||||
|
||||
//Free all the memory...
|
||||
template <class K,class V>
|
||||
Dictionary<K,V>::~Dictionary()
|
||||
{
|
||||
clear(); // Remove the entries
|
||||
delete[](table); // And the table as well
|
||||
}
|
||||
|
||||
// Remove all the entries and free the memory
|
||||
template <class K,class V>
|
||||
void Dictionary<K,V>::clear()
|
||||
{
|
||||
DNode<K,V> *temp,*del;
|
||||
uint32 i;
|
||||
//free all the data
|
||||
for (i=0; i<size; i++)
|
||||
{
|
||||
temp=table[i];
|
||||
while(temp!=NULL)
|
||||
{
|
||||
del=temp;
|
||||
temp=temp->hashNext;
|
||||
delete(del);
|
||||
}
|
||||
table[i]=NULL;
|
||||
}
|
||||
entries=0;
|
||||
|
||||
while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE))
|
||||
shrink();
|
||||
}
|
||||
|
||||
template <class K,class V>
|
||||
uint32 Dictionary<K,V>::keyHash(IN K &key)
|
||||
{
|
||||
uint32 retval=hashFunc(key);
|
||||
retval &= ((1<<tableBits)-1);
|
||||
assert(retval<getSize());
|
||||
return(retval);
|
||||
}
|
||||
|
||||
|
||||
template <class K,class V>
|
||||
void Dictionary<K,V>::print(IN FILE *out) const
|
||||
{
|
||||
DNode<K,V> *temp;
|
||||
uint32 i;
|
||||
|
||||
fprintf(out,"--------------------\n");
|
||||
for (i=0; i<getSize(); i++)
|
||||
{
|
||||
temp=table[i];
|
||||
|
||||
fprintf(out," |\n");
|
||||
fprintf(out,"[ ]");
|
||||
|
||||
while (temp!=NULL)
|
||||
{
|
||||
fprintf(out,"--[ ]");
|
||||
temp=temp->hashNext;
|
||||
}
|
||||
fprintf(out,"\n");
|
||||
}
|
||||
fprintf(out,"--------------------\n");
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Iterate through all the records. Index is for the table, offset specifies the
|
||||
// element in the linked list. Set both to 0 and continue calling till false
|
||||
// is returned.
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
|
||||
OUT V &value) const
|
||||
{
|
||||
DNode<K,V> *temp;
|
||||
|
||||
// index out of range
|
||||
if ((index<0)||(index >= getSize()))
|
||||
return(FALSE);
|
||||
|
||||
temp=table[index];
|
||||
while ((temp==NULL)&&((++index) < getSize()))
|
||||
{
|
||||
temp=table[index];
|
||||
offset=0;
|
||||
}
|
||||
|
||||
if (temp==NULL) // no more slots with data
|
||||
return(FALSE);
|
||||
|
||||
uint32 i=0;
|
||||
while ((temp!=NULL) && (i < offset))
|
||||
{
|
||||
temp=temp->hashNext;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (temp==NULL) // should never happen
|
||||
return(FALSE);
|
||||
|
||||
value=temp->value;
|
||||
if (temp->hashNext==NULL)
|
||||
{
|
||||
index++;
|
||||
offset=0;
|
||||
}
|
||||
else
|
||||
offset++;
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Return the current size of the hash table
|
||||
template <class K,class V>
|
||||
uint32 Dictionary<K,V>::getSize(void) const
|
||||
{ return(size); }
|
||||
|
||||
|
||||
// Return the current number of entries in the table
|
||||
template <class K,class V>
|
||||
uint32 Dictionary<K,V>::getEntries(void) const
|
||||
{ return(entries); }
|
||||
|
||||
|
||||
// Does the Dictionary contain the key?
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::contains(IN K &key)
|
||||
{
|
||||
int offset;
|
||||
DNode<K,V> *node;
|
||||
|
||||
offset=keyHash(key);
|
||||
|
||||
node=table[offset];
|
||||
|
||||
if (node==NULL)
|
||||
{ return(FALSE); } // can't find it
|
||||
|
||||
while(node!=NULL)
|
||||
{
|
||||
if ((node->key)==key)
|
||||
{ return(TRUE); }
|
||||
node=node->hashNext;
|
||||
}
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
|
||||
// Try and update the value of an already existing object
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::updateValue(IN K &key,IN V &value)
|
||||
{
|
||||
sint32 retval;
|
||||
|
||||
retval=remove(key);
|
||||
if (retval==FALSE)
|
||||
return(FALSE);
|
||||
|
||||
add(key,value);
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
// Add to the dictionary (if key exists, value is updated with the new V)
|
||||
template <class K, class V>
|
||||
bit8 Dictionary<K,V>::add(IN K &key,IN V &value)
|
||||
{
|
||||
int offset;
|
||||
DNode<K,V> *node,*item,*temp;
|
||||
float percent;
|
||||
|
||||
item=(DNode<K,V> *)new DNode<K,V>;
|
||||
assert(item!=NULL);
|
||||
|
||||
#ifdef KEY_MEM_OPS
|
||||
memcpy(&(item->key),&key,sizeof(K));
|
||||
#else
|
||||
item->key=key;
|
||||
#endif
|
||||
|
||||
#ifdef VALUE_MEM_OPS
|
||||
memcpy(&(item->value),&value,sizeof(V));
|
||||
#else
|
||||
item->value=value;
|
||||
#endif
|
||||
|
||||
item->hashNext=NULL;
|
||||
|
||||
//If key already exists, it will be overwritten
|
||||
remove(key);
|
||||
|
||||
offset=keyHash(key);
|
||||
|
||||
node=table[offset];
|
||||
|
||||
if (node==NULL)
|
||||
{ table[offset]=item; }
|
||||
else
|
||||
{
|
||||
temp=table[offset];
|
||||
table[offset]=item;
|
||||
item->hashNext=temp;
|
||||
}
|
||||
|
||||
entries++;
|
||||
percent=(float)entries;
|
||||
percent/=(float)getSize();
|
||||
if (percent>= EXPAND_THRESHOLD ) expand();
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
// Remove an item from the dictionary
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::remove(IN K &key,OUT V &value)
|
||||
{
|
||||
int offset;
|
||||
DNode<K,V> *node,*last,*temp;
|
||||
float percent;
|
||||
|
||||
if (entries==0)
|
||||
return(FALSE);
|
||||
|
||||
percent=(float)(entries-1);
|
||||
percent/=(float)getSize();
|
||||
|
||||
offset=keyHash(key);
|
||||
node=table[offset];
|
||||
|
||||
last=node;
|
||||
if (node==NULL) return(FALSE);
|
||||
|
||||
//special case table points to thing to delete
|
||||
|
||||
#ifdef KEY_MEM_OPS
|
||||
if (0==memcmp(&(node->key),&key,sizeof(K)))
|
||||
#else
|
||||
if ((node->key)==key)
|
||||
#endif
|
||||
{
|
||||
#ifdef VALUE_MEM_OPS
|
||||
memcpy(&value,&(node->value),sizeof(V));
|
||||
#else
|
||||
value=node->value;
|
||||
#endif
|
||||
temp=table[offset]->hashNext;
|
||||
delete(table[offset]);
|
||||
table[offset]=temp;
|
||||
entries--;
|
||||
if (percent <= SHRINK_THRESHOLD)
|
||||
shrink();
|
||||
return(TRUE);
|
||||
}
|
||||
node=node->hashNext;
|
||||
|
||||
//Now the case if the thing to delete is not the first
|
||||
while (node!=NULL)
|
||||
{
|
||||
#ifdef KEY_MEM_OPS
|
||||
if (0==memcmp(&(node->key),&key,sizeof(K)))
|
||||
#else
|
||||
if (node->key==key)
|
||||
#endif
|
||||
{
|
||||
#ifdef VALUE_MEM_OPS
|
||||
memcpy(&value,&(node->value),sizeof(V));
|
||||
#else
|
||||
value=node->value;
|
||||
#endif
|
||||
last->hashNext=node->hashNext;
|
||||
entries--;
|
||||
delete(node);
|
||||
break;
|
||||
}
|
||||
last=node;
|
||||
node=node->hashNext;
|
||||
}
|
||||
|
||||
if (percent <= SHRINK_THRESHOLD)
|
||||
shrink();
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::remove(IN K &key)
|
||||
{
|
||||
V temp;
|
||||
return(remove(key,temp));
|
||||
}
|
||||
|
||||
|
||||
// Remove some random K/V pair that's in the Dictionary
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::removeAny(OUT K &key,OUT V &value)
|
||||
{
|
||||
int offset;
|
||||
DNode<K,V> *node,*last,*temp;
|
||||
float percent;
|
||||
|
||||
if (entries==0)
|
||||
return(FALSE);
|
||||
|
||||
percent=(entries-1);
|
||||
percent/=(float)getSize();
|
||||
|
||||
int i;
|
||||
offset=-1;
|
||||
for (i=0; i<(int)getSize(); i++)
|
||||
if (table[i]!=NULL)
|
||||
{
|
||||
offset=i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset==-1) // Nothing there
|
||||
return(FALSE);
|
||||
|
||||
node=table[offset];
|
||||
last=node;
|
||||
|
||||
#ifdef KEY_MEM_OPS
|
||||
memcpy(&key,&(node->key),sizeof(K));
|
||||
#else
|
||||
key=node->key;
|
||||
#endif
|
||||
#ifdef VALUE_MEM_OPS
|
||||
memcpy(&value,&(node->value),sizeof(V));
|
||||
#else
|
||||
value=node->value;
|
||||
#endif
|
||||
|
||||
temp=table[offset]->hashNext;
|
||||
delete(table[offset]);
|
||||
table[offset]=temp;
|
||||
entries--;
|
||||
if (percent <= SHRINK_THRESHOLD)
|
||||
shrink();
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
template <class K,class V>
|
||||
bit8 Dictionary<K,V>::getValue(IN K &key,OUT V &value)
|
||||
{
|
||||
int offset;
|
||||
DNode<K,V> *node;
|
||||
|
||||
offset=keyHash(key);
|
||||
|
||||
node=table[offset];
|
||||
|
||||
if (node==NULL) return(FALSE);
|
||||
|
||||
#ifdef KEY_MEM_OPS
|
||||
while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K))))
|
||||
#else
|
||||
while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't
|
||||
#endif // have to do oper !=
|
||||
{ node=node->hashNext; }
|
||||
|
||||
if (node==NULL)
|
||||
{ return(FALSE); }
|
||||
|
||||
#ifdef VALUE_MEM_OPS
|
||||
memcpy(&value,&(node->value),sizeof(V));
|
||||
#else
|
||||
value=(node->value);
|
||||
#endif
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
|
||||
//A note about Shrink and Expand: They are never necessary, they are
|
||||
//only here to improve performance of the hash table by reducing
|
||||
//the length of the linked list at each table entry.
|
||||
|
||||
// Shrink the hash table by a factor of 2 (and relocate entries)
|
||||
template <class K,class V>
|
||||
void Dictionary<K,V>::shrink(void)
|
||||
{
|
||||
int i;
|
||||
int oldsize;
|
||||
uint32 offset;
|
||||
DNode<K,V> **oldtable,*temp,*first,*next;
|
||||
|
||||
if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE))
|
||||
return;
|
||||
|
||||
//fprintf(stderr,"Shrinking....\n");
|
||||
|
||||
oldtable=table;
|
||||
oldsize=size;
|
||||
size/=2;
|
||||
tableBits--;
|
||||
|
||||
table=(DNode<K,V> **)new DNode<K,V>*[size];
|
||||
assert(table!=NULL);
|
||||
memset((void *)table,0,size*sizeof(void *));
|
||||
|
||||
for (i=0; i<oldsize; i++)
|
||||
{
|
||||
temp=oldtable[i];
|
||||
while (temp!=NULL)
|
||||
{
|
||||
offset=keyHash(temp->key);
|
||||
first=table[offset];
|
||||
table[offset]=temp;
|
||||
next=temp->hashNext;
|
||||
temp->hashNext=first;
|
||||
temp=next;
|
||||
}
|
||||
}
|
||||
delete[](oldtable);
|
||||
}
|
||||
|
||||
|
||||
template <class K,class V>
|
||||
void Dictionary<K,V>::expand(void)
|
||||
{
|
||||
int i;
|
||||
int oldsize;
|
||||
uint32 offset;
|
||||
DNode<K,V> **oldtable,*temp,*first,*next;
|
||||
|
||||
if (keepSize==TRUE)
|
||||
return;
|
||||
|
||||
//fprintf(stderr,"Expanding...\n");
|
||||
|
||||
oldtable=table;
|
||||
oldsize=size;
|
||||
size*=2;
|
||||
tableBits++;
|
||||
|
||||
table=(DNode<K,V> **)new DNode<K,V>* [size];
|
||||
assert(table!=NULL);
|
||||
memset((void *)table,0,size*sizeof(void *));
|
||||
|
||||
for (i=0; i<oldsize; i++)
|
||||
{
|
||||
temp=oldtable[i];
|
||||
while (temp!=NULL)
|
||||
{
|
||||
offset=keyHash(temp->key);
|
||||
first=table[offset];
|
||||
table[offset]=temp;
|
||||
next=temp->hashNext;
|
||||
temp->hashNext=first;
|
||||
temp=next;
|
||||
}
|
||||
}
|
||||
delete[](oldtable);
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user