/* * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Includes //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ #include "OSHashTable.h" #include #include #include //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ #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 // FNV (Fowler/Noll/Vo) Prime (32-bit) constant #define kFNV_32_PRIME ((UInt32) 0x01000193UL) //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Hash - Does FNV hash on passed in OSData bytes. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ); // Perform hash while ( length != 0 ) { hash *= kFNV_32_PRIME; hash ^= *bytes++; length--; } ErrorExit: STATUS_LOG ( ( "-__OSHashTable::Hash(data)\n" ) ); return hash; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Hash - Does FNV hash on passed in OSString. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ); // Perform hash c = *bytes; while ( c != 0 ) { hash *= kFNV_32_PRIME; hash ^= c; bytes++; c = *bytes; } ErrorExit: STATUS_LOG ( ( "-__OSHashTable::Hash(string)\n" ) ); return hash; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Default Constructor //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ __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" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Constructor //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ __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" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Destructor //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ __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" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // InsertHashEntry - Called to insert an entry into the hash table. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ( ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // RemoveHashEntry - Called to remove an entry from the hash table. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ( ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Rehash - Called to grow or shrink the hash table and balance it out. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void __OSHashTable::Rehash ( void ) { __OSHashEntry * listHead = NULL; __OSHashEntryBucket * newTable = NULL; __OSHashEntryBucket * oldTable = NULL; UInt32 newSize = 0; UInt32 oldSize = 0; // The Rehash() method does the following: // ₯ Allocate new memory associated with the table of pointers to buckets. // ₯ Make everything into a single doubly-linked list by making // the tail of each bucket point at the head of the next bucket over. // ₯ Rehash each entry and drop it in its proper bucket. // ₯ Delete the memory associated with the old table of pointers to buckets. // Easy check to find out if we're growing or shrinking. if ( fEntries < ( fSize / 8 ) ) { newSize = fSize / kScaleFactor; } else { newSize = fSize * kScaleFactor; } // We now know the new table size. Attempt to allocate memory for new table of // pointers. newTable = IONew ( __OSHashEntryBucket, newSize ); require_nonzero ( newTable, ErrorExit ); bzero ( newTable, newSize * sizeof ( __OSHashEntryBucket ) ); // Must hold the table lock to go further. Lock ( ); // Rearrange in single list. Keep track of list head. listHead = SingleList ( ); require_nonzero_action ( listHead, ErrorExit, Unlock ( ); IODelete ( newTable, __OSHashEntryBucket, newSize ) ); // Switch the tables. oldTable = fTable; oldSize = fSize; fTable = newTable; fSize = newSize; fEntries = 0; // Put all the items in the new table. RehashList ( listHead ); // Drop the lock now, we don't need it anymore. Unlock ( ); // Delete memory associated with old table. IODelete ( oldTable, __OSHashEntryBucket, oldSize ); ErrorExit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // SingleList - Arranges items into a single doubly-linked list. // NB: This method must be called with fTableLock held. [PRIVATE] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ __OSHashEntry * __OSHashTable::SingleList ( void ) const { __OSHashEntryBucket * bucket = NULL; __OSHashEntry * firstBucket = NULL; __OSHashEntry * lastEntryInBucket = NULL; UInt32 index = 0; UInt32 bucketIndex = 0; // Find the first bucket with objects in it. index = FindFirstBucketWithEntries ( ); require ( ( index < fSize ), Exit ); bucket = &fTable[index]; firstBucket = bucket->firstEntry; require_nonzero ( firstBucket, Exit ); // Loop over all buckets to connect the hash entries into one giant linked-list. for ( ; index < ( fSize - 1 ); index++ ) { // Get the current bucket. bucket = &fTable[index]; if ( bucket->chainDepth == 0 ) continue; lastEntryInBucket = bucket->firstEntry; for ( bucketIndex = 0; bucketIndex < ( bucket->chainDepth - 1 ); bucketIndex++ ) { lastEntryInBucket = lastEntryInBucket->next; check ( lastEntryInBucket ); } // Get pointer to next bucket bucketIndex = FindNextBucketWithEntries ( &bucket, index + 1 ); require ( ( bucketIndex < fSize ), Exit ); require_nonzero ( bucket, Exit ); lastEntryInBucket->next = bucket->firstEntry; bucket->firstEntry->prev = lastEntryInBucket; } Exit: return firstBucket; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // RehashList - Drops the list of items into table via InsertHashEntry(). // NB: This method must be called with fTableLock held. [PRIVATE] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // FindFirstBucketWithEntries - Finds first bucket with entries. [PRIVATE] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // FindNextBucketWithEntries - Finds next bucket with entries. [PRIVATE] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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; }