如有错误请指正
接口定义请参考 点击打开链接,只有接口名字不一样,所里这里不再列出了。
接口实现
#include "hash.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define ARRAY_SIZE 3
// Store the bucket counts of hash table
static const unsigned long kBucketCount[ARRAY_SIZE] = \
{1024, 2048, 666666};
// Hold current Bucket size
static int giBucketCountIndex = 0;
/* ListNode to identify the data unit under processing */
typedef struct ListNode
{
char* key; // Current node key
void* value; // Pointer to the value which binding to the key
struct ListNode* next; // Next node
} ListNode;
/** Association */
typedef struct ListAssociation
{
struct ListNode* head_node;
struct ListNode* tail_node;
unsigned long uiNodeLength; // The length of linked list
} ListAssociation;
/* HashTable to collect the hash table data */
typedef struct HashTable
{
struct ListAssociation** bucketTable;
unsigned long uiBindingsCount; // Hold bindings size
unsigned long uiBucketCount; // Hold Bucket size
} HashTable;
typedef struct HashTable* HashTable_t;
/* Local help function
* Use calloc to allocate the memory,
* because it will initialize all memory bits to zero.
*/
static void* MyAlloc ( unsigned long size );
/**
*Local function, if find out the pcKey return the ListAssociation*,
*other return NULL
*/
static ListAssociation* HashTable_getListAssociation(
HashTable_t oHashTable,
const char* pcKey );
/**
*Local function, if find out the pcKey return the ListNode*,
*other return NULL
*/
static ListNode* ListAssociation_getListNode(
const ListAssociation* pListAssociation,
const char* pcKey );
/**
*Local function.
*If a pcKey isn’t already in the ListAssociation pointed to by
*pListAssociation,inserts it into the ListAssociation and returns 1.
*If not enough memory is available or pcKey already is in the
*ListAssociation, the function will leave ListAssociation
*unchanged and return 0.
*/
static int ListAssociation_put( ListAssociation* pListAssociation,
const char* pcKey,
const void* pvValue );
/**
*Local function, if find out the pcKey return the ListNode*,
*other return NULL
*/
static ListNode* HashTable_getListNode( HashTable_t oHashTable,
const char* pcKey );
/**
*Local help function,Refresh the hash table.
*For the rehash function, this is the array of bucket counts:
*1024, 2048, 666666. Initially,
*the bucket count should be set to the first size (1024). When
*HashTable_put detects that the total number of bindings exceeds the
*current bucket count,1024, a rehash function should increase the
*bucket count to 2048. Then as number of bindings exceeds 2048, the
*bucket count should be 666666. If the binding count goes
*over 666666, the bucket count should not be increased again.
*If successful return 1, otherwise return 0
*/
static int Rehash( HashTable_t oHashTable );
/**
* For help Rehash().
*/
static int ListAssociation_put_help_rehash( ListAssociation* pListAssociation,
ListNode* pListNode );
/**
* Update giBucketCountIndex.
* In some cases(example cycle test) the giBucketCountIndex may be wrong,
* so need update it to the real index.
*/
static int GetBucketSizeIndex( unsigned long uiBucketCounts );
// HashTable_hash
int HashTable_hash( const char* pcKey, int iBucketCount )
{
int i;
unsigned int uiHash = 0U;
assert( pcKey != NULL );
for ( i = 0; pcKey[i] != '\0'; i++ )
{
uiHash = uiHash * 66666
+ ( unsigned int )pcKey[i];
}
return ( int )( uiHash % ( unsigned int ) iBucketCount );
}
HashTable_t HashTable_new( void )
{
HashTable_t temp = ( HashTable_t ) MyAlloc ( sizeof ( HashTable ) );
if ( temp )
{
struct ListAssociation** pListAssociation = NULL;
temp->uiBucketCount = kBucketCount[0];// The default Bucket size
pListAssociation = ( ListAssociation** )\
MyAlloc( temp->uiBucketCount * sizeof( ListAssociation* ) );
if ( NULL == pListAssociation )
{
free( temp );
return NULL;
}
temp->bucketTable = pListAssociation;
}
return temp;
}
void HashTable_free( HashTable_t oHashTable )
{
assert ( oHashTable );
unsigned long uiBucketCount = oHashTable->uiBucketCount;
unsigned i = 0;
for ( i = 0; i < uiBucketCount; ++i )
{
struct ListAssociation* pListAssociation = NULL;
pListAssociation = oHashTable->bucketTable[i];
if ( NULL == pListAssociation )
{
continue;
}
ListNode* temp = pListAssociation->head_node;
while ( temp )
{
pListAssociation->head_node = pListAssociation->head_node->next;
if ( temp->key )// Release key
{
free ( temp->key );
}
free ( temp ); // Release ListNode
temp = pListAssociation->head_node;
}
free( pListAssociation );// Release ListAssociation
pListAssociation = NULL;
}
free( oHashTable->bucketTable );
free ( oHashTable );
oHashTable = NULL;
}
int HashTable_getLength( HashTable_t oHashTable )
{
return oHashTable->uiBindingsCount;
}
int HashTable_put( HashTable_t oHashTable, const char* pcKey, const void* pvValue )
{
assert ( oHashTable );
assert ( pcKey );
assert ( pvValue );
assert ( oHashTable->uiBucketCount );
if ( 0 == strlen( pcKey ) )
{
return 0;
}
// Check if the Bucket counts < 666666
if ( oHashTable->uiBucketCount < kBucketCount[ARRAY_SIZE - 1] )
{
// If bindings counts > Bucket counts,refresh the hash table,
if ( oHashTable->uiBindingsCount >= oHashTable->uiBucketCount )
{
// Refresh the hash table
if ( 0 == Rehash( oHashTable ) )
{
return 0;
}
}
}
ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );
if ( NULL == pListAssociation ) // There is no ListAssociation* in table
{
pListAssociation = ( ListAssociation* )MyAlloc ( sizeof( ListAssociation ) );
if ( NULL == pListAssociation )
{
return 0;
}
// Put pListAssociation into table
oHashTable->bucketTable[HashTable_hash( pcKey, oHashTable->uiBucketCount )] = pListAssociation;
}
// Insert the bindings into ListAssociation
if ( ListAssociation_put( pListAssociation, pcKey, pvValue ) )
{
oHashTable->uiBindingsCount = oHashTable->uiBindingsCount + 1;
return 1;
}
return 0;
}
int HashTable_contains( HashTable_t oHashTable, const char* pcKey )
{
assert ( oHashTable );
assert ( pcKey );
if ( HashTable_getListNode( oHashTable, pcKey ) )
{
return 1;
}
return 0;
}
void* HashTable_get( HashTable_t oHashTable, const char* pcKey )
{
assert ( oHashTable );
assert ( pcKey );
ListNode* pListNode = HashTable_getListNode( oHashTable, pcKey );
if ( pListNode )
{
return pListNode->value;
}
return NULL;
}
void HashTable_map( HashTable_t oHashTable,
void ( *pfApply )( const char* pcKey, const void* pvValue, void* pvExtra ),
const void* pvExtra )
{
assert ( oHashTable );
assert ( pfApply );
assert ( pvExtra );
unsigned long uiBucketCount = oHashTable->uiBucketCount;
unsigned i = 0;
// The table is empty, return
if ( 0 == HashTable_getLength( oHashTable ) )
{
return;
}
for ( i = 0; i < uiBucketCount; ++i )
{
ListAssociation* pListAssociation = oHashTable->bucketTable[i];
if ( NULL == pListAssociation )
{
continue;
}
ListNode* pListNode = pListAssociation->head_node;
while ( pListNode )
{
assert ( pListNode->key );
assert ( pListNode->value );
pfApply( pListNode->key, pListNode->value, ( void* )pvExtra );
pListNode = pListNode->next;
}
}
return;
}
void* HashTable_replace( HashTable_t oHashTable, const char* pcKey, const void* pvValue )
{
assert ( oHashTable );
assert ( pcKey );
void* value = NULL;
ListNode* pListNode = HashTable_getListNode( oHashTable, pcKey );
if ( pListNode )
{
value = pListNode->value;
pListNode->value = ( void* )pvValue;
}
return value;
}
void* HashTable_remove( HashTable_t oHashTable, const char* pcKey )
{
assert ( oHashTable );
assert ( pcKey );
ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );
if ( NULL == pListAssociation )
{
return NULL;
}
void* value = NULL;
ListNode* temp = pListAssociation->head_node;
ListNode* pre_node = NULL;
/*
*The key-value maybe has been removed
*or has not inserted successfully
*/
if ( NULL == temp )
{
return NULL;
}
if ( 0 == strcmp ( pcKey, temp->key ) ) // Head node is the node which need to be removed
{
value = temp->value;
pListAssociation->head_node = temp->next;
free ( temp->key );
free ( temp );
pListAssociation->uiNodeLength = pListAssociation->uiNodeLength - 1;
oHashTable->uiBindingsCount = oHashTable->uiBindingsCount - 1;
return value;
}
while ( 1 )
{
pre_node = temp;
temp = temp->next;
if ( NULL == temp )
{
return NULL;
}
if ( 0 == strcmp ( pcKey, temp->key ) ) // Find it
{
pre_node->next = temp->next;
value = temp->value;
// Do not forget to release the memory
free ( temp->key );
free ( temp );
// Update the counts
pListAssociation->uiNodeLength = pListAssociation->uiNodeLength - 1;
oHashTable->uiBindingsCount = oHashTable->uiBindingsCount - 1;
return value;
}
}
return NULL;
}
static void* MyAlloc ( unsigned long size )
{
void* tmp;
tmp = ( void* ) calloc ( size, sizeof ( char ) );
assert( tmp );
return tmp;
}
static ListAssociation* HashTable_getListAssociation( HashTable_t oHashTable,
const char* pcKey )
{
assert ( oHashTable );
assert ( pcKey );
if ( 0 == strlen( pcKey ) )
{
return NULL;
}
int iHash_key = -1;
iHash_key = HashTable_hash( pcKey, oHashTable->uiBucketCount );
assert( iHash_key >= 0 );
return oHashTable->bucketTable[iHash_key];
}
static ListNode* ListAssociation_getListNode( const ListAssociation* pListAssociation,
const char* pcKey )
{
assert ( pListAssociation );
assert ( pcKey );
if ( 0 == strlen( pcKey ) )
{
return NULL;
}
ListNode* pListNode = pListAssociation->head_node;
while ( pListNode )
{
if ( 0 == strcmp ( pcKey, pListNode->key ) )
{
return pListNode;
}
pListNode = pListNode->next;
}
return NULL;
}
static int ListAssociation_put( ListAssociation* pListAssociation,
const char* pcKey,
const void* pvValue )
{
assert ( pListAssociation );
assert ( pcKey );
assert ( pvValue );
if ( ListAssociation_getListNode( pListAssociation, pcKey ) )
{
return 0;
}
else
{
ListNode* new_node = ( ListNode* )MyAlloc ( sizeof( ListNode ) );
if ( NULL == new_node )
{
return 0;
}
char* key = ( char* ) MyAlloc ( strlen ( pcKey ) + 1 );
if ( NULL == key )
{
free( new_node );
return 0;
}
new_node->key = key;
strcpy ( new_node->key, pcKey );
new_node->value = ( void* )pvValue; //??
if ( 0 == pListAssociation->uiNodeLength ) // The list is empty
{
pListAssociation->head_node = new_node;
pListAssociation->tail_node = new_node;
pListAssociation->uiNodeLength = 1;
return 1;
}
if ( 1 == pListAssociation->uiNodeLength ) // Only one key-value in list
{
pListAssociation->head_node->next = new_node;
pListAssociation->tail_node = new_node;
pListAssociation->uiNodeLength = 2;
return 1;
}
else // More than one key-value in list
{
pListAssociation->tail_node->next = new_node;
pListAssociation->tail_node = new_node;
pListAssociation->uiNodeLength += 1;
return 1;
}
}
}
static ListNode* HashTable_getListNode( HashTable_t oHashTable, const char* pcKey )
{
assert ( oHashTable );
assert ( pcKey );
if ( 0 == strlen( pcKey ) )
{
return NULL;
}
ListAssociation* pListAssociation = NULL;
if ( 0 == HashTable_getLength( oHashTable ) )
{
return NULL;
}
pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );
if ( NULL == pListAssociation )
{
return NULL;
}
return ListAssociation_getListNode( pListAssociation, pcKey );
}
static int Rehash( HashTable_t oHashTable )
{
assert( oHashTable );
assert( oHashTable->uiBindingsCount >= oHashTable->uiBucketCount );
giBucketCountIndex = GetBucketSizeIndex( oHashTable->uiBucketCount );
assert( giBucketCountIndex >= 0 );
unsigned long uiBucketCounts = 0;
if ( ( ARRAY_SIZE - 1 ) == giBucketCountIndex )
{
return 1;
}
++giBucketCountIndex;
uiBucketCounts = kBucketCount[giBucketCountIndex];
// Create a new ListAssociation** base on uiBucketCounts
struct ListAssociation** pNewListAssociation = NULL;
pNewListAssociation = ( ListAssociation** )MyAlloc( uiBucketCounts * sizeof( ListAssociation* ) );
if ( NULL == pNewListAssociation )
{
return 0;
}
// Update the old ListAssociation** to new ListAssociation**
{
unsigned long uiBucketCount = oHashTable->uiBucketCount;
unsigned long i = 0;
for ( i = 0; i < uiBucketCount; ++i )
{
struct ListAssociation* pOldListAssociation = NULL;
pOldListAssociation = oHashTable->bucketTable[i];
if ( NULL == pOldListAssociation )
{
continue;
}
ListNode* pTempListNode = pOldListAssociation->head_node;
// Iterate current ListNode and update the bindings to new table
while ( pTempListNode )
{
int iHash_key = -1;
iHash_key = HashTable_hash( pTempListNode->key, uiBucketCounts );
assert( iHash_key >= 0 );
ListAssociation* pListAssociation = pNewListAssociation[iHash_key];
if ( NULL == pListAssociation ) // There is no ListAssociation* in table
{
pListAssociation = ( ListAssociation* )MyAlloc ( sizeof( ListAssociation ) );
if ( NULL == pListAssociation )
{
return 0;
}
// Put ListAssociation* into new table
pNewListAssociation[iHash_key] = pListAssociation;
}
pOldListAssociation->head_node = pOldListAssociation->head_node->next;
if ( 0 == ListAssociation_put_help_rehash( pListAssociation, pTempListNode ) )
{
return 0;
}
pTempListNode = pOldListAssociation->head_node;
}
// Do not forget to release memory
free( pOldListAssociation );
pOldListAssociation = NULL;
}
}
free( oHashTable->bucketTable );
// Update for the new BucketSize
oHashTable->bucketTable = pNewListAssociation;
oHashTable->uiBucketCount = uiBucketCounts;
return 1;
}
static int ListAssociation_put_help_rehash( ListAssociation* pListAssociation,
ListNode* pListNode )
{
assert ( pListAssociation );
assert ( pListNode );
if ( ListAssociation_getListNode( pListAssociation, pListNode->key ) )
{
return 0;
}
else
{
if ( 0 == pListAssociation->uiNodeLength ) // The list is empty
{
pListAssociation->head_node = pListNode;
pListAssociation->tail_node = pListNode;
pListAssociation->tail_node->next = NULL;
pListAssociation->uiNodeLength = 1;
return 1;
}
// Only one key-value in list
if ( 1 == pListAssociation->uiNodeLength )
{
pListAssociation->head_node->next = pListNode;
pListAssociation->tail_node = pListNode;
pListAssociation->tail_node->next = NULL;
pListAssociation->uiNodeLength = 2;
return 1;
}
else // More than one key-value in list
{
pListAssociation->tail_node->next = pListNode;
pListAssociation->tail_node = pListNode;
pListAssociation->tail_node->next = NULL;
pListAssociation->uiNodeLength += 1;
return 1;
}
}
}
static int GetBucketSizeIndex( unsigned long uiBucketCounts )
{
int i = 0;
for ( i = 0; i < ARRAY_SIZE; ++i )
{
if ( uiBucketCounts == kBucketCount[i] )
{
giBucketCountIndex = i;
return giBucketCountIndex;
}
}
return -1;
}