|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1992, 1998.
//
// File: domhash.c
//
// Contents: Implementation of public API for domain name lookup table
//
// History: SethuR -- Implemented
// MikeSwa -- Modified for Domain Name lookup 2/98
//
// Notes:
// 2/98 The major difference between the DFS version and the domain
// name lookup is the size of the table, the ability for
// wildcard lookups (*.foo.com), and the reverse order of the
// lookup (com hashes first in foo.com). To make the code more
// readable given its new purpose, the files, structures, and
// functions have been given non DFS-centric names. A quick
// mapping of the major files is (for those familiar with the
// DFS code):
// domhash.h (prefix.h) - Public include file
// _domhash.h (prefixp.h) - Private include file
// domhash.cpp (prefix.c) - Implementation of API
// _domhash.cpp (prefixp.c) - Private helper functions.
//
//--------------------------------------------------------------------------
#include "_domhash.h"
#include <stdio.h>
#define _ASSERT_DOMAIN_STRING(pstr) _ASSERT((_tcslen(pstr->Buffer)*sizeof(TCHAR)) == pstr->Length)
//---[ DOMAIN_NAME_TABLE ]-----------------------------------------------------
//
//
// Description:
// Class constructor
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
DOMAIN_NAME_TABLE::DOMAIN_NAME_TABLE() { ULONG i; m_dwSignature = DOMAIN_NAME_TABLE_SIG; m_cLookupAttempts = 0; m_cLookupSuccesses = 0; m_cLookupCollisions = 0; m_cHashCollisions = 0; m_cStringCollisions = 0; m_cBucketsUsed = 0; INITIALIZE_DOMAIN_NAME_TABLE_ENTRY(&RootEntry);
// Initialize the various buckets.
for (i = 0;i < NO_OF_HASH_BUCKETS;i++) { INITIALIZE_BUCKET(Buckets[i]); }
NamePageList.pFirstPage = NULL; }
//---[ ~DOMAIN_NAME_TABLE ]----------------------------------------------------
//
//
// Description:
// Class destructor - Dumps some stats to stderr
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
DOMAIN_NAME_TABLE::~DOMAIN_NAME_TABLE() { PNAME_PAGE pCurrentPage = NamePageList.pFirstPage; PNAME_PAGE pNextPage = NULL;
#ifdef DEBUG
//$$TODO find a more appropriate way to dump this (don't use *printf)
ULONG cTotalCollisions = m_cHashCollisions + m_cStringCollisions; ULONG ulPercentHash = cTotalCollisions ? (m_cHashCollisions*100/cTotalCollisions) : 0; ULONG ulPercentDesign = cTotalCollisions ? (m_cStringCollisions*100/cTotalCollisions) : 0; ULONG ulPercentCollisions = m_cLookupAttempts ? (m_cLookupCollisions*100/m_cLookupAttempts) : 0; ULONG ulAveCollisions = m_cLookupCollisions ? (cTotalCollisions/m_cLookupCollisions) : 0;
fprintf(stderr, "\nHash statistics\n"); fprintf(stderr, "==============================================\n"); fprintf(stderr, "Total lookup attempts %d\n", m_cLookupAttempts); fprintf(stderr, "Total lookup successes %d\n", m_cLookupSuccesses); fprintf(stderr, "Total lookups with hash collisions %d\n", m_cLookupCollisions); fprintf(stderr, "%% of lookups with hash collisions %d%%\n", ulPercentCollisions); fprintf(stderr, "Total hash Collisions %d\n", cTotalCollisions); fprintf(stderr, "Average length of lookups collisions %d\n", ulAveCollisions); fprintf(stderr, "Hash collisions due to hash function %d\n", m_cHashCollisions); fprintf(stderr, "Hash collisions due to string parent %d\n", m_cStringCollisions); fprintf(stderr, "%% of collsions because of hash function %d%%\n", ulPercentHash); fprintf(stderr, "%% of collsions because of basic design %d%%\n", ulPercentDesign); fprintf(stderr, "Total number of buckets used %d\n", m_cBucketsUsed); fprintf(stderr, "%% buckets used %d%%\n", m_cBucketsUsed*100/NO_OF_HASH_BUCKETS);
DumpTableContents(); #endif //DEBUG
//Free Name pages
while (pCurrentPage) { pNextPage = pCurrentPage->pNextPage; FREE_NAME_PAGE(pCurrentPage); pCurrentPage = pNextPage; }
}
//+---------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::HrInit
//
// Synopsis: Member function for initializing the domain name table
//
// Returns: HRESULT - S_OK on success
//
// History: 04-18-94 SethuR Created (as DfsInitializePrefixTable)
// 03-03-98 MikeSwa modified for Domain Table
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrInit() { TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrInit"); HRESULT hr = S_OK;
// Initialize the name page list.
NamePageList.pFirstPage = ALLOCATE_NAME_PAGE(); if (NamePageList.pFirstPage != NULL) { INITIALIZE_NAME_PAGE(NamePageList.pFirstPage); } else { hr = E_OUTOFMEMORY; }
TraceFunctLeave(); return hr; }
//+---------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::HrPrivInsertDomainName
//
// Synopsis: API for inserting a path in the prefix table
//
// Arguments: [pPath] -- the path to be looked up.
//
// [pvNewData] -- BLOB associated with the path
//
// [dwDomainNameTableFlags] -- flags that describe insert options
// DMT_INSERT_AS_WILDCARD -
// Set if the domain is NOT a wildcard
// domain, but it should be treated as one (more efficient
// than reallocated a string to prepend "*.").
// DMT_REPLACE_EXISTRING -
// Replace existing data if it exists. Old data is saved
// in ppvOldData.
//
// [ppvOldData] -- Old Data (if any) that was previously associated
// with this domain name. If NULL, previous data will
// not be returned
// Returns: HRESULT - S_OK on success
//
// History: 04-18-94 SethuR Created (as DfsInsertInPrefixTable)
// 03-02-98 MikeSwa Modified for Domain Table
// 05-11-98 MikeSwa... modified to support replace and treat
// as wildcard options.
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrPrivInsertDomainName( IN PDOMAIN_STRING pstrDomainName, IN DWORD dwDomainNameTableFlags, IN PVOID pvNewData, OUT PVOID *ppvOldData) { TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrPrivInsertDomainName"); HRESULT hr = S_OK; TCHAR Buffer[MAX_PATH_SEGMENT_SIZE]; PTCHAR NameBuffer = Buffer; USHORT cbNameBuffer = sizeof(Buffer); DOMAIN_STRING Path,Name; ULONG BucketNo; PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; PDOMAIN_NAME_TABLE_ENTRY pParentEntry = NULL; BOOL fNameFound = FALSE; BOOL fWildcard = FALSE; BOOL fReplaced = FALSE;
_ASSERT_DOMAIN_STRING(pstrDomainName);
// There is one special case, i.e., in which the domain name is '*'.
// Since this is the WILDCARD_CHAR which is treated in a special
// way, we do the processing upfront.
if (pstrDomainName->Length == 0 || pvNewData == NULL) { hr = E_INVALIDARG; goto Exit; }
if (ppvOldData) *ppvOldData = NULL;
Path.Length = pstrDomainName->Length; Path.MaximumLength = pstrDomainName->MaximumLength; Path.Buffer = pstrDomainName->Buffer; pParentEntry = &RootEntry;
//Check if wildcard "*."
if (DMT_INSERT_AS_WILDCARD & dwDomainNameTableFlags) { fWildcard = TRUE; _ASSERT(!fAdjustPathIfWildcard(pstrDomainName, &Path)); } else if (fAdjustPathIfWildcard(pstrDomainName, &Path)) { fWildcard = TRUE; } else if (fIsWildcardRoot(pstrDomainName)) { if (RootEntry.pWildCardData != NULL) { hr = DOMHASH_E_DOMAIN_EXISTS; } else { RootEntry.pWildCardData = pvNewData; } goto Exit; }
if (Path.Length >= MAX_PATH_SEGMENT_SIZE) { NameBuffer = (PTCHAR) pvMalloc(Path.Length + sizeof(TCHAR)); if (NameBuffer == NULL) { hr = E_OUTOFMEMORY; DebugTrace((LPARAM) hr, "ERROR: Unable to allocate %d non-paged bytes", (Path.Length + sizeof(TCHAR)) ); goto Exit; } else { cbNameBuffer = Path.Length + sizeof(TCHAR); } }
while (Path.Length > 0) { Name.Length = 0; Name.Buffer = NameBuffer; Name.MaximumLength = cbNameBuffer;
// Process the name segment
BucketNo = ulSplitCaseInsensitivePath(&Path,&Name);
if (Name.Length > 0) { // Lookup the table to see if the name segment already exists.
LookupBucket(&(Buckets[BucketNo]),&Name,pParentEntry,&pEntry,&fNameFound);
DebugTrace((LPARAM) pEntry, "Returned pEntry");
if (pEntry == NULL) { // Initialize the new entry and initialize the name segment.
pEntry = ALLOCATE_DOMAIN_NAME_TABLE_ENTRY(this);
if (!pEntry) { hr = E_OUTOFMEMORY; break; }
INITIALIZE_DOMAIN_NAME_TABLE_ENTRY(pEntry);
// Allocate the name space entry if there is no entry in the
// name page.
if (!fNameFound) { PTSTR pBuffer;
// Allocate the entry in the name page.
pBuffer = ALLOCATE_NAME_PAGE_ENTRY(NamePageList,(Name.Length/sizeof(TCHAR)));
if (pBuffer != NULL) { RtlCopyMemory(pBuffer,Name.Buffer,Name.Length); pEntry->PathSegment = Name; pEntry->PathSegment.Buffer = pBuffer; } else { hr = E_OUTOFMEMORY; //We shan't leak memory
FREE_DOMAIN_NAME_TABLE_ENTRY(pEntry); pEntry = NULL; break; } } else pEntry->PathSegment = Name;
// thread the entry to point to the parent.
pEntry->pParentEntry = pParentEntry;
// Insert the entry in the bucket.
if (0 == Buckets[BucketNo].NoOfEntries) InterlockedIncrement((PLONG) &m_cBucketsUsed);
INSERT_IN_BUCKET(Buckets[BucketNo],pEntry);
// Insert the entry in the parent's children list.
INSERT_IN_CHILD_LIST(pEntry, pParentEntry); } else { // Increment the no. of children associated with this entry
pEntry->NoOfChildren++; }
pParentEntry = pEntry; } else { hr = E_INVALIDARG; DebugTrace((LPARAM) hr, "ERROR: Unable to insert domain name"); break; } }
if (SUCCEEDED(hr)) { // The entry was successfully inserted in the prefix table. Update
// the data (BLOB) associated with it.
// We do it outside the loop to prevent redundant comparisons within
// the loop.
if (fWildcard) { if (pEntry->pWildCardData) //make sure we aren't writing over anything
{ if (ppvOldData) *ppvOldData = pEntry->pWildCardData;
if (DMT_REPLACE_EXISTRING & dwDomainNameTableFlags) { fReplaced = TRUE; pEntry->pWildCardData = pvNewData; } else { hr = DOMHASH_E_DOMAIN_EXISTS; } } else { pEntry->pWildCardData = pvNewData; } } else { if (pEntry->pData) //make sure we aren't writing over anything
{ if (ppvOldData) *ppvOldData = pEntry->pData;
if (DMT_REPLACE_EXISTRING & dwDomainNameTableFlags) { fReplaced = TRUE; pEntry->pData = pvNewData; } else { hr = DOMHASH_E_DOMAIN_EXISTS; } } else { pEntry->pData = pvNewData; } } }
// If a new entry was not successfully inserted we need to walk up the chain
// of parent entries to undo the increment to the reference count and
// remove the entries from their parent links.
if (FAILED(hr) || //hr could be set in above if statement
fReplaced) //remove extra child counts
{ while (pParentEntry != NULL) { PDOMAIN_NAME_TABLE_ENTRY pMaybeTempEntry;
pMaybeTempEntry = pParentEntry; pParentEntry = pParentEntry->pParentEntry;
if (pParentEntry && --pMaybeTempEntry->NoOfChildren == 0) { //
// If pParentEntry == NULL, pMaybeTempEntry is
// RootEntry. Do not try to remove it.
//
_ASSERT(FAILED(hr) && "We shouldn't get here during replace"); REMOVE_FROM_CHILD_LIST(pMaybeTempEntry); REMOVE_FROM_BUCKET(pMaybeTempEntry); FREE_DOMAIN_NAME_TABLE_ENTRY(pMaybeTempEntry); } } }
Exit:
TraceFunctLeave(); return hr; }
//+---------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::HrFindDomainName
//
// Synopsis: Method API for looking up a name segment in a prefix table
//
// Arguments: IN pPath -- the path to be looked up.
//
// OUT ppData -- placeholder for the BLOB for the prefix.
//
// IN fExtactMatch -- FALSE if wildcard matches are allowed
//
// Returns: HRESULT - S_OK on success
//
// History: 04-18-94 SethuR Created (as DfsLookupPrefixTable)
// 03-02-98 MikeSwa Modified for Domain Table
// 06-03-98 MikeSwa Modified to use new HrLookupDomainName
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrFindDomainName( PDOMAIN_STRING pPath, PVOID *ppData, BOOL fExactMatch) { TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrFindDomainName"); HRESULT hr = S_OK; PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; BOOL fExactMatchFound = FALSE; _ASSERT_DOMAIN_STRING(pPath);
if (pPath->Length == 0) { hr = E_INVALIDARG; } else { hr = HrLookupDomainName(pPath, &fExactMatchFound, &pEntry);
// Update the BLOB placeholder with the results of the lookup.
if (SUCCEEDED(hr)) { _ASSERT(pEntry); if (fExactMatchFound && pEntry->pData) { //exact match found & non-wildcard data is there... use it!
*ppData = pEntry->pData; } else if (fExactMatch) //exact match requested, but none found
{ hr = DOMHASH_E_NO_SUCH_DOMAIN; } else //exact match not requested
{ //Find the first ancestor with wildcard data
while (pEntry->pParentEntry && !pEntry->pWildCardData) { _ASSERT(pEntry != &RootEntry); pEntry = pEntry->pParentEntry; } *ppData = pEntry->pWildCardData; if (!*ppData) //no wildcard match found
{ _ASSERT(pEntry == &RootEntry); //We should search back to root
hr = DOMHASH_E_NO_SUCH_DOMAIN; } } } else if (!fExactMatch && (DOMHASH_E_NO_SUCH_DOMAIN == hr)) { //if we don't require an exact match.... check the wildcard root
if (RootEntry.pWildCardData) { hr = S_OK; *ppData = RootEntry.pWildCardData; } }
} TraceFunctLeave(); return hr;
}
//+---------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::HrRemoveDomainName
//
// Synopsis: private fn. for looking up a name segment in a prefix table
//
// Arguments: [pPath] -- the path to be removed from table.
// [ppvData] - Data that WAS stored in entry
//
// Returns: HRESULT
// S_OK on success
// DOMHASH_E_NO_SUCH_DOMAIN if not found
//
// History: 04-18-94 SethuR Created (as DfsRemoveFromPrefixTable)
// 03-03-98 MikeSwa - Updated for Domain Table
// 06-03-98 MikeSwa - Modified to use new HrLookupDomainName
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrRemoveDomainName(PDOMAIN_STRING pPath, PVOID *ppvData) { TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrRemoveDomainName"); HRESULT hr = S_OK; DOMAIN_STRING Path; BOOL fWildcard = FALSE; BOOL fExactMatchFound = FALSE; _ASSERT_DOMAIN_STRING(pPath);
PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; PDOMAIN_NAME_TABLE_ENTRY pTempEntry = NULL;
if (!ppvData) { hr = E_INVALIDARG; goto Exit; }
if (pPath->Length == 0) { hr = E_INVALIDARG; goto Exit; }
Path.Length = pPath->Length; Path.MaximumLength = pPath->MaximumLength; Path.Buffer = pPath->Buffer;
if (fAdjustPathIfWildcard(pPath, &Path)) { fWildcard = TRUE; } else if (fIsWildcardRoot(pPath)) { *ppvData = RootEntry.pWildCardData; if (!*ppvData) { hr = DOMHASH_E_NO_SUCH_DOMAIN; } RootEntry.pWildCardData = NULL; goto Exit; }
hr = HrLookupDomainName(&Path, &fExactMatchFound, &pEntry);
if (SUCCEEDED(hr)) {
if (!fExactMatchFound) { //only a partial match was found
hr = DOMHASH_E_NO_SUCH_DOMAIN; goto Exit; }
// Destroy the association between the data associated with
// this prefix.
if (!fWildcard) { *ppvData = pEntry->pData; pEntry->pData = NULL; } else { *ppvData = pEntry->pWildCardData; pEntry->pWildCardData = NULL; }
if (!*ppvData) //no data of of requested type in entry
{ //Make sure this isn't a completely NULL data leaf node (ie no way to delete it)
_ASSERT(pEntry->pFirstChildEntry || pEntry->pData || pEntry->pWildCardData); hr = DOMHASH_E_NO_SUCH_DOMAIN; goto Exit; }
// found an exact match for the given path name in the table.
// traverse the list of parent pointers and delete them if
// required.
RemoveTableEntry(pEntry); }
Exit: TraceFunctLeave(); return hr; }
//+---------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::HrLookupDomainName
//
// Synopsis: Private function for looking up an *entry* in the table. It
// makes no guarantees that there is user data available for the
// returned entry. This is the caller's responsibility. It will
// match the longest partial path... check fExactMatch to see
// if an exact match was found
//
// Arguments: IN pPath -- the path to be looked up.
//
// OUT pfExactMatch -- Exact Match was found
//
// OUT ppEntry -- The matching entry for the path.
//
//
// Returns: HRESULT
// S_OK on success
// DOMHASH_E_NO_SUCH_DOMAIN if not found
// E_OUTOFMEMORY
//
// History: 04-18-94 SethuR Created (as _LookupPrefixTable)
// 03-03-98 MikeSwa Modified for Domain Table
// 06-03-98 MikeSwa ExactMatch changed to OUT parameter
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrLookupDomainName( DOMAIN_STRING *pPath, BOOL *pfExactMatch, PDOMAIN_NAME_TABLE_ENTRY *ppEntry) { TraceFunctEnterEx((LPARAM) this, "DOMAIN_NAME_TABLE::HrLookupDomainName"); HRESULT hr = S_OK; DOMAIN_STRING Path = *pPath; TCHAR Buffer[MAX_PATH_SEGMENT_SIZE]; PTCHAR NameBuffer = Buffer; USHORT cbNameBuffer = sizeof(Buffer); DOMAIN_STRING Name; ULONG BucketNo; BOOL fPrefixFound = FALSE; PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; PDOMAIN_NAME_TABLE_ENTRY pParentEntry = &RootEntry; BOOL fNameFound = FALSE;
_ASSERT(Path.Buffer[0] != PATH_DELIMITER);
*pfExactMatch = FALSE;
if (Path.Length >= MAX_PATH_SEGMENT_SIZE) { NameBuffer = (PTCHAR) pvMalloc(Path.Length + sizeof(TCHAR)); if (NameBuffer == NULL) { hr = E_OUTOFMEMORY; DebugTrace((LPARAM) hr, "ERROR: Unable to allocate %d non-paged bytes", (Path.Length + sizeof(TCHAR)) ); goto Exit; } else { cbNameBuffer = Path.Length + sizeof(TCHAR); } }
while (Path.Length > 0) { Name.Length = 0; Name.Buffer = NameBuffer; Name.MaximumLength = cbNameBuffer;
BucketNo = ulSplitCaseInsensitivePath(&Path,&Name);
if (Name.Length > 0) { // Process the name segment
// Lookup the bucket to see if the entry exists.
LookupBucket(&(Buckets[BucketNo]),&Name,pParentEntry,&pEntry,&fNameFound);
DebugTrace((LPARAM) pEntry, "Returned pEntry");
if (pEntry != NULL) { *pfExactMatch = TRUE; _ASSERT(fNameFound && "Lookup bucket is broken"); // Cache the data available for this prefix if any.
*ppEntry = pEntry; } else { *pfExactMatch = FALSE; break; }
// set the stage for processing the next name segment.
pParentEntry = pEntry; } }
//Not even a partial match was found
if (!*ppEntry) { _ASSERT(FALSE == *pfExactMatch); hr = DOMHASH_E_NO_SUCH_DOMAIN; DebugTrace((LPARAM) hr, "INFO: Path %s not found", pPath->Buffer); }
Exit:
TraceFunctLeave(); return hr; }
//---[ DOMAIN_NAME_TABLE::HrIterateOverSubDomains ]----------------------------
//
//
// Description:
//
// Parameters:
// IN strDomain - Domain string to search for subdomains of
// (should not start with "*.")
// IN pfn - Mapping function (described below)
// IN pvContext - Context ptr pass to mapping function
//
// Notes:
// VOID DomainTableInteratorFunction(
// IN PVOID pvContext, //context passed to HrIterateOverSubDomains
// IN PVOID pvData, //data entry to look at
// IN BOOL fWildcardData, //true if data is a wildcard entry
// OUT BOOL *pfContinue, //TRUE if iterator should continue to the next entry
// OUT BOOL *pfRemoveEntry); //TRUE if entry should be deleted
//
// Returns:
// S_OK on success
// DOMHASH_E_NO_SUCH_DOMAIN if there is no matching domain or subdomains
// History:
// 6/5/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
HRESULT DOMAIN_NAME_TABLE::HrIterateOverSubDomains( IN DOMAIN_STRING *pstrDomain, IN DOMAIN_ITR_FN pfn, IN PVOID pvContext) { HRESULT hr = S_OK; PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; PDOMAIN_NAME_TABLE_ENTRY pRootEntry = NULL; PDOMAIN_NAME_TABLE_ENTRY pNextEntry = NULL; BOOL fExactMatchFound = FALSE; BOOL fContinue = TRUE; BOOL fDelete = FALSE; DWORD cDomainsFound = 0; BOOL fWildcard = FALSE;
_ASSERT(pfn && "Invalid Param - pfn"); _ASSERT((!pstrDomain || (WILDCARD_CHAR != pstrDomain->Buffer[0])) && "Invalid param - string starts with '*'");
if (pstrDomain) { hr = HrLookupDomainName(pstrDomain, &fExactMatchFound, &pEntry);
if (FAILED(hr)) goto Exit;
if (!fExactMatchFound) //there must be an entry at root of subtree
{ hr = DOMHASH_E_NO_SUCH_DOMAIN; goto Exit; }
_ASSERT(pEntry); } else { //if !pstrDomain.., iterate over entire hash table
pEntry = &RootEntry; }
pRootEntry = pEntry;
//Traverse all the child entries of pRootEntry (preorder)
while (pEntry) { //get next entry before it is deleted
pNextEntry = pNextTableEntry(pEntry, pRootEntry);
//This check must be done before call to RemoveTableEntry
//If there is no wildcard data, then entry might be deleted
//after call to RemoveTableEntry (if it has no children)
fWildcard = (NULL != pEntry->pWildCardData);
if (pEntry->pData) { cDomainsFound++; pfn(pvContext, pEntry->pData, FALSE, &fContinue, &fDelete); if (fDelete) { pEntry->pData = NULL; RemoveTableEntry(pEntry); } if (!fContinue) break; }
if (fWildcard) { cDomainsFound++; pfn(pvContext, pEntry->pWildCardData, TRUE, &fContinue, &fDelete); if (fDelete) { pEntry->pWildCardData = NULL; RemoveTableEntry(pEntry); } if (!fContinue) break; } pEntry = pNextEntry; }
if (!cDomainsFound) hr = DOMHASH_E_NO_SUCH_DOMAIN;
Exit: return hr; }
//+----------------------------------------------------------------------------
//
// Function: DOMAIN_NAME_TABLE::pvNextDomainName
//
// Synopsis: Enumerates the entries in the table in ordered fashion.
// Note that state is maintained between calls to
// pvNextDomainName - the caller must ensure that the table
// is not modified between calls to pNextTableEntry by acquiring
// an external read lock
//
// Arguments: IN OUT PVOID *ppvContext - context used to hold place
//
// Returns: Valid pointer to data associated with the next Prefix Table
// entry, or NULL if at the end of the enumeration.
//
//-----------------------------------------------------------------------------
PVOID DOMAIN_NAME_TABLE::pvNextDomainName(IN OUT PVOID *ppvContext) { PDOMAIN_NAME_TABLE_ENTRY pEntry, pNextEntry; PVOID pvData = NULL; bool fDataUsed = false;
_ASSERT(ppvContext);
if ((PVOID) this == *ppvContext) { *ppvContext = NULL; goto Exit; }
//Find entry to get data for
if (!*ppvContext) { //We're starting over
pNextEntry = &RootEntry;
//Find first entry with valid data
while (pNextEntry != NULL && pNextEntry->pData == NULL && pNextEntry->pWildCardData == NULL) { pNextEntry = pNextTableEntry(pNextEntry); } } else { //Use context provided as starting point
if (ENTRY_SIG == **((DWORD**) ppvContext)) { pNextEntry = (PDOMAIN_NAME_TABLE_ENTRY) *ppvContext; } else { _ASSERT(WILDCARD_SIG == **((DWORD **) ppvContext)); pNextEntry = CONTAINING_RECORD(*ppvContext, DOMAIN_NAME_TABLE_ENTRY, dwWildCardSig); _ASSERT(ENTRY_SIG == pNextEntry->dwEntrySig); fDataUsed = true; }
//If this is a next entry... either pData or pWildCard should be non-NULL
_ASSERT(pNextEntry->pData || pNextEntry->pWildCardData); }
pEntry = pNextEntry;
//Save data to return in pvData
if (pEntry != NULL) { if (pEntry->pData && !fDataUsed) { pvData = pEntry->pData; } else { _ASSERT(pEntry->pWildCardData); pvData = pEntry->pWildCardData; } }
//Determine what context to return
if (pNextEntry != NULL) { if (!fDataUsed && pNextEntry->pWildCardData && pEntry->pData) { //use wildcard data next time through
*ppvContext = (PVOID) &(pNextEntry->dwWildCardSig); } else { do //find next entry that does not point to NULL info
{ pNextEntry = pNextTableEntry( pNextEntry ); } while ( pNextEntry != NULL && pNextEntry->pData == NULL && pNextEntry->pWildCardData == NULL); *ppvContext = (PVOID) pNextEntry; _ASSERT(*ppvContext != (PVOID) this); //so our sentinal value works
if (NULL == *ppvContext) { *ppvContext = (PVOID) this; } } }
Exit:
return pvData; }
//+----------------------------------------------------------------------------
//
// Function: pNextTableEntry
//
// Synopsis: Given a pointer to a Prefix Table Entry, this function will
// return a pointer to the "next" prefix table entry.
//
// The "next" entry is chosen as follows:
// If the start entry has a valid child, the child is
// is returned.
// else if the start entry has a valid sibling, the sibling
// is returned
// else the first valid sibling of the closest ancestor is
// returned.
//
// Arguments: [pEntry] -- The entry to start from.
// [pRootEntry] -- Root node of subtree being enumerated
// (NULL or address of root entry will do all)
//
// Returns: Pointer to the next DOMAIN_NAME_TABLE_ENTRY that has a valid
// pData, or NULL if there are no more entries.
//
// Note: You must have a read lock over a sequence of calls into this
// function (you cannot release it between calls).
// History;
// 06/09/98 - Mikeswa modified to accept RootEntry
//
//-----------------------------------------------------------------------------
PDOMAIN_NAME_TABLE_ENTRY DOMAIN_NAME_TABLE::pNextTableEntry(IN PDOMAIN_NAME_TABLE_ENTRY pEntry, IN PDOMAIN_NAME_TABLE_ENTRY pRootEntry) { PDOMAIN_NAME_TABLE_ENTRY pNextEntry = NULL; _ASSERT(pEntry);
if (pEntry->pFirstChildEntry != NULL) { pNextEntry = pEntry->pFirstChildEntry; } else if ((pEntry->pSiblingEntry != NULL) && //if there is a sibling entry
(pEntry != pRootEntry)) //this is not the root entry
{ //Should have same parent
_ASSERT(pEntry->pParentEntry == pEntry->pSiblingEntry->pParentEntry); pNextEntry = pEntry->pSiblingEntry; } else { for (pNextEntry = pEntry->pParentEntry; pNextEntry != NULL && pNextEntry->pSiblingEntry == NULL && pNextEntry != pRootEntry; pNextEntry = pNextEntry->pParentEntry) { //NOTHING;
}
if (pNextEntry == pRootEntry) { pNextEntry = NULL; } else if (pNextEntry != NULL) { pNextEntry = pNextEntry->pSiblingEntry; }
} return pNextEntry; } //---[ DOMAIN_NAME_TABLE::DumpTableContents ]----------------------------------
//
//
// Description:
// Print out contents of table. Intended primarily for leak detection
// during table destructor
// Parameters:
// -
// Returns:
// -
//
//-----------------------------------------------------------------------------
void DOMAIN_NAME_TABLE::DumpTableContents() { PDOMAIN_NAME_TABLE_ENTRY pEntry = NULL; DOMAIN_STRING Path; CHAR Buffer[MAX_PATH_SEGMENT_SIZE+1]; DWORD cLeaks = 0;
Path.Length = 0; Path.MaximumLength = MAX_PATH_SEGMENT_SIZE; Path.Buffer = Buffer;
//Check for leaked entries
pEntry = pNextTableEntry(&RootEntry); if (pEntry) { fprintf(stderr, "\nFOUND LEAKED ENTRIES!!\n\n"); fprintf(stderr, "Entry ID # Children pData pWildCard Path\n"); fprintf(stderr, "===========================================================================\n"); while(pEntry) { _ASSERT(pEntry); GET_DOMAIN_NAME_TABLE_ENTRY_PATH(pEntry, &Path); fprintf(stderr, "0x%p %10.10d 0x%p 0x%p %s\n", pEntry, pEntry->NoOfChildren, pEntry->pData, pEntry->pWildCardData, Path.Buffer); cLeaks++; pEntry = pNextTableEntry(pEntry); } fprintf(stderr, "===========================================================================\n"); fprintf(stderr, "Total Leaks: %d\n", cLeaks); } }
//---[ DOMAIN_NAME_TABLE::RemoveTableEntry ]------------------------------------
//
//
// Description:
// Removes an entry from the table
// Parameters:
// IN pentry - Entry to remove
// Returns:
// -
// History:
// 6/8/98 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void DOMAIN_NAME_TABLE::RemoveTableEntry(IN PDOMAIN_NAME_TABLE_ENTRY pEntry) { PDOMAIN_NAME_TABLE_ENTRY pTempEntry = NULL; while (pEntry != NULL) { pTempEntry = pEntry; pEntry = pEntry->pParentEntry; if (pEntry && (--pTempEntry->NoOfChildren) == 0) { _ASSERT(!pTempEntry->pData && !pTempEntry->pWildCardData); //
// pEntry == NULL means pTempEntry is pTable->RootEntry.
// Do not try to remove it. (we also do not maintain a child count
// on it).
//
REMOVE_FROM_CHILD_LIST(pTempEntry); REMOVE_FROM_BUCKET(pTempEntry); FREE_DOMAIN_NAME_TABLE_ENTRY(pTempEntry); } } }
|