HashTable object

BlitzMax Forums/BlitzMax Programming/HashTable object

I figure I may as well share the version I molested...

Rem
    HASHTABLE OBJECT
    
    - M.Laurenson/Defoc8 2006
EndRem

Rem
    While not explicitly mentioned in the code (most of you probably
    overlooked the actual writing in the post), I can promise
    you Def would appreciate if you credited him if you used this.
    No credit is to go to me.
    
    -Noel
EndRem

Strict

Public

Import "hash.c"

Extern "C"
    Function __c_hash:Int(key$z, length:Int, initval:Int)="Hash"
EndExtern

Global HashKey:Int(name:String) = gHashKey

Function gHashKey%( name:String )
    Return __c_hash( name, name.Length, 0 )
End Function

Type gHashTable
    'private:

    Field table:TList[]
    
    'protected:
    
    Method _rement(key:Int)
        For Local entry:gHashEntry = EachIn table[key Shr 24]
            If entry.key = key
                entry.link.Remove( )
                Return
            EndIf
        Next
    End Method
    
    Method _addent(key:Int,o:Object)
        Local entry:gHashEntry = New gHashEntry
        entry.key = key
        entry.obj = o 
        entry.link = table[key Shr 24].AddLast(entry)
    End Method
    
    Method _getent:Object(key:Int)
        For Local entry:gHashEntry = EachIn table[key Shr 24]
            If entry.key = key
                Return entry.obj
            EndIf
        Next
        Return Null
    End Method
    
    'public:
    
    Method New()
        table=New TList[256]
        For Local n:Int = 0 To 255
            table[n] = New TList
        Next
    End Method
    
    Method SetEntry( name:String, obj:Object )
        Local key:Int = HashKey( name )
        Local o:Object = _getent(key)
        If o And o <> obj Then _rement(key)
        _addent(key,obj)
    End Method
    
    Method InsertEntry(name:String,obj:Object)
        Local key:Int = HashKey( name )
        _addent(key,obj)
    End Method
    
    Method RemoveEntry(name:String)
        Local key:Int = HashKey( name )
        _rement(key)
    End Method
    
    Method GetEntry:Object(name:String)
        Local key:Int = HashKey( name )
        Return _getent(key)
    End Method
    
    Method Flush()
        For Local n:Int = 0 To 255
            table[n].Clear( )
        Next
    End Method 
    
    Method GetEntryCount(index:Int)
        Return table[index].Count( )
    End Method
End Type

Type gHashEntry
    Field key:Int
    Field obj:Object
    Field link:TLink
End Type


hash.c
/*
    Code by Bob Jenkins, December 1996, Public Domain.  Plus some minor,
    minor tweaks by Noel Cower.  Nothing that affects the implementation.
    You can use this free for any purpose.  It has no warranty.
*/

#define mix(a,b,c) \
{ \
  a -= b; a -= c; a ^= (c>>13); \
  b -= c; b -= a; b ^= (a<<8); \
  c -= a; c -= b; c ^= (b>>13); \
  a -= b; a -= c; a ^= (c>>12);  \
  b -= c; b -= a; b ^= (a<<16); \
  c -= a; c -= b; c ^= (b>>5); \
  a -= b; a -= c; a ^= (c>>3);  \
  b -= c; b -= a; b ^= (a<<10); \
  c -= a; c -= b; c ^= (b>>15); \
}

int Hash( k, length, initval )
register const unsigned char *k;        /* the key */
register int  length;   /* the length of the key */
register int  initval;    /* the previous hash, or an arbitrary value */
{
   register int a,b,c,len;

   /* Set up the internal state */
   len = length;
   a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
   c = initval;           /* the previous hash value */

   /*---------------------------------------- handle most of the key */
   while (len >= 12)
   {
      a += (k[0] +((int)k[1]<<8) +((int)k[2]<<16) +((int)k[3]<<24));
      b += (k[4] +((int)k[5]<<8) +((int)k[6]<<16) +((int)k[7]<<24));
      c += (k[8] +((int)k[9]<<8) +((int)k[10]<<16)+((int)k[11]<<24));
      mix(a,b,c);
      k += 12; len -= 12;
   }

   /*------------------------------------- handle the last 11 bytes */
   c += length;
   switch(len)              /* all the case statements fall through */
   {
   case 11: c+=((int)k[10]<<24);
   case 10: c+=((int)k[9]<<16);
   case 9 : c+=((int)k[8]<<8);
      /* the first byte of c is reserved for the length */
   case 8 : b+=((int)k[7]<<24);
   case 7 : b+=((int)k[6]<<16);
   case 6 : b+=((int)k[5]<<8);
   case 5 : b+=k[4];
   case 4 : a+=((int)k[3]<<24);
   case 3 : a+=((int)k[2]<<16);
   case 2 : a+=((int)k[1]<<8);
   case 1 : a+=k[0];
     /* case 0: nothing left to add */
   }
   mix(a,b,c);
   /*-------------------------------------------- report the result */
   return c;
}


"If it ain't broke, don't fix it" doesn't really apply to Def's code ;) Then again, I'm rather notorious for fixing that which isn't broken.