Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

668 lines
16 KiB

//
// Copyright (C) 2000, Microsoft Corporation
//
// File: shash.c
//
// Contents: Generic hashtable
// Classes:
//
// History: April. 9 2001, Author: Rohanp
//
//-----------------------------------------------------------------------------
#ifdef KERNEL_MODE
#include <ntos.h>
#include <string.h>
#include <fsrtl.h>
#include <zwapi.h>
#include <windef.h>
#else
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#endif
#include <shash.h>
extern
ULONG
SHashComputeHashValue(
IN void* lpv
);
extern
int
SHashMatchNameKeysCaseInsensitive( void* pvKey1,
void* pvKey2
);
extern
void*
SHashAllocate( ULONG cbAlloc );
extern
void
SHashFree( void* lpv );
extern
BOOLEAN
SHashReadLockTable(PSHASH_TABLE pTable);
extern
BOOLEAN
SHashWriteLockTable(PSHASH_TABLE pTable);
extern
BOOLEAN
SHashReadUnLockTable(PSHASH_TABLE pTable);
extern
BOOLEAN
SHashWriteUnLockTable(PSHASH_TABLE pTable);
void *
SHashAllocLock(void);
void
SHashFreeLock(void * pMem);
DWORD g_ExpireSeconds = SHASH_DEFAULT_HASHTIMEOUT;
DWORD
ShashInitializeFunctionTable(PSHASH_TABLE pHashTable,
PSHASH_FUNCTABLE pFuncTable)
{
pHashTable->HashFunc = SHashComputeHashValue;
pHashTable->CompareFunc = SHashMatchNameKeysCaseInsensitive;
pHashTable->AllocFunc = SHashAllocate;
pHashTable->FreeFunc = SHashFree;
pHashTable->AllocHashEntryFunc = SHashAllocate;
pHashTable->FreeHashEntryFunc = SHashFree;
pHashTable->AllocLockFunc = SHashAllocLock;
pHashTable->FreeLockFunc = SHashFreeLock;
pHashTable->WriteLockFunc = SHashWriteLockTable;
pHashTable->ReadLockFunc = SHashReadLockTable;
pHashTable->ReleaseWriteLockFunc = SHashWriteUnLockTable;
pHashTable->ReleaseReadLockFunc = SHashReadUnLockTable;
if(pFuncTable->HashFunc)
{
pHashTable->HashFunc = pFuncTable->HashFunc;
}
if(pFuncTable->CompareFunc)
{
pHashTable->CompareFunc = pFuncTable->CompareFunc;
}
if ((pFuncTable->AllocFunc) || (pFuncTable->FreeFunc))
{
if ((pFuncTable->AllocFunc == NULL) ||
(pFuncTable->FreeFunc == NULL))
{
return STATUS_INVALID_PARAMETER;
}
pHashTable->AllocFunc = pFuncTable->AllocFunc;
pHashTable->FreeFunc = pFuncTable->FreeFunc;
//
// If Alloc/Free functions are user-defined, then AllocHashEntry and FreeHashEntry
// will also default to those same AllocFunc and FreeFunc calls. These can of course,
// be overridden with their own user-defined functions (see below).
//
pHashTable->AllocHashEntryFunc = pFuncTable->AllocFunc;
pHashTable->FreeHashEntryFunc = pFuncTable->FreeFunc;
}
// separate alloc/free functions for individual hash data.
if ((pFuncTable->AllocHashEntryFunc) || (pFuncTable->FreeHashEntryFunc))
{
if ((pFuncTable->AllocHashEntryFunc == NULL) ||
(pFuncTable->FreeHashEntryFunc == NULL))
{
return STATUS_INVALID_PARAMETER;
}
pHashTable->AllocHashEntryFunc = pFuncTable->AllocHashEntryFunc;
pHashTable->FreeHashEntryFunc = pFuncTable->FreeHashEntryFunc;
}
if ((pFuncTable->AllocLockFunc) || (pFuncTable->FreeLockFunc))
{
if ((pFuncTable->AllocLockFunc == NULL) ||
(pFuncTable->FreeLockFunc == NULL))
{
return STATUS_INVALID_PARAMETER;
}
pHashTable->AllocLockFunc = pFuncTable->AllocLockFunc;
pHashTable->FreeLockFunc = pFuncTable->FreeLockFunc;
}
if ((pFuncTable->WriteLockFunc) || (pFuncTable->ReadLockFunc) ||
(pFuncTable->ReleaseWriteLockFunc) || (pFuncTable->ReleaseReadLockFunc))
{
pHashTable->WriteLockFunc = pFuncTable->WriteLockFunc;
pHashTable->ReadLockFunc = pFuncTable->ReadLockFunc;
pHashTable->ReleaseWriteLockFunc = pFuncTable->ReleaseWriteLockFunc;
pHashTable->ReleaseReadLockFunc = pFuncTable->ReleaseReadLockFunc;
}
if(pFuncTable->NumBuckets)
{
pHashTable->NumBuckets = pFuncTable->NumBuckets;
}
else
{
pHashTable->NumBuckets = SHASH_DEFAULT_HASH_SIZE;
}
//test to see if buckets is a power of 2
if((pHashTable->NumBuckets & (pHashTable->NumBuckets - 1)) == 0)
{
pHashTable->Flags = SHASH_CAP_POWER_OF2;
}
pHashTable->Flags |= pFuncTable->Flags;
return STATUS_SUCCESS;
}
ULONG
ShashComputeNearestPowerOfTwo(ULONG Value)
{
ULONG PowerOf2 = Value;
while( PowerOf2 & (PowerOf2 - 1) )
{
PowerOf2 = PowerOf2 & (PowerOf2 - 1) ;
}
PowerOf2 <<= 1 ;
return PowerOf2;
}
NTSTATUS
ShashInitHashTable(
PSHASH_TABLE *ppHashTable,
PSHASH_FUNCTABLE pFuncTable)
{
PSHASH_TABLE pHashTable = NULL;
ULONG cbHashTable = 0;
DWORD LoopVar = 0;
ULONG NumBuckets = SHASH_DEFAULT_HASH_SIZE;
NTSTATUS Status = STATUS_SUCCESS;
PFNALLOC lAllocFunc = SHashAllocate;
PFNFREE lFreeFunc = SHashFree;
if(pFuncTable && pFuncTable->NumBuckets)
{
NumBuckets = pFuncTable->NumBuckets;
}
if(pFuncTable)
{
if ((pFuncTable->AllocFunc != NULL) &&
(pFuncTable->FreeFunc != NULL))
{
lAllocFunc = pFuncTable->AllocFunc;
lFreeFunc = pFuncTable->FreeFunc;
}
}
cbHashTable = sizeof(SHASH_TABLE) + NumBuckets * sizeof(SHASH_ENTRY);
pHashTable = lAllocFunc (cbHashTable);
if (pHashTable == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
Status = ShashInitializeFunctionTable(pHashTable,
pFuncTable);
if (Status != STATUS_SUCCESS)
{
lFreeFunc(pHashTable);
goto Exit;
}
pHashTable->pLock = pHashTable->AllocLockFunc();
if(pHashTable->pLock == NULL)
{
lFreeFunc (pHashTable);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
RtlZeroMemory(&pHashTable->HashBuckets[0], NumBuckets * sizeof(SHASH_ENTRY));
*ppHashTable = pHashTable;
for(LoopVar = 0; LoopVar < NumBuckets; LoopVar++)
{
InitializeListHead(&pHashTable->HashBuckets[LoopVar].ListHead);
}
Exit:
return Status;
}
void
ShashTerminateHashTable(
PSHASH_TABLE pHashTable
)
{
PFNFREE FreeFunc = NULL;
if(pHashTable != NULL)
{
pHashTable->FreeLockFunc( pHashTable->pLock);
FreeFunc = pHashTable->FreeFunc;
FreeFunc (pHashTable);
}
}
NTSTATUS
SHashInsertKey(IN PSHASH_TABLE pTable,
IN void * pData,
IN void * pvKeyIn,
IN DWORD InsertFlag)
{
ULONG dwHash = 0;
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY ple = NULL;
PLIST_ENTRY pleRemove = NULL;
PLIST_ENTRY pleStart = NULL;
PSHASH_HEADER pEntry = NULL;
PSHASH_HEADER pHeader = NULL;
int iSign = 0;
BOOLEAN LockAquired = TRUE;
dwHash = pTable->HashFunc(pvKeyIn) ;
if(pTable->Flags & SHASH_CAP_POWER_OF2)
{
dwHash = dwHash & (pTable->NumBuckets - 1);
}
else
{
dwHash = dwHash % pTable->NumBuckets;
}
LockAquired = pTable->WriteLockFunc(pTable);
if(!LockAquired)
{
Status = STATUS_LOCK_NOT_GRANTED;
goto Exit;
}
pHeader = (PSHASH_HEADER) pData;
pHeader->dwHash = dwHash;
pleStart = &pTable->HashBuckets[dwHash].ListHead ;
ple = pleStart->Flink ;
while( ple != pleStart )
{
pEntry = CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
iSign = pTable->CompareFunc(pEntry->pvKey, pvKeyIn ) ;
if( iSign > 0 )
{
break ;
}
else if((InsertFlag == SHASH_FAIL_IFFOUND) && (iSign == 0))
{
Status = STATUS_OBJECT_NAME_COLLISION;
goto Exit;
}
else if((InsertFlag == SHASH_REPLACE_IFFOUND) && (iSign == 0))
{
pEntry->Flags |= SHASH_FLAG_DELETE_PENDING;
if(InterlockedDecrement(&pEntry->RefCount) == 0)
{
pleRemove = ple;
ple = ple->Flink;
RemoveEntryList( pleRemove ) ;
InterlockedDecrement(&pTable->HashBuckets[dwHash].Count);
InterlockedDecrement(&pTable->TotalItems);
pTable->FreeHashEntryFunc(pEntry);
}
break;
}
ple = ple->Flink ;
}
InterlockedIncrement(&pHeader->RefCount);
InsertTailList( ple, &pHeader->ListEntry ) ;
InterlockedIncrement(&pTable->HashBuckets[dwHash].Count);
InterlockedIncrement(&pTable->TotalItems);
Exit:
if(LockAquired)
{
pTable->ReleaseWriteLockFunc(pTable);
}
return Status;
}
//
// Remove an item from the hash table
//
NTSTATUS
SHashRemoveKey( IN PSHASH_TABLE pTable,
IN void * pvKeyIn,
IN PFNREMOVEKEY pRemoveFunc
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY pleStart = NULL ;
PLIST_ENTRY ple= NULL ;
PSHASH_HEADER pEntry = NULL;
int iSign = 0;
DWORD dwHash = 0;
BOOLEAN LockAquired = TRUE;
dwHash = pTable->HashFunc(pvKeyIn) ;
if(pTable->Flags & SHASH_CAP_POWER_OF2)
{
dwHash= dwHash & (pTable->NumBuckets - 1);
}
else
{
dwHash = dwHash % pTable->NumBuckets;
}
LockAquired = pTable->WriteLockFunc(pTable);
if(!LockAquired)
{
Status = STATUS_LOCK_NOT_GRANTED;
goto Exit;
}
pleStart = &pTable->HashBuckets[dwHash].ListHead ;
ple = pleStart->Flink ;
while( ple != pleStart )
{
pEntry = (PSHASH_HEADER) CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
iSign = pTable->CompareFunc( pEntry->pvKey, pvKeyIn ) ;
if( iSign == 0 )
{
if(pRemoveFunc)
{
pRemoveFunc(pEntry);
}
pEntry->Flags |= SHASH_FLAG_DELETE_PENDING;
if(InterlockedDecrement(&pEntry->RefCount) == 0)
{
RemoveEntryList( ple ) ;
InterlockedDecrement(&pTable->HashBuckets[dwHash].Count);
InterlockedDecrement(&pTable->TotalItems);
pTable->FreeHashEntryFunc(pEntry);
}
break;
}
else if( iSign > 0 )
{
break ;
}
ple = ple->Flink ;
}
Exit:
if(LockAquired)
{
pTable->ReleaseWriteLockFunc(pTable);
}
return Status;
}
PSHASH_HEADER
SHashLookupKeyEx( IN PSHASH_TABLE pTable,
IN void* pvKeyIn
)
{
PSHASH_HEADER pEntry = NULL;
PLIST_ENTRY pleStart = NULL ;
PLIST_ENTRY ple= NULL ;
int iSign = 0;
DWORD dwHash = 0;
BOOLEAN LockAquired = TRUE;
dwHash = pTable->HashFunc(pvKeyIn) ;
if(pTable->Flags & SHASH_CAP_POWER_OF2)
{
dwHash= dwHash & (pTable->NumBuckets - 1);
}
else
{
dwHash = dwHash % pTable->NumBuckets;
}
LockAquired = pTable->ReadLockFunc(pTable);
if(!LockAquired)
{
goto Exit;
}
pleStart = &pTable->HashBuckets[dwHash].ListHead ;
ple = pleStart->Flink ;
while( ple != pleStart )
{
pEntry = (PSHASH_HEADER)CONTAINING_RECORD(ple, SHASH_HEADER, ListEntry);
iSign = pTable->CompareFunc( pEntry->pvKey, pvKeyIn ) ;
if( iSign == 0 )
{
InterlockedIncrement(&pEntry->RefCount);
pTable->ReleaseReadLockFunc(pTable);
return pEntry;
}
else if( iSign > 0 )
{
break ;
}
ple = ple->Flink ;
}
Exit:
if(LockAquired)
{
pTable->ReleaseReadLockFunc(pTable);
}
return NULL;
}
NTSTATUS
SHashIsKeyInTable( IN PSHASH_TABLE pTable,
IN void* pvKeyIn
)
{
NTSTATUS Status = STATUS_OBJECT_PATH_NOT_FOUND;
PSHASH_HEADER pEntry = NULL;
pEntry = SHashLookupKeyEx(pTable, pvKeyIn);
if(pEntry != NULL)
{
SHashReleaseReference(pTable, pEntry);
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
SHashGetDataFromTable( IN PSHASH_TABLE pTable,
IN void* pvKeyIn,
IN void ** ppData
)
{
NTSTATUS Status = STATUS_OBJECT_PATH_NOT_FOUND;
PSHASH_HEADER pEntry = NULL;
pEntry = SHashLookupKeyEx(pTable, pvKeyIn);
if(pEntry != NULL)
{
*ppData = (void *)pEntry;
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
SHashReleaseReference( IN PSHASH_TABLE pTable,
IN PSHASH_HEADER pData
)
{
if(InterlockedDecrement(&pData->RefCount) == 0)
{
pTable->WriteLockFunc(pTable);
InterlockedDecrement(&pTable->TotalItems);
RemoveEntryList( &pData->ListEntry ) ;
InterlockedDecrement(&pTable->HashBuckets[pData->dwHash].Count);
pTable->FreeHashEntryFunc(pData);
pTable->ReleaseWriteLockFunc(pTable);
}
return STATUS_SUCCESS;
}
NTSTATUS
SHashMarkForDeletion( IN PSHASH_TABLE pTable,
IN PSHASH_HEADER pData
)
{
NTSTATUS Status = STATUS_LOCK_NOT_GRANTED;
BOOLEAN LockAquired = TRUE;
LockAquired = pTable->WriteLockFunc(pTable);
if(LockAquired)
{
pData->Flags |= SHASH_FLAG_DELETE_PENDING;
if(InterlockedDecrement(&pData->RefCount) == 0)
{
InterlockedDecrement(&pTable->TotalItems);
RemoveEntryList( &pData->ListEntry ) ;
InterlockedDecrement(&pTable->HashBuckets[pData->dwHash].Count);
pTable->FreeHashEntryFunc(pData);
}
pTable->ReleaseWriteLockFunc(pTable);
Status = STATUS_SUCCESS;
}
return Status;
}
#if 0
NTSTATUS
SHashReplaceInTable(IN PSHASH_TABLE pTable,
IN void* pvKeyIn,
IN OUT PVOID *ppData
)
{
NTSTATUS Status = STATUS_SUCCESS;
Status = STATUS_OBJECT_PATH_NOT_FOUND;
return Status;
}
#endif
NTSTATUS
ShashEnumerateItems(IN PSHASH_TABLE pTable,
IN PFNENUMERATEKEY pfnCallback,
IN LPVOID lpvClientContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY pCur = NULL ;
PLIST_ENTRY pNext = NULL;
PLIST_ENTRY ListHead = NULL;
PSHASH_HEADER pEntry = NULL;
DWORD i = 0;
BOOLEAN LockAquired = TRUE;
LockAquired = pTable->ReadLockFunc(pTable);
if(!LockAquired)
{
Status = STATUS_LOCK_NOT_GRANTED;
goto Exit;
}
for( i = 0; i < pTable->NumBuckets && Status == STATUS_SUCCESS; i++ )
{
ListHead = &pTable->HashBuckets[i].ListHead ;
pCur = ListHead->Flink ;
while( pCur != ListHead && Status == STATUS_SUCCESS )
{
//
// save the next pointer now,
//
pNext = pCur->Flink ;
pEntry = CONTAINING_RECORD( pCur, SHASH_HEADER, ListEntry ) ;
if ((pEntry->Flags & SHASH_FLAG_DELETE_PENDING) == 0)
{
Status = pfnCallback( pEntry,
lpvClientContext );
}
//
// move to the next item
//
pCur = pNext ;
}
}
Exit:
if(LockAquired)
{
pTable->ReleaseReadLockFunc(pTable);
}
return Status;
}