/*++ Copyright (c) 1998 Microsoft Corporation Module Name: utilities.cpp Abstract: SIS Groveler utility functions Authors: Cedric Krumbein, 1998 Environment: User Mode Revision History: --*/ #include "all.hxx" /*****************************************************************************/ // GetPerformanceTime() converts the time interval // measured using QueryPerformanceCounter() into milliseconds. PerfTime GetPerformanceTime() { LARGE_INTEGER count; QueryPerformanceCounter(&count); return (PerfTime)count.QuadPart; } /*****************************************************************************/ // PerformanceTimeToMSec() converts the time interval measured // using QueryPerformanceCounter() into milliseconds. // PerformanceTimeToUSec() converts it into microseconds. static DOUBLE frequency = 0.0; DWORD PerformanceTimeToMSec(PerfTime timeInterval) { if (frequency == 0.0) { LARGE_INTEGER intFreq; QueryPerformanceFrequency(&intFreq); frequency = (DOUBLE)intFreq.QuadPart; } return (DWORD)((DOUBLE)timeInterval * 1000.0 / frequency); } LONGLONG PerformanceTimeToUSec(PerfTime timeInterval) { if (frequency == 0.0) { LARGE_INTEGER intFreq; QueryPerformanceFrequency(&intFreq); frequency = (DOUBLE)intFreq.QuadPart; } return (LONGLONG)((DOUBLE)timeInterval * 1000000.0 / frequency); } /*****************************************************************************/ // GetTime() returns the current file time. DWORDLONG GetTime() { SYSTEMTIME systemTime; FILETIME fileTime; ULARGE_INTEGER time; BOOL success; GetSystemTime(&systemTime); success = SystemTimeToFileTime(&systemTime, &fileTime); ASSERT_ERROR(success); time.HighPart = fileTime.dwHighDateTime; time.LowPart = fileTime.dwLowDateTime; return time.QuadPart; } /*****************************************************************************/ // PrintTime() converts the supplied file time into a printable string. TCHAR *PrintTime( TCHAR *string, DWORDLONG time) { FILETIME fileTime; SYSTEMTIME systemTime; DWORD strLen; BOOL success; fileTime.dwHighDateTime = ((ULARGE_INTEGER *)&time)->HighPart; fileTime.dwLowDateTime = ((ULARGE_INTEGER *)&time)->LowPart; success = FileTimeToSystemTime(&fileTime, &systemTime); ASSERT_ERROR(success); strLen = _stprintf(string, _T("%02hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu"), systemTime.wYear % 100, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds); ASSERT(strLen == 21); return string; } /*****************************************************************************/ // GetParentName() extracts the parent directory // name out of a full-path file name. BOOL GetParentName( const TCHAR *fileName, TFileName *parentName) { DWORD hi, lo; ASSERT(fileName != NULL); ASSERT(parentName != NULL); if (fileName[0] == _T('\\')) lo = 1; else if (_istalpha(fileName[0]) && fileName[1] == _T(':') && fileName[2] == _T('\\')) lo = 3; else return FALSE; hi = _tcslen(fileName) - 1; if (hi < lo) hi = lo; else for (; hi > lo; hi--) if (fileName[hi] == _T('\\')) break; parentName->assign(fileName, hi); return TRUE; } /*****************************************************************************/ // GetFileID gets the file's ID given its name. DWORDLONG GetFileID(const TCHAR *fileName) { HANDLE fileHandle; BY_HANDLE_FILE_INFORMATION fileInfo; ULARGE_INTEGER fileID; BOOL success; ASSERT(fileName != NULL && fileName[0] != _T('\0')); fileHandle = CreateFile( fileName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (fileHandle == INVALID_HANDLE_VALUE) return 0; if (GetFileInformationByHandle(fileHandle, &fileInfo)) { fileID.HighPart = fileInfo.nFileIndexHigh; fileID.LowPart = fileInfo.nFileIndexLow; } else fileID.QuadPart = 0; success = CloseHandle(fileHandle); ASSERT_ERROR(success); return fileID.QuadPart; } /*****************************************************************************/ // GetFileName gets the file's name given either // an open handle to the file or the file's ID. BOOL GetFileName( HANDLE fileHandle, TFileName *tFileName) #ifdef _UNICODE { IO_STATUS_BLOCK ioStatusBlock; NTSTATUS ntStatus; for (int i = 2; i > 0; --i) { if (tFileName->nameLenMax < 8) // sanity check tFileName->resize(); ntStatus = NtQueryInformationFile( fileHandle, &ioStatusBlock, tFileName->nameInfo, tFileName->nameInfoSize, FileNameInformation); if (ntStatus != STATUS_BUFFER_OVERFLOW) break; ASSERT(tFileName->nameInfo->FileNameLength > tFileName->nameInfoSize - sizeof(ULONG)); tFileName->resize(tFileName->nameInfo->FileNameLength / sizeof(WCHAR) + 1); } if (ntStatus != STATUS_SUCCESS) return FALSE; tFileName->nameLen = tFileName->nameInfo->FileNameLength / sizeof(WCHAR); tFileName->name[tFileName->nameLen] = _T('\0'); return TRUE; } #else { IO_STATUS_BLOCK ioStatusBlock; NTSTATUS ntStatus; TFileName tempName; ULONG nameLen; for (int i = 2; i > 0; --i) { ntStatus = NtQueryInformationFile( fileHandle, &ioStatusBlock, tempName.nameInfo, tempName.nameInfoSize - sizeof(WCHAR), FileNameInformation); if (ntStatus != STATUS_BUFFER_OVERFLOW) break; ASSERT(tempName.nameInfo->FileNameLength > tempName.nameInfoSize - sizeof(ULONG)); nameLen = tempName.nameInfo->FileNameLength / sizeof(WCHAR); tempName.resize((tempName.nameInfo->FileNameLength + sizeof(WCHAR)) / sizeof(TCHAR)); } if (ntStatus != STATUS_SUCCESS) return FALSE; tempName.nameInfo->FileName[nameLen] = UNICODE_NULL; if (tFileName->nameLenMax < nameLen + 1) tFileName->resize(nameLen + 1); sprintf(tFileName->name, "%S", tempName.name); tFileName->nameLen = nameLen; return TRUE; } #endif BOOL GetFileName( HANDLE volumeHandle, DWORDLONG fileID, TFileName *tFileName) { UNICODE_STRING fileIDString; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; HANDLE fileHandle; NTSTATUS ntStatus; BOOL success; fileIDString.Length = sizeof(DWORDLONG); fileIDString.MaximumLength = sizeof(DWORDLONG); fileIDString.Buffer = (WCHAR *)&fileID; objectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); objectAttributes.RootDirectory = volumeHandle; objectAttributes.ObjectName = &fileIDString; objectAttributes.Attributes = OBJ_CASE_INSENSITIVE; objectAttributes.SecurityDescriptor = NULL; objectAttributes.SecurityQualityOfService = NULL; ntStatus = NtCreateFile( &fileHandle, GENERIC_READ, &objectAttributes, &ioStatusBlock, NULL, 0, FILE_SHARE_VALID_FLAGS, FILE_OPEN, FILE_OPEN_BY_FILE_ID | FILE_OPEN_REPARSE_POINT | FILE_NO_INTERMEDIATE_BUFFERING, NULL, 0); if (ntStatus != STATUS_SUCCESS) return FALSE; success = GetFileName(fileHandle, tFileName); NtClose(fileHandle); return success; } /*****************************************************************************/ // GetCSIndex() returns the SIS reparse point's common store // index. The file handle must point to an open reparse point. BOOL GetCSIndex( HANDLE fileHandle, CSID *csIndex) { IO_STATUS_BLOCK ioStatusBlock; BYTE buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; REPARSE_DATA_BUFFER *reparseBuffer; SI_REPARSE_BUFFER *sisReparseBuffer; ASSERT(fileHandle != NULL); ASSERT(csIndex != NULL); if (NtFsControlFile( fileHandle, NULL, NULL, NULL, &ioStatusBlock, FSCTL_GET_REPARSE_POINT, NULL, 0, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE) != STATUS_SUCCESS) { memset(csIndex, 0, sizeof(CSID)); return FALSE; } reparseBuffer = (REPARSE_DATA_BUFFER *)buffer; if (reparseBuffer->ReparseTag != IO_REPARSE_TAG_SIS) { memset(csIndex, 0, sizeof(CSID)); return FALSE; } sisReparseBuffer = (SI_REPARSE_BUFFER *) reparseBuffer->GenericReparseBuffer.DataBuffer; if (sisReparseBuffer->ReparsePointFormatVersion != SIS_REPARSE_BUFFER_FORMAT_VERSION) { memset(csIndex, 0, sizeof(CSID)); return FALSE; } *csIndex = sisReparseBuffer->CSid; return TRUE; } /*****************************************************************************/ // GetCSName() converts the common store // index into a dynamically allocated string. TCHAR *GetCSName(CSID *csIndex) { TCHAR *rpcStr; RPC_STATUS rpcStatus; ASSERT(csIndex != NULL); rpcStatus = UuidToString(csIndex, (unsigned short **)&rpcStr); if (rpcStatus != RPC_S_OK) { ASSERT(rpcStr == NULL); return NULL; } ASSERT(rpcStr != NULL); return rpcStr; } /*****************************************************************************/ // FreeCSName frees the string allocated by GetCSName(). VOID FreeCSName(TCHAR *rpcStr) { RPC_STATUS rpcStatus; ASSERT(rpcStr != NULL); rpcStatus = RpcStringFree((unsigned short **)&rpcStr); ASSERT(rpcStatus == RPC_S_OK); } /*****************************************************************************/ // Checksum() generates a checksum on the data supplied in the buffer. // The checksum function used is selected at compile-time; currently // the 131-hash and the "Bill 32" hash functions are implemented. #define HASH131 // #define BILL32HASH Signature Checksum( const VOID *buffer, DWORD bufferLen, DWORDLONG offset, Signature firstWord) { Signature *bufferPtr, word, signature; DWORD numWords, numBytes, rotate; ASSERT(buffer != NULL); bufferPtr = (Signature *)buffer; numWords = bufferLen / sizeof(Signature); numBytes = bufferLen % sizeof(Signature); signature = firstWord; #ifdef BILL32HASH rotate = (DWORD)(offset / sizeof(Signature) % (sizeof(Signature)*8-1)); #endif while (numWords-- > 0) { word = *bufferPtr++; #ifdef HASH131 signature = signature * 131 + word; #endif #ifdef BILL32HASH signature ^= ROTATE_RIGHT(word, rotate); rotate = (rotate+1) % (sizeof(Signature)*8-1); #endif } if (numBytes > 0) { word = 0; memcpy(&word, bufferPtr, numBytes); #ifdef HASH131 signature = signature * 131 + word; #endif #ifdef BILL32HASH signature ^= ROTATE_RIGHT(word, rotate); #endif } return signature; } /*****************************************************************************/ /************************ Table class private methods ************************/ /*****************************************************************************/ DWORD Table::Hash( const VOID *key, DWORD keyLen) const { USHORT *keyPtr; DWORD hashValue; if (keyLen == 0) return 0; ASSERT(key != NULL); if (keyLen <= sizeof(DWORD)) { hashValue = 0; memcpy(&hashValue, key, keyLen); return hashValue; } keyPtr = (USHORT *)key; hashValue = 0; while (keyLen >= sizeof(USHORT)) { hashValue = hashValue*37 + (DWORD)*keyPtr++; keyLen -= sizeof(USHORT); } if (keyLen > 0) hashValue = hashValue*37 + (DWORD)*(BYTE *)keyPtr; hashValue *= TABLE_RANDOM_CONSTANT; if ((LONG)hashValue < 0) hashValue = (DWORD)-(LONG)hashValue; hashValue %= TABLE_RANDOM_PRIME; return hashValue; } /*****************************************************************************/ DWORD Table::BucketNum(DWORD hashValue) const { DWORD bucketNum; ASSERT(expandIndex < 1U << level); ASSERT(numBuckets == (1U << level) + expandIndex); bucketNum = hashValue & ~(~0U << level); if (bucketNum < expandIndex) bucketNum = hashValue & ~(~0U << (level+1)); ASSERT(bucketNum < numBuckets); return bucketNum; } /*****************************************************************************/ VOID Table::Expand() { TableEntry **oldSlotAddr, **newSlotAddr, *oldChain, *newChain, *entry; TableSegment **newDirectory, *newSegment; DWORD oldNewMask; #if DBG TableEntry *prevChain; DWORD mask; #endif // Increase the directory size if necessary. ASSERT(directory != NULL); ASSERT(dirSize >= TABLE_SEGMENT_SIZE); ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0); if (numBuckets >= dirSize * TABLE_SEGMENT_SIZE) { newDirectory = new TableSegment * [dirSize + TABLE_DIR_SIZE]; ASSERT(newDirectory != NULL); memcpy(newDirectory, directory, sizeof(TableSegment *) * dirSize); memset(newDirectory+dirSize, 0, sizeof(TableSegment *) * TABLE_DIR_SIZE); dirSize += TABLE_DIR_SIZE; delete directory; directory = newDirectory; } // Find the old bucket to be expanded. ASSERT(expandIndex >> TABLE_SEGMENT_BITS < dirSize); oldSlotAddr = &directory[expandIndex >> TABLE_SEGMENT_BITS] ->slot[expandIndex & TABLE_SEGMENT_MASK]; ASSERT(oldSlotAddr != NULL); // Find the new bucket, and create a new segment if necessary. ASSERT(numBuckets >> TABLE_SEGMENT_BITS < dirSize); newSegment = directory[numBuckets >> TABLE_SEGMENT_BITS]; if (newSegment == NULL) { newSegment = new TableSegment; ASSERT(newSegment != NULL); memset(newSegment, 0, sizeof(TableSegment)); directory[numBuckets >> TABLE_SEGMENT_BITS] = newSegment; } newSlotAddr = &newSegment->slot[numBuckets & TABLE_SEGMENT_MASK]; ASSERT(*newSlotAddr == NULL); // Relocate entries from the old to the new bucket. oldNewMask = 1U << level; oldChain = NULL; newChain = NULL; entry = *oldSlotAddr; #if DBG prevChain = NULL; mask = ~(~0U << (level+1)); #endif while (entry != NULL) { ASSERT((entry->hashValue & ~(~0U << level)) == expandIndex); ASSERT( entry->prevChain == prevChain); // This entry moves to the new bucket. if ((entry->hashValue & oldNewMask) != 0) { if (newChain == NULL) { *newSlotAddr = entry; entry->prevChain = NULL; } else { newChain->nextChain = entry; entry ->prevChain = newChain; } newChain = entry; ASSERT((entry->hashValue & mask) == numBuckets); } // This entry stays in the old bucket. else { if (oldChain == NULL) { *oldSlotAddr = entry; entry->prevChain = NULL; } else { oldChain->nextChain = entry; entry ->prevChain = oldChain; } oldChain = entry; ASSERT((entry->hashValue & mask) == expandIndex); } #if DBG prevChain = entry; #endif entry = entry->nextChain; } // Finish off each bucket chain. if (oldChain == NULL) *oldSlotAddr = NULL; else oldChain->nextChain = NULL; if (newChain == NULL) *newSlotAddr = NULL; else newChain->nextChain = NULL; // Adjust the expand index and level, and increment the number of buckets. if (++expandIndex == 1U << level) { level++; expandIndex = 0; } numBuckets++; ASSERT(expandIndex < 1U << level); ASSERT(numBuckets == (1U << level) + expandIndex); } /*****************************************************************************/ VOID Table::Contract() { TableEntry **targetSlotAddr, **victimSlotAddr, *firstVictimEntry, *prevChain, *entry; TableSegment **newDirectory; #if DBG DWORD mask; #endif // Adjust the expand index and level, and decrement the number of buckets. ASSERT(expandIndex < 1U << level); ASSERT(numBuckets == (1U << level) + expandIndex); if (expandIndex > 0) expandIndex--; else expandIndex = (1U << --level) - 1; numBuckets--; ASSERT(expandIndex < 1U << level); ASSERT(numBuckets == (1U << level) + expandIndex); // Find the target and victim buckets. ASSERT(directory != NULL); ASSERT(dirSize >= TABLE_SEGMENT_SIZE); ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0); targetSlotAddr = &directory[expandIndex >> TABLE_SEGMENT_BITS] ->slot[expandIndex & TABLE_SEGMENT_MASK]; victimSlotAddr = &directory[numBuckets >> TABLE_SEGMENT_BITS] ->slot[numBuckets & TABLE_SEGMENT_MASK]; ASSERT(targetSlotAddr != NULL); ASSERT(victimSlotAddr != NULL); // If the victim buffer isn't empty, ... if ((firstVictimEntry = *victimSlotAddr) != NULL) { #if DBG mask = ~(~0U << (level+1)); #endif ASSERT((firstVictimEntry->hashValue & mask) == numBuckets); ASSERT( firstVictimEntry->prevChain == NULL); // ... find the end of the target bucket chain, ... entry = *targetSlotAddr; prevChain = NULL; while (entry != NULL) { ASSERT((entry->hashValue & mask) == expandIndex); ASSERT( entry->prevChain == prevChain); prevChain = entry; entry = entry->nextChain; } // ... then add the victim bucket chain to the end of the target bucket chain. if (prevChain == NULL) *targetSlotAddr = firstVictimEntry; else { prevChain->nextChain = firstVictimEntry; firstVictimEntry->prevChain = prevChain; } } // Delete the victim bucket, and delete the victim segment if no buckets remain. if ((numBuckets & TABLE_SEGMENT_MASK) == 0) { delete directory[numBuckets >> TABLE_SEGMENT_BITS]; directory[numBuckets >> TABLE_SEGMENT_BITS] = NULL; } else *victimSlotAddr = NULL; // Reduce the size of the directory if necessary. if (numBuckets <= (dirSize - TABLE_DIR_SIZE) * TABLE_SEGMENT_SIZE && dirSize > TABLE_DIR_SIZE) { dirSize -= TABLE_DIR_SIZE; newDirectory = new TableSegment * [dirSize]; ASSERT(newDirectory != NULL); memcpy(newDirectory, directory, sizeof(TableSegment *) * dirSize); delete directory; directory = newDirectory; } } /*****************************************************************************/ /************************ Table class public methods *************************/ /*****************************************************************************/ Table::Table() { firstEntry = NULL; lastEntry = NULL; numEntries = 0; numBuckets = TABLE_SEGMENT_SIZE; expandIndex = 0; level = TABLE_SEGMENT_BITS; dirSize = TABLE_DIR_SIZE; directory = new TableSegment * [dirSize]; ASSERT(directory != NULL); memset(directory, 0, sizeof(TableSegment *) * dirSize); directory[0] = new TableSegment; ASSERT(directory[0] != NULL); memset(directory[0], 0, sizeof(TableSegment)); } /*****************************************************************************/ Table::~Table() { TableEntry *entry, *prevEntry; DWORD numSegments, segmentNum, count; entry = firstEntry; prevEntry = NULL; count = 0; while (entry != NULL) { ASSERT(entry->prevEntry == prevEntry); prevEntry = entry; entry = entry->nextEntry; delete prevEntry->data; delete prevEntry; count++; } ASSERT(count == numEntries); numSegments = numBuckets >> TABLE_SEGMENT_BITS; ASSERT(directory != NULL); ASSERT(dirSize >= TABLE_SEGMENT_SIZE); ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0); ASSERT(numSegments <= dirSize); for (segmentNum = 0; segmentNum < numSegments; segmentNum++) { ASSERT(directory[segmentNum] != NULL); delete directory[segmentNum]; } delete directory; } /*****************************************************************************/ BOOL Table::Put( VOID *data, DWORD keyLen) { TableEntry **slotAddr, *prevChain, *entry; DWORD hashValue, bucketNum; #if DBG DWORD mask; #endif ASSERT(data != NULL); ASSERT(keyLen > 0); // Find the bucket for this data. hashValue = Hash(data, keyLen); bucketNum = BucketNum(hashValue); #if DBG mask = ~(~0U << (bucketNum < expandIndex || bucketNum >= 1U << level ? level+1 : level)); #endif ASSERT(directory != NULL); slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS] ->slot[bucketNum & TABLE_SEGMENT_MASK]; ASSERT(slotAddr != NULL); entry = *slotAddr; prevChain = NULL; // Look at each entry in the bucket to determine if the data is // already present. If a matching entry is found, return FALSE. while (entry != NULL) { ASSERT((entry->hashValue & mask) == bucketNum); ASSERT( entry->prevChain == prevChain); if (hashValue == entry->hashValue && keyLen == entry->keyLen && memcmp(data, entry->data, keyLen) == 0) return FALSE; prevChain = entry; entry = entry->nextChain; } // No entry with matching data was found in this bucket. // Create a new entry and add it to the end of the bucket chain. entry = new TableEntry; ASSERT(entry != NULL); if (prevChain == NULL) { *slotAddr = entry; entry->prevChain = NULL; } else { prevChain->nextChain = entry; entry ->prevChain = prevChain; } entry->nextChain = NULL; // Add the entry to the end of the doubly-linked list. if (lastEntry == NULL) { ASSERT(firstEntry == NULL); ASSERT(numEntries == 0); firstEntry = entry; entry->prevEntry = NULL; } else { ASSERT(firstEntry != NULL); ASSERT(numEntries > 0); lastEntry->nextEntry = entry; entry ->prevEntry = lastEntry; } entry->nextEntry = NULL; lastEntry = entry; numEntries++; // Fill out the entry. entry->hashValue = hashValue; entry->keyLen = keyLen; entry->data = data; // Expand the table if necessary. if (numEntries > numBuckets * TABLE_MAX_LOAD) { Expand(); ASSERT(numEntries <= numBuckets * TABLE_MAX_LOAD); } return TRUE; } /*****************************************************************************/ VOID *Table::Get( const VOID *key, DWORD keyLen, BOOL erase) { TableEntry **slotAddr, *entry, *prevChain; DWORD hashValue, bucketNum; VOID *dataPtr; #if DBG DWORD mask; #endif ASSERT(key != NULL); ASSERT(keyLen > 0); // Find the bucket for this data. hashValue = Hash(key, keyLen); bucketNum = BucketNum(hashValue); #if DBG mask = ~(~0U << (bucketNum < expandIndex || bucketNum >= 1U << level ? level+1 : level)); #endif ASSERT(directory != NULL); slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS] ->slot[bucketNum & TABLE_SEGMENT_MASK]; ASSERT(slotAddr != NULL); entry = *slotAddr; prevChain = NULL; // Look at each entry in the bucket. while (entry != NULL) { ASSERT((entry->hashValue & mask) == bucketNum); ASSERT( entry->prevChain == prevChain); if (hashValue == entry->hashValue && keyLen == entry->keyLen && memcmp(key, entry->data, keyLen) == 0) { // The entry with matching data has been found. dataPtr = entry->data; ASSERT(dataPtr != NULL); // If erasure is disabled, remove the entry from the doubly-linked list ... if (erase) { if (entry->prevEntry == NULL) { ASSERT(firstEntry == entry); firstEntry = entry->nextEntry; } else entry->prevEntry->nextEntry = entry->nextEntry; if (entry->nextEntry == NULL) { ASSERT(lastEntry == entry); lastEntry = entry->prevEntry; } else entry->nextEntry->prevEntry = entry->prevEntry; // ... and from the bucket chain, ... if (prevChain == NULL) *slotAddr = entry->nextChain; else prevChain->nextChain = entry->nextChain; if (entry->nextChain != NULL) { ASSERT(entry->nextChain->prevChain == entry); entry->nextChain->prevChain = prevChain; } // ... then delete the entry. delete entry; // Decrement the number of entries, and contract the table if necessary. numEntries--; if (numBuckets > TABLE_SEGMENT_SIZE && numEntries < numBuckets * TABLE_MIN_LOAD) { Contract(); ASSERT(numBuckets <= TABLE_SEGMENT_SIZE || numEntries >= numBuckets * TABLE_MIN_LOAD); } } return dataPtr; } // No entry with matching data has yet been found. // Continue following the bucket chain. prevChain = entry; entry = entry->nextChain; } // No entry with matching data was found in this bucket. return NULL; } /*****************************************************************************/ VOID *Table::GetFirst( DWORD *keyLen, BOOL erase) { TableEntry **slotAddr, *entry; DWORD bucketNum; VOID *dataPtr; // If the table is empty, then simply return. if (firstEntry == NULL) { ASSERT(lastEntry == NULL); ASSERT(numEntries == 0); return NULL; } dataPtr = firstEntry->data; ASSERT(dataPtr != NULL); if (keyLen != NULL) { *keyLen = firstEntry->keyLen; ASSERT(firstEntry->keyLen > 0); } // If erasure is enabled, remove the first entry from the doubly-linked list ... if (erase) { entry = firstEntry; firstEntry = entry->nextEntry; if (firstEntry == NULL) { ASSERT(numEntries == 1); ASSERT(lastEntry == entry); lastEntry = NULL; } else { ASSERT(numEntries > 1); ASSERT(firstEntry->prevEntry == entry); firstEntry->prevEntry = NULL; } // ... and from the bucket chain, ... if (entry->prevChain == NULL) { bucketNum = BucketNum(entry->hashValue); ASSERT(directory != NULL); slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS] ->slot[bucketNum & TABLE_SEGMENT_MASK]; ASSERT( slotAddr != NULL); ASSERT(*slotAddr == entry); *slotAddr = entry->nextChain; } else { ASSERT(entry->prevChain->nextChain == entry); entry->prevChain->nextChain = entry->nextChain; } if (entry->nextChain != NULL) { ASSERT(entry->nextChain->prevChain == entry); entry->nextChain->prevChain = entry->prevChain; } // ... then delete the entry. delete entry; // Decrement the number of entries, and contract the table if necessary. numEntries--; if (numBuckets > TABLE_SEGMENT_SIZE && numEntries < numBuckets * TABLE_MIN_LOAD) { Contract(); ASSERT(numBuckets <= TABLE_SEGMENT_SIZE || numEntries >= numBuckets * TABLE_MIN_LOAD); } } return dataPtr; } /*****************************************************************************/ DWORD Table::Number() const { return numEntries; } /*****************************************************************************/ /************************* FIFO class public methods *************************/ /*****************************************************************************/ FIFO::FIFO() { head = tail = NULL; numEntries = 0; } /*****************************************************************************/ FIFO::~FIFO() { FIFOEntry *entry = head, *oldEntry; DWORD count = 0; while ((oldEntry = entry) != NULL) { entry = entry->next; delete oldEntry->data; delete oldEntry; count++; } ASSERT(count == numEntries); } /*****************************************************************************/ VOID FIFO::Put(VOID *data) { FIFOEntry *newEntry; ASSERT(data != NULL); newEntry = new FIFOEntry; ASSERT(newEntry != NULL); newEntry->next = NULL; newEntry->data = data; if (tail != NULL) tail->next = newEntry; else head = newEntry; tail = newEntry; numEntries++; } /*****************************************************************************/ VOID *FIFO::Get() { FIFOEntry *oldHead; VOID *dataPtr; if (head == NULL) { ASSERT(tail == NULL); ASSERT(numEntries == 0); return NULL; } ASSERT(tail != NULL); ASSERT(numEntries > 0); dataPtr = head->data; oldHead = head; head = head->next; delete oldHead; if (head == NULL) tail = NULL; numEntries--; return dataPtr; } /*****************************************************************************/ DWORD FIFO::Number() const { return numEntries; } /*****************************************************************************/ /************************* LIFO class public methods *************************/ /*****************************************************************************/ LIFO::LIFO() { top = NULL; numEntries = 0; } /*****************************************************************************/ LIFO::~LIFO() { LIFOEntry *entry = top, *oldEntry; DWORD count = 0; while ((oldEntry = entry) != NULL) { entry = entry->next; delete oldEntry->data; delete oldEntry; count++; } ASSERT(count == numEntries); } /*****************************************************************************/ VOID LIFO::Put(VOID *data) { LIFOEntry *newEntry; ASSERT(data != NULL); newEntry = new LIFOEntry; ASSERT(newEntry != NULL); newEntry->next = top; newEntry->data = data; top = newEntry; numEntries++; } /*****************************************************************************/ VOID *LIFO::Get() { LIFOEntry *oldTop; VOID *dataPtr; if (top == NULL) { ASSERT(numEntries == 0); return NULL; } ASSERT(numEntries > 0); dataPtr = top->data; oldTop = top; top = top->next; delete oldTop; numEntries--; return dataPtr; } /*****************************************************************************/ DWORD LIFO::Number() const { return numEntries; }