Source code of Windows XP (NT5)
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.

1317 lines
31 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. utilities.cpp
  5. Abstract:
  6. SIS Groveler utility functions
  7. Authors:
  8. Cedric Krumbein, 1998
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #include "all.hxx"
  14. /*****************************************************************************/
  15. // GetPerformanceTime() converts the time interval
  16. // measured using QueryPerformanceCounter() into milliseconds.
  17. PerfTime GetPerformanceTime()
  18. {
  19. LARGE_INTEGER count;
  20. QueryPerformanceCounter(&count);
  21. return (PerfTime)count.QuadPart;
  22. }
  23. /*****************************************************************************/
  24. // PerformanceTimeToMSec() converts the time interval measured
  25. // using QueryPerformanceCounter() into milliseconds.
  26. // PerformanceTimeToUSec() converts it into microseconds.
  27. static DOUBLE frequency = 0.0;
  28. DWORD PerformanceTimeToMSec(PerfTime timeInterval)
  29. {
  30. if (frequency == 0.0) {
  31. LARGE_INTEGER intFreq;
  32. QueryPerformanceFrequency(&intFreq);
  33. frequency = (DOUBLE)intFreq.QuadPart;
  34. }
  35. return (DWORD)((DOUBLE)timeInterval * 1000.0 / frequency);
  36. }
  37. LONGLONG PerformanceTimeToUSec(PerfTime timeInterval)
  38. {
  39. if (frequency == 0.0) {
  40. LARGE_INTEGER intFreq;
  41. QueryPerformanceFrequency(&intFreq);
  42. frequency = (DOUBLE)intFreq.QuadPart;
  43. }
  44. return (LONGLONG)((DOUBLE)timeInterval * 1000000.0 / frequency);
  45. }
  46. /*****************************************************************************/
  47. // GetTime() returns the current file time.
  48. DWORDLONG GetTime()
  49. {
  50. SYSTEMTIME systemTime;
  51. FILETIME fileTime;
  52. ULARGE_INTEGER time;
  53. BOOL success;
  54. GetSystemTime(&systemTime);
  55. success = SystemTimeToFileTime(&systemTime, &fileTime);
  56. ASSERT_ERROR(success);
  57. time.HighPart = fileTime.dwHighDateTime;
  58. time.LowPart = fileTime.dwLowDateTime;
  59. return time.QuadPart;
  60. }
  61. /*****************************************************************************/
  62. // PrintTime() converts the supplied file time into a printable string.
  63. TCHAR *PrintTime(
  64. TCHAR *string,
  65. DWORDLONG time)
  66. {
  67. FILETIME fileTime;
  68. SYSTEMTIME systemTime;
  69. DWORD strLen;
  70. BOOL success;
  71. fileTime.dwHighDateTime = ((ULARGE_INTEGER *)&time)->HighPart;
  72. fileTime.dwLowDateTime = ((ULARGE_INTEGER *)&time)->LowPart;
  73. success = FileTimeToSystemTime(&fileTime, &systemTime);
  74. ASSERT_ERROR(success);
  75. strLen = _stprintf(string, _T("%02hu/%02hu/%02hu %02hu:%02hu:%02hu.%03hu"),
  76. systemTime.wYear % 100,
  77. systemTime.wMonth,
  78. systemTime.wDay,
  79. systemTime.wHour,
  80. systemTime.wMinute,
  81. systemTime.wSecond,
  82. systemTime.wMilliseconds);
  83. ASSERT(strLen == 21);
  84. return string;
  85. }
  86. /*****************************************************************************/
  87. // GetParentName() extracts the parent directory
  88. // name out of a full-path file name.
  89. BOOL GetParentName(
  90. const TCHAR *fileName,
  91. TFileName *parentName)
  92. {
  93. DWORD hi, lo;
  94. ASSERT(fileName != NULL);
  95. ASSERT(parentName != NULL);
  96. if (fileName[0] == _T('\\'))
  97. lo = 1;
  98. else if (_istalpha(fileName[0])
  99. && fileName[1] == _T(':')
  100. && fileName[2] == _T('\\'))
  101. lo = 3;
  102. else
  103. return FALSE;
  104. hi = _tcslen(fileName) - 1;
  105. if (hi < lo)
  106. hi = lo;
  107. else
  108. for (; hi > lo; hi--)
  109. if (fileName[hi] == _T('\\'))
  110. break;
  111. parentName->assign(fileName, hi);
  112. return TRUE;
  113. }
  114. /*****************************************************************************/
  115. // GetFileID gets the file's ID given its name.
  116. DWORDLONG GetFileID(const TCHAR *fileName)
  117. {
  118. HANDLE fileHandle;
  119. BY_HANDLE_FILE_INFORMATION fileInfo;
  120. ULARGE_INTEGER fileID;
  121. BOOL success;
  122. ASSERT(fileName != NULL && fileName[0] != _T('\0'));
  123. fileHandle = CreateFile(
  124. fileName,
  125. 0,
  126. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  127. NULL,
  128. OPEN_EXISTING,
  129. FILE_FLAG_BACKUP_SEMANTICS,
  130. NULL);
  131. if (fileHandle == INVALID_HANDLE_VALUE)
  132. return 0;
  133. if (GetFileInformationByHandle(fileHandle, &fileInfo)) {
  134. fileID.HighPart = fileInfo.nFileIndexHigh;
  135. fileID.LowPart = fileInfo.nFileIndexLow;
  136. } else
  137. fileID.QuadPart = 0;
  138. success = CloseHandle(fileHandle);
  139. ASSERT_ERROR(success);
  140. return fileID.QuadPart;
  141. }
  142. /*****************************************************************************/
  143. // GetFileName gets the file's name given either
  144. // an open handle to the file or the file's ID.
  145. BOOL GetFileName(
  146. HANDLE fileHandle,
  147. TFileName *tFileName)
  148. #ifdef _UNICODE
  149. {
  150. IO_STATUS_BLOCK ioStatusBlock;
  151. NTSTATUS ntStatus;
  152. for (int i = 2; i > 0; --i) {
  153. if (tFileName->nameLenMax < 8) // sanity check
  154. tFileName->resize();
  155. ntStatus = NtQueryInformationFile(
  156. fileHandle,
  157. &ioStatusBlock,
  158. tFileName->nameInfo,
  159. tFileName->nameInfoSize,
  160. FileNameInformation);
  161. if (ntStatus != STATUS_BUFFER_OVERFLOW)
  162. break;
  163. ASSERT(tFileName->nameInfo->FileNameLength > tFileName->nameInfoSize - sizeof(ULONG));
  164. tFileName->resize(tFileName->nameInfo->FileNameLength / sizeof(WCHAR) + 1);
  165. }
  166. if (ntStatus != STATUS_SUCCESS)
  167. return FALSE;
  168. tFileName->nameLen = tFileName->nameInfo->FileNameLength / sizeof(WCHAR);
  169. tFileName->name[tFileName->nameLen] = _T('\0');
  170. return TRUE;
  171. }
  172. #else
  173. {
  174. IO_STATUS_BLOCK ioStatusBlock;
  175. NTSTATUS ntStatus;
  176. TFileName tempName;
  177. ULONG nameLen;
  178. for (int i = 2; i > 0; --i) {
  179. ntStatus = NtQueryInformationFile(
  180. fileHandle,
  181. &ioStatusBlock,
  182. tempName.nameInfo,
  183. tempName.nameInfoSize - sizeof(WCHAR),
  184. FileNameInformation);
  185. if (ntStatus != STATUS_BUFFER_OVERFLOW)
  186. break;
  187. ASSERT(tempName.nameInfo->FileNameLength > tempName.nameInfoSize - sizeof(ULONG));
  188. nameLen = tempName.nameInfo->FileNameLength / sizeof(WCHAR);
  189. tempName.resize((tempName.nameInfo->FileNameLength + sizeof(WCHAR)) / sizeof(TCHAR));
  190. }
  191. if (ntStatus != STATUS_SUCCESS)
  192. return FALSE;
  193. tempName.nameInfo->FileName[nameLen] = UNICODE_NULL;
  194. if (tFileName->nameLenMax < nameLen + 1)
  195. tFileName->resize(nameLen + 1);
  196. sprintf(tFileName->name, "%S", tempName.name);
  197. tFileName->nameLen = nameLen;
  198. return TRUE;
  199. }
  200. #endif
  201. BOOL GetFileName(
  202. HANDLE volumeHandle,
  203. DWORDLONG fileID,
  204. TFileName *tFileName)
  205. {
  206. UNICODE_STRING fileIDString;
  207. OBJECT_ATTRIBUTES objectAttributes;
  208. IO_STATUS_BLOCK ioStatusBlock;
  209. HANDLE fileHandle;
  210. NTSTATUS ntStatus;
  211. BOOL success;
  212. fileIDString.Length = sizeof(DWORDLONG);
  213. fileIDString.MaximumLength = sizeof(DWORDLONG);
  214. fileIDString.Buffer = (WCHAR *)&fileID;
  215. objectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
  216. objectAttributes.RootDirectory = volumeHandle;
  217. objectAttributes.ObjectName = &fileIDString;
  218. objectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
  219. objectAttributes.SecurityDescriptor = NULL;
  220. objectAttributes.SecurityQualityOfService = NULL;
  221. ntStatus = NtCreateFile(
  222. &fileHandle,
  223. GENERIC_READ,
  224. &objectAttributes,
  225. &ioStatusBlock,
  226. NULL,
  227. 0,
  228. FILE_SHARE_VALID_FLAGS,
  229. FILE_OPEN,
  230. FILE_OPEN_BY_FILE_ID |
  231. FILE_OPEN_REPARSE_POINT |
  232. FILE_NO_INTERMEDIATE_BUFFERING,
  233. NULL,
  234. 0);
  235. if (ntStatus != STATUS_SUCCESS)
  236. return FALSE;
  237. success = GetFileName(fileHandle, tFileName);
  238. NtClose(fileHandle);
  239. return success;
  240. }
  241. /*****************************************************************************/
  242. // GetCSIndex() returns the SIS reparse point's common store
  243. // index. The file handle must point to an open reparse point.
  244. BOOL GetCSIndex(
  245. HANDLE fileHandle,
  246. CSID *csIndex)
  247. {
  248. IO_STATUS_BLOCK ioStatusBlock;
  249. BYTE buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  250. REPARSE_DATA_BUFFER *reparseBuffer;
  251. SI_REPARSE_BUFFER *sisReparseBuffer;
  252. ASSERT(fileHandle != NULL);
  253. ASSERT(csIndex != NULL);
  254. if (NtFsControlFile(
  255. fileHandle,
  256. NULL,
  257. NULL,
  258. NULL,
  259. &ioStatusBlock,
  260. FSCTL_GET_REPARSE_POINT,
  261. NULL,
  262. 0,
  263. buffer,
  264. MAXIMUM_REPARSE_DATA_BUFFER_SIZE) != STATUS_SUCCESS) {
  265. memset(csIndex, 0, sizeof(CSID));
  266. return FALSE;
  267. }
  268. reparseBuffer = (REPARSE_DATA_BUFFER *)buffer;
  269. if (reparseBuffer->ReparseTag != IO_REPARSE_TAG_SIS) {
  270. memset(csIndex, 0, sizeof(CSID));
  271. return FALSE;
  272. }
  273. sisReparseBuffer = (SI_REPARSE_BUFFER *)
  274. reparseBuffer->GenericReparseBuffer.DataBuffer;
  275. if (sisReparseBuffer->ReparsePointFormatVersion != SIS_REPARSE_BUFFER_FORMAT_VERSION) {
  276. memset(csIndex, 0, sizeof(CSID));
  277. return FALSE;
  278. }
  279. *csIndex = sisReparseBuffer->CSid;
  280. return TRUE;
  281. }
  282. /*****************************************************************************/
  283. // GetCSName() converts the common store
  284. // index into a dynamically allocated string.
  285. TCHAR *GetCSName(CSID *csIndex)
  286. {
  287. TCHAR *rpcStr;
  288. RPC_STATUS rpcStatus;
  289. ASSERT(csIndex != NULL);
  290. rpcStatus = UuidToString(csIndex, (unsigned short **)&rpcStr);
  291. if (rpcStatus != RPC_S_OK) {
  292. ASSERT(rpcStr == NULL);
  293. return NULL;
  294. }
  295. ASSERT(rpcStr != NULL);
  296. return rpcStr;
  297. }
  298. /*****************************************************************************/
  299. // FreeCSName frees the string allocated by GetCSName().
  300. VOID FreeCSName(TCHAR *rpcStr)
  301. {
  302. RPC_STATUS rpcStatus;
  303. ASSERT(rpcStr != NULL);
  304. rpcStatus = RpcStringFree((unsigned short **)&rpcStr);
  305. ASSERT(rpcStatus == RPC_S_OK);
  306. }
  307. /*****************************************************************************/
  308. // Checksum() generates a checksum on the data supplied in the buffer.
  309. // The checksum function used is selected at compile-time; currently
  310. // the 131-hash and the "Bill 32" hash functions are implemented.
  311. #define HASH131
  312. // #define BILL32HASH
  313. Signature Checksum(
  314. const VOID *buffer,
  315. DWORD bufferLen,
  316. DWORDLONG offset,
  317. Signature firstWord)
  318. {
  319. Signature *bufferPtr,
  320. word,
  321. signature;
  322. DWORD numWords,
  323. numBytes,
  324. rotate;
  325. ASSERT(buffer != NULL);
  326. bufferPtr = (Signature *)buffer;
  327. numWords = bufferLen / sizeof(Signature);
  328. numBytes = bufferLen % sizeof(Signature);
  329. signature = firstWord;
  330. #ifdef BILL32HASH
  331. rotate = (DWORD)(offset / sizeof(Signature) % (sizeof(Signature)*8-1));
  332. #endif
  333. while (numWords-- > 0) {
  334. word = *bufferPtr++;
  335. #ifdef HASH131
  336. signature = signature * 131 + word;
  337. #endif
  338. #ifdef BILL32HASH
  339. signature ^= ROTATE_RIGHT(word, rotate);
  340. rotate = (rotate+1) % (sizeof(Signature)*8-1);
  341. #endif
  342. }
  343. if (numBytes > 0) {
  344. word = 0;
  345. memcpy(&word, bufferPtr, numBytes);
  346. #ifdef HASH131
  347. signature = signature * 131 + word;
  348. #endif
  349. #ifdef BILL32HASH
  350. signature ^= ROTATE_RIGHT(word, rotate);
  351. #endif
  352. }
  353. return signature;
  354. }
  355. /*****************************************************************************/
  356. /************************ Table class private methods ************************/
  357. /*****************************************************************************/
  358. DWORD Table::Hash(
  359. const VOID *key,
  360. DWORD keyLen) const
  361. {
  362. USHORT *keyPtr;
  363. DWORD hashValue;
  364. if (keyLen == 0)
  365. return 0;
  366. ASSERT(key != NULL);
  367. if (keyLen <= sizeof(DWORD)) {
  368. hashValue = 0;
  369. memcpy(&hashValue, key, keyLen);
  370. return hashValue;
  371. }
  372. keyPtr = (USHORT *)key;
  373. hashValue = 0;
  374. while (keyLen >= sizeof(USHORT)) {
  375. hashValue = hashValue*37 + (DWORD)*keyPtr++;
  376. keyLen -= sizeof(USHORT);
  377. }
  378. if (keyLen > 0)
  379. hashValue = hashValue*37 + (DWORD)*(BYTE *)keyPtr;
  380. hashValue *= TABLE_RANDOM_CONSTANT;
  381. if ((LONG)hashValue < 0)
  382. hashValue = (DWORD)-(LONG)hashValue;
  383. hashValue %= TABLE_RANDOM_PRIME;
  384. return hashValue;
  385. }
  386. /*****************************************************************************/
  387. DWORD Table::BucketNum(DWORD hashValue) const
  388. {
  389. DWORD bucketNum;
  390. ASSERT(expandIndex < 1U << level);
  391. ASSERT(numBuckets == (1U << level) + expandIndex);
  392. bucketNum = hashValue & ~(~0U << level);
  393. if (bucketNum < expandIndex)
  394. bucketNum = hashValue & ~(~0U << (level+1));
  395. ASSERT(bucketNum < numBuckets);
  396. return bucketNum;
  397. }
  398. /*****************************************************************************/
  399. VOID Table::Expand()
  400. {
  401. TableEntry **oldSlotAddr,
  402. **newSlotAddr,
  403. *oldChain,
  404. *newChain,
  405. *entry;
  406. TableSegment **newDirectory,
  407. *newSegment;
  408. DWORD oldNewMask;
  409. #if DBG
  410. TableEntry *prevChain;
  411. DWORD mask;
  412. #endif
  413. // Increase the directory size if necessary.
  414. ASSERT(directory != NULL);
  415. ASSERT(dirSize >= TABLE_SEGMENT_SIZE);
  416. ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0);
  417. if (numBuckets >= dirSize * TABLE_SEGMENT_SIZE) {
  418. newDirectory = new TableSegment * [dirSize + TABLE_DIR_SIZE];
  419. ASSERT(newDirectory != NULL);
  420. memcpy(newDirectory, directory, sizeof(TableSegment *) * dirSize);
  421. memset(newDirectory+dirSize, 0, sizeof(TableSegment *) * TABLE_DIR_SIZE);
  422. dirSize += TABLE_DIR_SIZE;
  423. delete directory;
  424. directory = newDirectory;
  425. }
  426. // Find the old bucket to be expanded.
  427. ASSERT(expandIndex >> TABLE_SEGMENT_BITS < dirSize);
  428. oldSlotAddr = &directory[expandIndex >> TABLE_SEGMENT_BITS]
  429. ->slot[expandIndex & TABLE_SEGMENT_MASK];
  430. ASSERT(oldSlotAddr != NULL);
  431. // Find the new bucket, and create a new segment if necessary.
  432. ASSERT(numBuckets >> TABLE_SEGMENT_BITS < dirSize);
  433. newSegment = directory[numBuckets >> TABLE_SEGMENT_BITS];
  434. if (newSegment == NULL) {
  435. newSegment = new TableSegment;
  436. ASSERT(newSegment != NULL);
  437. memset(newSegment, 0, sizeof(TableSegment));
  438. directory[numBuckets >> TABLE_SEGMENT_BITS] = newSegment;
  439. }
  440. newSlotAddr = &newSegment->slot[numBuckets & TABLE_SEGMENT_MASK];
  441. ASSERT(*newSlotAddr == NULL);
  442. // Relocate entries from the old to the new bucket.
  443. oldNewMask = 1U << level;
  444. oldChain = NULL;
  445. newChain = NULL;
  446. entry = *oldSlotAddr;
  447. #if DBG
  448. prevChain = NULL;
  449. mask = ~(~0U << (level+1));
  450. #endif
  451. while (entry != NULL) {
  452. ASSERT((entry->hashValue & ~(~0U << level)) == expandIndex);
  453. ASSERT( entry->prevChain == prevChain);
  454. // This entry moves to the new bucket.
  455. if ((entry->hashValue & oldNewMask) != 0) {
  456. if (newChain == NULL) {
  457. *newSlotAddr = entry;
  458. entry->prevChain = NULL;
  459. } else {
  460. newChain->nextChain = entry;
  461. entry ->prevChain = newChain;
  462. }
  463. newChain = entry;
  464. ASSERT((entry->hashValue & mask) == numBuckets);
  465. }
  466. // This entry stays in the old bucket.
  467. else {
  468. if (oldChain == NULL) {
  469. *oldSlotAddr = entry;
  470. entry->prevChain = NULL;
  471. } else {
  472. oldChain->nextChain = entry;
  473. entry ->prevChain = oldChain;
  474. }
  475. oldChain = entry;
  476. ASSERT((entry->hashValue & mask) == expandIndex);
  477. }
  478. #if DBG
  479. prevChain = entry;
  480. #endif
  481. entry = entry->nextChain;
  482. }
  483. // Finish off each bucket chain.
  484. if (oldChain == NULL)
  485. *oldSlotAddr = NULL;
  486. else
  487. oldChain->nextChain = NULL;
  488. if (newChain == NULL)
  489. *newSlotAddr = NULL;
  490. else
  491. newChain->nextChain = NULL;
  492. // Adjust the expand index and level, and increment the number of buckets.
  493. if (++expandIndex == 1U << level) {
  494. level++;
  495. expandIndex = 0;
  496. }
  497. numBuckets++;
  498. ASSERT(expandIndex < 1U << level);
  499. ASSERT(numBuckets == (1U << level) + expandIndex);
  500. }
  501. /*****************************************************************************/
  502. VOID Table::Contract()
  503. {
  504. TableEntry **targetSlotAddr,
  505. **victimSlotAddr,
  506. *firstVictimEntry,
  507. *prevChain,
  508. *entry;
  509. TableSegment **newDirectory;
  510. #if DBG
  511. DWORD mask;
  512. #endif
  513. // Adjust the expand index and level, and decrement the number of buckets.
  514. ASSERT(expandIndex < 1U << level);
  515. ASSERT(numBuckets == (1U << level) + expandIndex);
  516. if (expandIndex > 0)
  517. expandIndex--;
  518. else
  519. expandIndex = (1U << --level) - 1;
  520. numBuckets--;
  521. ASSERT(expandIndex < 1U << level);
  522. ASSERT(numBuckets == (1U << level) + expandIndex);
  523. // Find the target and victim buckets.
  524. ASSERT(directory != NULL);
  525. ASSERT(dirSize >= TABLE_SEGMENT_SIZE);
  526. ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0);
  527. targetSlotAddr = &directory[expandIndex >> TABLE_SEGMENT_BITS]
  528. ->slot[expandIndex & TABLE_SEGMENT_MASK];
  529. victimSlotAddr = &directory[numBuckets >> TABLE_SEGMENT_BITS]
  530. ->slot[numBuckets & TABLE_SEGMENT_MASK];
  531. ASSERT(targetSlotAddr != NULL);
  532. ASSERT(victimSlotAddr != NULL);
  533. // If the victim buffer isn't empty, ...
  534. if ((firstVictimEntry = *victimSlotAddr) != NULL) {
  535. #if DBG
  536. mask = ~(~0U << (level+1));
  537. #endif
  538. ASSERT((firstVictimEntry->hashValue & mask) == numBuckets);
  539. ASSERT( firstVictimEntry->prevChain == NULL);
  540. // ... find the end of the target bucket chain, ...
  541. entry = *targetSlotAddr;
  542. prevChain = NULL;
  543. while (entry != NULL) {
  544. ASSERT((entry->hashValue & mask) == expandIndex);
  545. ASSERT( entry->prevChain == prevChain);
  546. prevChain = entry;
  547. entry = entry->nextChain;
  548. }
  549. // ... then add the victim bucket chain to the end of the target bucket chain.
  550. if (prevChain == NULL)
  551. *targetSlotAddr = firstVictimEntry;
  552. else {
  553. prevChain->nextChain = firstVictimEntry;
  554. firstVictimEntry->prevChain = prevChain;
  555. }
  556. }
  557. // Delete the victim bucket, and delete the victim segment if no buckets remain.
  558. if ((numBuckets & TABLE_SEGMENT_MASK) == 0) {
  559. delete directory[numBuckets >> TABLE_SEGMENT_BITS];
  560. directory[numBuckets >> TABLE_SEGMENT_BITS] = NULL;
  561. } else
  562. *victimSlotAddr = NULL;
  563. // Reduce the size of the directory if necessary.
  564. if (numBuckets <= (dirSize - TABLE_DIR_SIZE) * TABLE_SEGMENT_SIZE
  565. && dirSize > TABLE_DIR_SIZE) {
  566. dirSize -= TABLE_DIR_SIZE;
  567. newDirectory = new TableSegment * [dirSize];
  568. ASSERT(newDirectory != NULL);
  569. memcpy(newDirectory, directory, sizeof(TableSegment *) * dirSize);
  570. delete directory;
  571. directory = newDirectory;
  572. }
  573. }
  574. /*****************************************************************************/
  575. /************************ Table class public methods *************************/
  576. /*****************************************************************************/
  577. Table::Table()
  578. {
  579. firstEntry = NULL;
  580. lastEntry = NULL;
  581. numEntries = 0;
  582. numBuckets = TABLE_SEGMENT_SIZE;
  583. expandIndex = 0;
  584. level = TABLE_SEGMENT_BITS;
  585. dirSize = TABLE_DIR_SIZE;
  586. directory = new TableSegment * [dirSize];
  587. ASSERT(directory != NULL);
  588. memset(directory, 0, sizeof(TableSegment *) * dirSize);
  589. directory[0] = new TableSegment;
  590. ASSERT(directory[0] != NULL);
  591. memset(directory[0], 0, sizeof(TableSegment));
  592. }
  593. /*****************************************************************************/
  594. Table::~Table()
  595. {
  596. TableEntry *entry,
  597. *prevEntry;
  598. DWORD numSegments,
  599. segmentNum,
  600. count;
  601. entry = firstEntry;
  602. prevEntry = NULL;
  603. count = 0;
  604. while (entry != NULL) {
  605. ASSERT(entry->prevEntry == prevEntry);
  606. prevEntry = entry;
  607. entry = entry->nextEntry;
  608. delete prevEntry->data;
  609. delete prevEntry;
  610. count++;
  611. }
  612. ASSERT(count == numEntries);
  613. numSegments = numBuckets >> TABLE_SEGMENT_BITS;
  614. ASSERT(directory != NULL);
  615. ASSERT(dirSize >= TABLE_SEGMENT_SIZE);
  616. ASSERT(dirSize % TABLE_SEGMENT_SIZE == 0);
  617. ASSERT(numSegments <= dirSize);
  618. for (segmentNum = 0; segmentNum < numSegments; segmentNum++) {
  619. ASSERT(directory[segmentNum] != NULL);
  620. delete directory[segmentNum];
  621. }
  622. delete directory;
  623. }
  624. /*****************************************************************************/
  625. BOOL Table::Put(
  626. VOID *data,
  627. DWORD keyLen)
  628. {
  629. TableEntry **slotAddr,
  630. *prevChain,
  631. *entry;
  632. DWORD hashValue,
  633. bucketNum;
  634. #if DBG
  635. DWORD mask;
  636. #endif
  637. ASSERT(data != NULL);
  638. ASSERT(keyLen > 0);
  639. // Find the bucket for this data.
  640. hashValue = Hash(data, keyLen);
  641. bucketNum = BucketNum(hashValue);
  642. #if DBG
  643. mask = ~(~0U << (bucketNum < expandIndex || bucketNum >= 1U << level
  644. ? level+1 : level));
  645. #endif
  646. ASSERT(directory != NULL);
  647. slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS]
  648. ->slot[bucketNum & TABLE_SEGMENT_MASK];
  649. ASSERT(slotAddr != NULL);
  650. entry = *slotAddr;
  651. prevChain = NULL;
  652. // Look at each entry in the bucket to determine if the data is
  653. // already present. If a matching entry is found, return FALSE.
  654. while (entry != NULL) {
  655. ASSERT((entry->hashValue & mask) == bucketNum);
  656. ASSERT( entry->prevChain == prevChain);
  657. if (hashValue == entry->hashValue
  658. && keyLen == entry->keyLen
  659. && memcmp(data, entry->data, keyLen) == 0)
  660. return FALSE;
  661. prevChain = entry;
  662. entry = entry->nextChain;
  663. }
  664. // No entry with matching data was found in this bucket.
  665. // Create a new entry and add it to the end of the bucket chain.
  666. entry = new TableEntry;
  667. ASSERT(entry != NULL);
  668. if (prevChain == NULL) {
  669. *slotAddr = entry;
  670. entry->prevChain = NULL;
  671. } else {
  672. prevChain->nextChain = entry;
  673. entry ->prevChain = prevChain;
  674. }
  675. entry->nextChain = NULL;
  676. // Add the entry to the end of the doubly-linked list.
  677. if (lastEntry == NULL) {
  678. ASSERT(firstEntry == NULL);
  679. ASSERT(numEntries == 0);
  680. firstEntry = entry;
  681. entry->prevEntry = NULL;
  682. } else {
  683. ASSERT(firstEntry != NULL);
  684. ASSERT(numEntries > 0);
  685. lastEntry->nextEntry = entry;
  686. entry ->prevEntry = lastEntry;
  687. }
  688. entry->nextEntry = NULL;
  689. lastEntry = entry;
  690. numEntries++;
  691. // Fill out the entry.
  692. entry->hashValue = hashValue;
  693. entry->keyLen = keyLen;
  694. entry->data = data;
  695. // Expand the table if necessary.
  696. if (numEntries > numBuckets * TABLE_MAX_LOAD) {
  697. Expand();
  698. ASSERT(numEntries <= numBuckets * TABLE_MAX_LOAD);
  699. }
  700. return TRUE;
  701. }
  702. /*****************************************************************************/
  703. VOID *Table::Get(
  704. const VOID *key,
  705. DWORD keyLen,
  706. BOOL erase)
  707. {
  708. TableEntry **slotAddr,
  709. *entry,
  710. *prevChain;
  711. DWORD hashValue,
  712. bucketNum;
  713. VOID *dataPtr;
  714. #if DBG
  715. DWORD mask;
  716. #endif
  717. ASSERT(key != NULL);
  718. ASSERT(keyLen > 0);
  719. // Find the bucket for this data.
  720. hashValue = Hash(key, keyLen);
  721. bucketNum = BucketNum(hashValue);
  722. #if DBG
  723. mask = ~(~0U << (bucketNum < expandIndex || bucketNum >= 1U << level
  724. ? level+1 : level));
  725. #endif
  726. ASSERT(directory != NULL);
  727. slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS]
  728. ->slot[bucketNum & TABLE_SEGMENT_MASK];
  729. ASSERT(slotAddr != NULL);
  730. entry = *slotAddr;
  731. prevChain = NULL;
  732. // Look at each entry in the bucket.
  733. while (entry != NULL) {
  734. ASSERT((entry->hashValue & mask) == bucketNum);
  735. ASSERT( entry->prevChain == prevChain);
  736. if (hashValue == entry->hashValue
  737. && keyLen == entry->keyLen
  738. && memcmp(key, entry->data, keyLen) == 0) {
  739. // The entry with matching data has been found.
  740. dataPtr = entry->data;
  741. ASSERT(dataPtr != NULL);
  742. // If erasure is disabled, remove the entry from the doubly-linked list ...
  743. if (erase) {
  744. if (entry->prevEntry == NULL) {
  745. ASSERT(firstEntry == entry);
  746. firstEntry = entry->nextEntry;
  747. } else
  748. entry->prevEntry->nextEntry = entry->nextEntry;
  749. if (entry->nextEntry == NULL) {
  750. ASSERT(lastEntry == entry);
  751. lastEntry = entry->prevEntry;
  752. } else
  753. entry->nextEntry->prevEntry = entry->prevEntry;
  754. // ... and from the bucket chain, ...
  755. if (prevChain == NULL)
  756. *slotAddr = entry->nextChain;
  757. else
  758. prevChain->nextChain = entry->nextChain;
  759. if (entry->nextChain != NULL) {
  760. ASSERT(entry->nextChain->prevChain == entry);
  761. entry->nextChain->prevChain = prevChain;
  762. }
  763. // ... then delete the entry.
  764. delete entry;
  765. // Decrement the number of entries, and contract the table if necessary.
  766. numEntries--;
  767. if (numBuckets > TABLE_SEGMENT_SIZE
  768. && numEntries < numBuckets * TABLE_MIN_LOAD) {
  769. Contract();
  770. ASSERT(numBuckets <= TABLE_SEGMENT_SIZE
  771. || numEntries >= numBuckets * TABLE_MIN_LOAD);
  772. }
  773. }
  774. return dataPtr;
  775. }
  776. // No entry with matching data has yet been found.
  777. // Continue following the bucket chain.
  778. prevChain = entry;
  779. entry = entry->nextChain;
  780. }
  781. // No entry with matching data was found in this bucket.
  782. return NULL;
  783. }
  784. /*****************************************************************************/
  785. VOID *Table::GetFirst(
  786. DWORD *keyLen,
  787. BOOL erase)
  788. {
  789. TableEntry **slotAddr,
  790. *entry;
  791. DWORD bucketNum;
  792. VOID *dataPtr;
  793. // If the table is empty, then simply return.
  794. if (firstEntry == NULL) {
  795. ASSERT(lastEntry == NULL);
  796. ASSERT(numEntries == 0);
  797. return NULL;
  798. }
  799. dataPtr = firstEntry->data;
  800. ASSERT(dataPtr != NULL);
  801. if (keyLen != NULL) {
  802. *keyLen = firstEntry->keyLen;
  803. ASSERT(firstEntry->keyLen > 0);
  804. }
  805. // If erasure is enabled, remove the first entry from the doubly-linked list ...
  806. if (erase) {
  807. entry = firstEntry;
  808. firstEntry = entry->nextEntry;
  809. if (firstEntry == NULL) {
  810. ASSERT(numEntries == 1);
  811. ASSERT(lastEntry == entry);
  812. lastEntry = NULL;
  813. } else {
  814. ASSERT(numEntries > 1);
  815. ASSERT(firstEntry->prevEntry == entry);
  816. firstEntry->prevEntry = NULL;
  817. }
  818. // ... and from the bucket chain, ...
  819. if (entry->prevChain == NULL) {
  820. bucketNum = BucketNum(entry->hashValue);
  821. ASSERT(directory != NULL);
  822. slotAddr = &directory[bucketNum >> TABLE_SEGMENT_BITS]
  823. ->slot[bucketNum & TABLE_SEGMENT_MASK];
  824. ASSERT( slotAddr != NULL);
  825. ASSERT(*slotAddr == entry);
  826. *slotAddr = entry->nextChain;
  827. } else {
  828. ASSERT(entry->prevChain->nextChain == entry);
  829. entry->prevChain->nextChain = entry->nextChain;
  830. }
  831. if (entry->nextChain != NULL) {
  832. ASSERT(entry->nextChain->prevChain == entry);
  833. entry->nextChain->prevChain = entry->prevChain;
  834. }
  835. // ... then delete the entry.
  836. delete entry;
  837. // Decrement the number of entries, and contract the table if necessary.
  838. numEntries--;
  839. if (numBuckets > TABLE_SEGMENT_SIZE
  840. && numEntries < numBuckets * TABLE_MIN_LOAD) {
  841. Contract();
  842. ASSERT(numBuckets <= TABLE_SEGMENT_SIZE
  843. || numEntries >= numBuckets * TABLE_MIN_LOAD);
  844. }
  845. }
  846. return dataPtr;
  847. }
  848. /*****************************************************************************/
  849. DWORD Table::Number() const
  850. {
  851. return numEntries;
  852. }
  853. /*****************************************************************************/
  854. /************************* FIFO class public methods *************************/
  855. /*****************************************************************************/
  856. FIFO::FIFO()
  857. {
  858. head = tail = NULL;
  859. numEntries = 0;
  860. }
  861. /*****************************************************************************/
  862. FIFO::~FIFO()
  863. {
  864. FIFOEntry *entry = head,
  865. *oldEntry;
  866. DWORD count = 0;
  867. while ((oldEntry = entry) != NULL) {
  868. entry = entry->next;
  869. delete oldEntry->data;
  870. delete oldEntry;
  871. count++;
  872. }
  873. ASSERT(count == numEntries);
  874. }
  875. /*****************************************************************************/
  876. VOID FIFO::Put(VOID *data)
  877. {
  878. FIFOEntry *newEntry;
  879. ASSERT(data != NULL);
  880. newEntry = new FIFOEntry;
  881. ASSERT(newEntry != NULL);
  882. newEntry->next = NULL;
  883. newEntry->data = data;
  884. if (tail != NULL)
  885. tail->next = newEntry;
  886. else
  887. head = newEntry;
  888. tail = newEntry;
  889. numEntries++;
  890. }
  891. /*****************************************************************************/
  892. VOID *FIFO::Get()
  893. {
  894. FIFOEntry *oldHead;
  895. VOID *dataPtr;
  896. if (head == NULL) {
  897. ASSERT(tail == NULL);
  898. ASSERT(numEntries == 0);
  899. return NULL;
  900. }
  901. ASSERT(tail != NULL);
  902. ASSERT(numEntries > 0);
  903. dataPtr = head->data;
  904. oldHead = head;
  905. head = head->next;
  906. delete oldHead;
  907. if (head == NULL)
  908. tail = NULL;
  909. numEntries--;
  910. return dataPtr;
  911. }
  912. /*****************************************************************************/
  913. DWORD FIFO::Number() const
  914. {
  915. return numEntries;
  916. }
  917. /*****************************************************************************/
  918. /************************* LIFO class public methods *************************/
  919. /*****************************************************************************/
  920. LIFO::LIFO()
  921. {
  922. top = NULL;
  923. numEntries = 0;
  924. }
  925. /*****************************************************************************/
  926. LIFO::~LIFO()
  927. {
  928. LIFOEntry *entry = top,
  929. *oldEntry;
  930. DWORD count = 0;
  931. while ((oldEntry = entry) != NULL) {
  932. entry = entry->next;
  933. delete oldEntry->data;
  934. delete oldEntry;
  935. count++;
  936. }
  937. ASSERT(count == numEntries);
  938. }
  939. /*****************************************************************************/
  940. VOID LIFO::Put(VOID *data)
  941. {
  942. LIFOEntry *newEntry;
  943. ASSERT(data != NULL);
  944. newEntry = new LIFOEntry;
  945. ASSERT(newEntry != NULL);
  946. newEntry->next = top;
  947. newEntry->data = data;
  948. top = newEntry;
  949. numEntries++;
  950. }
  951. /*****************************************************************************/
  952. VOID *LIFO::Get()
  953. {
  954. LIFOEntry *oldTop;
  955. VOID *dataPtr;
  956. if (top == NULL) {
  957. ASSERT(numEntries == 0);
  958. return NULL;
  959. }
  960. ASSERT(numEntries > 0);
  961. dataPtr = top->data;
  962. oldTop = top;
  963. top = top->next;
  964. delete oldTop;
  965. numEntries--;
  966. return dataPtr;
  967. }
  968. /*****************************************************************************/
  969. DWORD LIFO::Number() const
  970. {
  971. return numEntries;
  972. }