#include "OSHashTable.h"
#include <libkern/c++/OSData.h>
#include <libkern/c++/OSString.h>
#include <IOKit/IOLib.h>
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "OSHashTable"
#if DEBUG
#define OS_HASH_TABLE_DEBUGGING_LEVEL 0
#endif
#include "IOSCSIArchitectureModelFamilyDebugging.h"
#if ( OS_HASH_TABLE_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x) IOPanic x
#else
#define PANIC_NOW(x)
#endif
#if ( OS_HASH_TABLE_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x) IOLog x
#else
#define ERROR_LOG(x)
#endif
#if ( OS_HASH_TABLE_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
#define kFNV_32_PRIME ((UInt32) 0x01000193UL)
UInt32
__OSHashTable::Hash ( OSData * data ) const
{
const UInt8 * bytes = NULL;
UInt32 hash = 0;
UInt32 length = 0;
STATUS_LOG ( ( "+__OSHashTable::Hash(data)\n" ) );
require_nonzero ( data, ErrorExit );
bytes = ( const UInt8 * ) data->getBytesNoCopy ( );
require_nonzero ( bytes, ErrorExit );
length = data->getLength ( );
require_nonzero ( length, ErrorExit );
while ( length != 0 )
{
hash *= kFNV_32_PRIME;
hash ^= *bytes++;
length--;
}
ErrorExit:
STATUS_LOG ( ( "-__OSHashTable::Hash(data)\n" ) );
return hash;
}
UInt32
__OSHashTable::Hash ( OSString * string ) const
{
const UInt8 * bytes = NULL;
UInt32 hash = 0;
UInt32 c = 0;
STATUS_LOG ( ( "+__OSHashTable::Hash(string)\n" ) );
require_nonzero ( string, ErrorExit );
bytes = ( const UInt8 * ) string->getCStringNoCopy ( );
require_nonzero ( bytes, ErrorExit );
c = *bytes;
while ( c != 0 )
{
hash *= kFNV_32_PRIME;
hash ^= c;
bytes++;
c = *bytes;
}
ErrorExit:
STATUS_LOG ( ( "-__OSHashTable::Hash(string)\n" ) );
return hash;
}
__OSHashTable::__OSHashTable ( void )
{
STATUS_LOG ( ( "+__OSHashTable::__OSHashTable(void)\n" ) );
fTableLock = IORecursiveLockAlloc ( );
fSize = kDefaultStartSize;
fEntries = 0;
fMaxChainDepth = 0;
fTable = IONew ( __OSHashEntryBucket, fSize );
bzero ( fTable, fSize * sizeof ( __OSHashEntryBucket ) );
STATUS_LOG ( ( "-__OSHashTable::__OSHashTable(void)\n" ) );
}
__OSHashTable::__OSHashTable ( UInt32 startSize )
{
STATUS_LOG ( ( "+__OSHashTable::__OSHashTable(UInt32)\n" ) );
fTableLock = IORecursiveLockAlloc ( );
fSize = startSize;
fEntries = 0;
fMaxChainDepth = 0;
fTable = IONew ( __OSHashEntryBucket, fSize );
bzero ( fTable, fSize * sizeof ( __OSHashEntryBucket ) );
STATUS_LOG ( ( "-__OSHashTable::__OSHashTable(UInt32)\n" ) );
}
__OSHashTable::~__OSHashTable ( void )
{
STATUS_LOG ( ( "+__OSHashTable::~__OSHashTable\n" ) );
if ( fTableLock != NULL )
{
IORecursiveLockFree ( fTableLock );
fTableLock = NULL;
}
if ( fTable != NULL )
{
IODelete ( fTable, __OSHashEntryBucket, fSize );
fTable = NULL;
}
STATUS_LOG ( ( "-__OSHashTable::~__OSHashTable\n" ) );
}
void
__OSHashTable::InsertHashEntry ( __OSHashEntry * newEntry )
{
__OSHashEntryBucket * header = NULL;
UInt32 hashValue = 0;
Lock ( );
hashValue = newEntry->hashValue % fSize;
header = &fTable[hashValue];
newEntry->next = header->firstEntry;
newEntry->prev = NULL;
if ( header->firstEntry != NULL )
{
header->firstEntry->prev = newEntry;
}
header->firstEntry = newEntry;
header->chainDepth++;
STATUS_LOG ( ( "__OSHashTable::InsertHashEntry, bucket = %ld, chainDepth = %ld\n", hashValue, header->chainDepth ) );
fEntries++;
if ( fEntries > ( fSize / 2 ) )
{
STATUS_LOG ( ( "Should grow hash table\n" ) );
}
Unlock ( );
}
void
__OSHashTable::RemoveHashEntry ( __OSHashEntry * oldEntry )
{
__OSHashEntry * next = NULL;
__OSHashEntry * prev = NULL;
__OSHashEntryBucket * header = NULL;
UInt32 hashValue = 0;
Lock ( );
hashValue = oldEntry->hashValue % fSize;
header = &fTable[hashValue];
prev = oldEntry->prev;
next = oldEntry->next;
if ( prev != NULL )
{
prev->next = next;
}
else
{
header->firstEntry = next;
}
if ( next != NULL )
{
next->prev = prev;
}
header->chainDepth--;
STATUS_LOG ( ( "__OSHashTable::RemoveHashEntry, bucket = %ld, chainDepth = %ld\n", hashValue, header->chainDepth ) );
fEntries--;
if ( fEntries < ( fSize / 8 ) )
{
STATUS_LOG ( ( "Should shrink hash table\n" ) );
}
Unlock ( );
}
void
__OSHashTable::Rehash ( void )
{
__OSHashEntry * listHead = NULL;
__OSHashEntryBucket * newTable = NULL;
__OSHashEntryBucket * oldTable = NULL;
UInt32 newSize = 0;
UInt32 oldSize = 0;
if ( fEntries < ( fSize / 8 ) )
{
newSize = fSize / kScaleFactor;
}
else
{
newSize = fSize * kScaleFactor;
}
newTable = IONew ( __OSHashEntryBucket, newSize );
require_nonzero ( newTable, ErrorExit );
bzero ( newTable, newSize * sizeof ( __OSHashEntryBucket ) );
Lock ( );
listHead = SingleList ( );
require_nonzero_action ( listHead,
ErrorExit,
Unlock ( );
IODelete ( newTable, __OSHashEntryBucket, newSize ) );
oldTable = fTable;
oldSize = fSize;
fTable = newTable;
fSize = newSize;
fEntries = 0;
RehashList ( listHead );
Unlock ( );
IODelete ( oldTable, __OSHashEntryBucket, oldSize );
ErrorExit:
return;
}
__OSHashEntry *
__OSHashTable::SingleList ( void ) const
{
__OSHashEntryBucket * bucket = NULL;
__OSHashEntry * firstBucket = NULL;
__OSHashEntry * lastEntryInBucket = NULL;
UInt32 index = 0;
UInt32 bucketIndex = 0;
index = FindFirstBucketWithEntries ( );
require ( ( index < fSize ), Exit );
bucket = &fTable[index];
firstBucket = bucket->firstEntry;
require_nonzero ( firstBucket, Exit );
for ( ; index < ( fSize - 1 ); index++ )
{
bucket = &fTable[index];
if ( bucket->chainDepth == 0 )
continue;
lastEntryInBucket = bucket->firstEntry;
for ( bucketIndex = 0; bucketIndex < ( bucket->chainDepth - 1 ); bucketIndex++ )
{
lastEntryInBucket = lastEntryInBucket->next;
check ( lastEntryInBucket );
}
bucketIndex = FindNextBucketWithEntries ( &bucket, index + 1 );
require ( ( bucketIndex < fSize ), Exit );
require_nonzero ( bucket, Exit );
lastEntryInBucket->next = bucket->firstEntry;
bucket->firstEntry->prev = lastEntryInBucket;
}
Exit:
return firstBucket;
}
void
__OSHashTable::RehashList ( __OSHashEntry * listHead )
{
__OSHashEntry * next = NULL;
__OSHashEntry * prev = NULL;
prev = listHead;
next = prev->next;
InsertHashEntry ( prev );
while ( next != NULL )
{
prev = next;
next = prev->next;
InsertHashEntry ( prev );
}
}
UInt32
__OSHashTable::FindFirstBucketWithEntries ( void ) const
{
UInt32 index = 0;
__OSHashEntryBucket * bucket = NULL;
for ( index = 0; index < fSize; index++ )
{
bucket = &fTable[index];
if ( bucket->chainDepth != 0 )
{
break;
}
}
return index;
}
UInt32
__OSHashTable::FindNextBucketWithEntries (
__OSHashEntryBucket ** bucket,
UInt32 startLocation ) const
{
UInt32 index = 0;
__OSHashEntryBucket * localBucket = NULL;
*bucket = NULL;
for ( index = startLocation; index < fSize; index++ )
{
localBucket = &fTable[index];
if ( localBucket->chainDepth != 0 )
{
*bucket = localBucket;
break;
}
}
return index;
}