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.

755 lines
17 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. cache.c
  5. Abstract:
  6. Implements a cache mechanism to speed up OpenRegKeyStr.
  7. Author:
  8. Jim Schmidt (jimschm) 11-Sep-2000
  9. Revisions:
  10. <alias> <date> <comments>
  11. --*/
  12. #include "pch.h"
  13. #include "regp.h"
  14. #define DBG_REG "Reg"
  15. typedef struct {
  16. HKEY Key;
  17. BOOL Unicode;
  18. UINT RefCount;
  19. UINT ClosesNeeded;
  20. REGSAM Sam;
  21. UINT KeyStringBytes;
  22. BYTE KeyString[];
  23. } REGKEYCACHE, *PREGKEYCACHE;
  24. typedef struct {
  25. HKEY Key;
  26. BOOL Unicode;
  27. UINT RefCount;
  28. UINT ClosesNeeded;
  29. REGSAM Sam;
  30. UINT KeyStringBytes;
  31. CHAR KeyString[MAX_REGISTRY_KEY];
  32. } WORKITEMA, *PWORKITEMA;
  33. typedef struct {
  34. HKEY Key;
  35. BOOL Unicode;
  36. UINT RefCount;
  37. UINT ClosesNeeded;
  38. REGSAM Sam;
  39. UINT KeyStringBytes;
  40. WCHAR KeyString[MAX_REGISTRY_KEY];
  41. } WORKITEMW, *PWORKITEMW;
  42. GROWLIST g_KeyCache = GROWLIST_INIT;
  43. UINT g_MaxCacheCount = 0;
  44. UINT g_CacheAddPos;
  45. CHAR g_LastParent[MAX_REGISTRY_KEY];
  46. UINT g_LastParentBytes;
  47. UINT g_LastParentUse;
  48. WCHAR g_LastParentW[MAX_REGISTRY_KEY];
  49. UINT g_LastParentBytesW;
  50. UINT g_LastParentUseW;
  51. BOOL
  52. pRemoveItemFromCache (
  53. IN UINT Item
  54. );
  55. //
  56. // Implementation
  57. //
  58. VOID
  59. RegInitializeCache (
  60. IN UINT InitialCacheSize
  61. )
  62. {
  63. if (InitialCacheSize > 64) {
  64. InitialCacheSize = 64;
  65. }
  66. g_MaxCacheCount = InitialCacheSize;
  67. g_CacheAddPos = 0;
  68. g_LastParentUse = 0;
  69. g_LastParentUseW = 0;
  70. }
  71. VOID
  72. RegTerminateCache (
  73. VOID
  74. )
  75. {
  76. UINT u;
  77. UINT count;
  78. count = GrowListGetSize (&g_KeyCache);
  79. for (u = 0 ; u < count ; u++) {
  80. pRemoveItemFromCache (u);
  81. }
  82. FreeGrowList (&g_KeyCache);
  83. g_MaxCacheCount = 0;
  84. g_LastParentUse = 0;
  85. g_LastParentUseW = 0;
  86. }
  87. BOOL
  88. pRemoveItemFromCache (
  89. IN UINT Item
  90. )
  91. {
  92. PREGKEYCACHE cacheItem;
  93. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, Item);
  94. if (!cacheItem) {
  95. return TRUE;
  96. }
  97. if (cacheItem->RefCount == 0) {
  98. while (cacheItem->ClosesNeeded) {
  99. CloseRegKeyWorker (cacheItem->Key);
  100. cacheItem->ClosesNeeded--;
  101. }
  102. return TRUE;
  103. } else {
  104. return FALSE;
  105. }
  106. }
  107. VOID
  108. RegRecordParentInCacheA (
  109. IN PCSTR KeyString,
  110. IN PCSTR StringEnd
  111. )
  112. {
  113. HKEY key;
  114. UINT byteCount;
  115. CHAR lowerStr[MAX_REGISTRY_KEY];
  116. HKEY rootKey;
  117. UINT end;
  118. if (!g_MaxCacheCount) {
  119. return;
  120. }
  121. if (StringEnd <= KeyString) {
  122. return;
  123. }
  124. byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
  125. if (byteCount >= (MAX_REGISTRY_KEY * sizeof (CHAR))) {
  126. return;
  127. }
  128. if (g_LastParentUse && g_LastParentUse < 3 && g_LastParentBytes == byteCount) {
  129. StringCopyABA (lowerStr, KeyString, StringEnd);
  130. CharLowerA (lowerStr);
  131. if (StringMatchA (g_LastParent, lowerStr)) {
  132. g_LastParentUse++;
  133. if (g_LastParentUse == 3) {
  134. //
  135. // Stimulate the cache
  136. //
  137. rootKey = ConvertRootStringToKeyA (lowerStr, &end);
  138. if (rootKey) {
  139. if (lowerStr[end]) {
  140. #ifdef DEBUG
  141. key = OpenRegKeyWorkerA (rootKey, &lowerStr[end], __FILE__, __LINE__);
  142. #else
  143. key = OpenRegKeyWorkerA (rootKey, &lowerStr[end]);
  144. #endif
  145. if (key) {
  146. RegAddKeyToCacheA (lowerStr, key, g_OpenSam);
  147. CloseRegKey (key);
  148. }
  149. }
  150. }
  151. }
  152. return;
  153. }
  154. }
  155. StringCopyABA (g_LastParent, KeyString, StringEnd);
  156. CharLowerA (g_LastParent);
  157. g_LastParentBytes = byteCount;
  158. g_LastParentUse = 1;
  159. }
  160. HKEY
  161. RegGetKeyFromCacheA (
  162. IN PCSTR KeyString,
  163. IN PCSTR KeyEnd, OPTIONAL
  164. IN REGSAM Sam,
  165. IN BOOL IncRefCount
  166. )
  167. {
  168. UINT stringBytes;
  169. PCSTR end;
  170. CHAR lowerStr[MAX_REGISTRY_KEY];
  171. UINT u;
  172. UINT count;
  173. PREGKEYCACHE cacheItem;
  174. count = GrowListGetSize (&g_KeyCache);
  175. if (!count) {
  176. return NULL;
  177. }
  178. if (!KeyEnd) {
  179. end = StackStringCopyA (lowerStr, KeyString);
  180. } else {
  181. if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
  182. KeyEnd = KeyString + MAX_REGISTRY_KEY;
  183. }
  184. end = StringCopyABA (lowerStr, KeyString, KeyEnd);
  185. }
  186. CharLowerA (lowerStr);
  187. stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
  188. //
  189. // Scan the cache sequentially (it should be small), and return a match
  190. // if one is found. Stored strings are always in lower case.
  191. //
  192. u = g_CacheAddPos;
  193. do {
  194. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
  195. if (!cacheItem) {
  196. return NULL;
  197. }
  198. if ((cacheItem->KeyStringBytes == stringBytes) &&
  199. (!cacheItem->Unicode) &&
  200. ((cacheItem->Sam & Sam) == Sam) &&
  201. (StringMatchA ((PCSTR) cacheItem->KeyString, lowerStr))
  202. ) {
  203. if (IncRefCount) {
  204. cacheItem->RefCount++;
  205. }
  206. return cacheItem->Key;
  207. }
  208. u++;
  209. if (u >= count) {
  210. u = 0;
  211. }
  212. } while (u != g_CacheAddPos);
  213. return NULL;
  214. }
  215. VOID
  216. RegAddKeyToCacheA (
  217. IN PCSTR KeyString,
  218. IN HKEY Key,
  219. IN REGSAM Sam
  220. )
  221. {
  222. PREGKEYCACHE cacheItem;
  223. PREGKEYCACHE lastAddItem;
  224. WORKITEMA workItem;
  225. PCSTR end;
  226. UINT minStructSize;
  227. UINT pos;
  228. UINT count;
  229. UINT u;
  230. if (!g_MaxCacheCount || !Key) {
  231. return;
  232. }
  233. //
  234. // Scan the cache for the existing Key
  235. //
  236. count = GrowListGetSize (&g_KeyCache);
  237. for (u = 0 ; u < count ; u++) {
  238. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
  239. if (cacheItem->Key == Key) {
  240. cacheItem->RefCount++;
  241. g_CacheAddPos = u;
  242. cacheItem->Sam |= Sam;
  243. if (cacheItem->KeyStringBytes == 0 && *KeyString && !cacheItem->Unicode) {
  244. //
  245. // This key was added before we knew the name. Update the name
  246. // now.
  247. //
  248. DEBUGMSG ((DBG_REG, "Updating empty-named key %s", KeyString));
  249. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
  250. CopyMemory (&workItem, cacheItem, minStructSize);
  251. end = StackStringCopyA (workItem.KeyString, KeyString);
  252. CharLowerA (workItem.KeyString);
  253. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  254. workItem.ClosesNeeded++;
  255. minStructSize += workItem.KeyStringBytes + sizeof (CHAR);
  256. GrowListSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
  257. } else if (*KeyString == 0) {
  258. cacheItem->ClosesNeeded++;
  259. }
  260. return;
  261. }
  262. }
  263. //
  264. // Create the new cache item
  265. //
  266. workItem.Key = Key;
  267. workItem.Unicode = FALSE;
  268. workItem.RefCount = 1;
  269. workItem.ClosesNeeded = 1;
  270. workItem.Sam = Sam;
  271. end = StackStringCopyA (workItem.KeyString, KeyString);
  272. CharLowerA (workItem.KeyString);
  273. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  274. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (CHAR);
  275. //
  276. // Put work item into grow list
  277. //
  278. if (count < g_MaxCacheCount) {
  279. g_CacheAddPos = count;
  280. GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  281. } else {
  282. //
  283. // Look for a closed key to discard. If cache is too full, then
  284. // increase the cache size. If the cache size hits 64, then don't
  285. // cache this add.
  286. //
  287. lastAddItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, g_CacheAddPos);
  288. if (lastAddItem) {
  289. for (pos = 0 ; pos < count ; pos++) {
  290. if (pos == g_CacheAddPos) {
  291. continue;
  292. }
  293. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, pos);
  294. if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
  295. break;
  296. }
  297. if (cacheItem->Unicode) {
  298. continue;
  299. }
  300. if (cacheItem->RefCount) {
  301. continue;
  302. }
  303. if (!StringPrefixA ((PCSTR) lastAddItem->KeyString, (PCSTR) cacheItem->KeyString)) {
  304. break;
  305. }
  306. }
  307. if (pos == count) {
  308. if (g_MaxCacheCount == 64) {
  309. DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
  310. return;
  311. }
  312. g_MaxCacheCount++;
  313. GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  314. } else {
  315. pRemoveItemFromCache (pos);
  316. GrowListSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
  317. }
  318. g_CacheAddPos = pos;
  319. }
  320. }
  321. }
  322. BOOL
  323. RegDecrementRefCount (
  324. IN HKEY Key
  325. )
  326. {
  327. UINT u;
  328. UINT count;
  329. PREGKEYCACHE cacheItem;
  330. if (!g_MaxCacheCount) {
  331. return FALSE;
  332. }
  333. count = GrowListGetSize (&g_KeyCache);
  334. for (u = 0 ; u < count ; u++) {
  335. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
  336. if (cacheItem->Key == Key) {
  337. if (cacheItem->RefCount == 0) {
  338. //
  339. // The caller is tried to close the key more times than what
  340. // it was opened.
  341. //
  342. if (cacheItem->Unicode) {
  343. DEBUGMSGW ((
  344. DBG_WHOOPS,
  345. "Reg key %s ref count == 0; trying to close it too many times",
  346. cacheItem->KeyString
  347. ));
  348. } else {
  349. DEBUGMSGA ((
  350. DBG_WHOOPS,
  351. "Reg key %s ref count == 0; trying to close it too many times",
  352. cacheItem->KeyString
  353. ));
  354. }
  355. } else {
  356. cacheItem->RefCount--;
  357. }
  358. //
  359. // Return TRUE to postpone the close
  360. //
  361. return TRUE;
  362. }
  363. }
  364. return FALSE;
  365. }
  366. VOID
  367. RegRecordParentInCacheW (
  368. IN PCWSTR KeyString,
  369. IN PCWSTR StringEnd
  370. )
  371. {
  372. HKEY key;
  373. UINT byteCount;
  374. WCHAR lowerStr[MAX_REGISTRY_KEY];
  375. HKEY rootKey;
  376. UINT end;
  377. if (!g_MaxCacheCount) {
  378. return;
  379. }
  380. if (StringEnd <= KeyString) {
  381. return;
  382. }
  383. byteCount = (UINT) (HALF_PTR) ((PBYTE) StringEnd - (PBYTE) KeyString);
  384. if (byteCount >= (MAX_REGISTRY_KEY * sizeof (WCHAR))) {
  385. return;
  386. }
  387. if (g_LastParentUseW && g_LastParentUseW < 3 && g_LastParentBytesW == byteCount) {
  388. StringCopyABW (lowerStr, KeyString, StringEnd);
  389. CharLowerW (lowerStr);
  390. if (StringMatchW (g_LastParentW, lowerStr)) {
  391. g_LastParentUseW++;
  392. if (g_LastParentUseW == 3) {
  393. //
  394. // Stimulate the cache
  395. //
  396. rootKey = ConvertRootStringToKeyW (lowerStr, &end);
  397. if (rootKey) {
  398. if (lowerStr[end]) {
  399. #ifdef DEBUG
  400. key = OpenRegKeyWorkerW (rootKey, &lowerStr[end], __FILE__, __LINE__);
  401. #else
  402. key = OpenRegKeyWorkerW (rootKey, &lowerStr[end]);
  403. #endif
  404. if (key) {
  405. RegAddKeyToCacheW (lowerStr, key, g_OpenSam);
  406. CloseRegKey (key);
  407. }
  408. }
  409. }
  410. }
  411. return;
  412. }
  413. }
  414. StringCopyABW (g_LastParentW, KeyString, StringEnd);
  415. CharLowerW (g_LastParentW);
  416. g_LastParentBytesW = byteCount;
  417. g_LastParentUseW = 1;
  418. }
  419. HKEY
  420. RegGetKeyFromCacheW (
  421. IN PCWSTR KeyString,
  422. IN PCWSTR KeyEnd, OPTIONAL
  423. IN REGSAM Sam,
  424. IN BOOL IncRefCount
  425. )
  426. {
  427. UINT stringBytes;
  428. PCWSTR end;
  429. WCHAR lowerStr[MAX_REGISTRY_KEY];
  430. UINT u;
  431. UINT count;
  432. PREGKEYCACHE cacheItem;
  433. count = GrowListGetSize (&g_KeyCache);
  434. if (!count) {
  435. return NULL;
  436. }
  437. if (!KeyEnd) {
  438. end = StackStringCopyW (lowerStr, KeyString);
  439. } else {
  440. if (KeyEnd > KeyString + MAX_REGISTRY_KEY) {
  441. KeyEnd = KeyString + MAX_REGISTRY_KEY;
  442. }
  443. end = StringCopyABW (lowerStr, KeyString, KeyEnd);
  444. }
  445. CharLowerW (lowerStr);
  446. stringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) lowerStr);
  447. //
  448. // Scan the cache sequentially (it should be small), and return a match
  449. // if one is found. Stored strings are always in lower case.
  450. //
  451. u = g_CacheAddPos;
  452. do {
  453. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
  454. if (!cacheItem) {
  455. return NULL;
  456. }
  457. if ((cacheItem->KeyStringBytes == stringBytes) &&
  458. (cacheItem->Unicode) &&
  459. ((cacheItem->Sam & Sam) == Sam) &&
  460. (StringMatchW ((PCWSTR) cacheItem->KeyString, lowerStr))
  461. ) {
  462. if (IncRefCount) {
  463. cacheItem->RefCount++;
  464. }
  465. return cacheItem->Key;
  466. }
  467. u++;
  468. if (u >= count) {
  469. u = 0;
  470. }
  471. } while (u != g_CacheAddPos);
  472. return NULL;
  473. }
  474. VOID
  475. RegAddKeyToCacheW (
  476. IN PCWSTR KeyString,
  477. IN HKEY Key,
  478. IN REGSAM Sam
  479. )
  480. {
  481. PREGKEYCACHE cacheItem;
  482. PREGKEYCACHE lastAddItem;
  483. WORKITEMW workItem;
  484. PCWSTR end;
  485. UINT minStructSize;
  486. UINT pos;
  487. UINT count;
  488. UINT u;
  489. if (!g_MaxCacheCount || !Key) {
  490. return;
  491. }
  492. //
  493. // Scan the cache for the existing Key
  494. //
  495. count = GrowListGetSize (&g_KeyCache);
  496. for (u = 0 ; u < count ; u++) {
  497. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, u);
  498. if (cacheItem->Key == Key) {
  499. cacheItem->RefCount++;
  500. g_CacheAddPos = u;
  501. cacheItem->Sam |= Sam;
  502. if (cacheItem->KeyStringBytes == 0 && *KeyString && cacheItem->Unicode) {
  503. //
  504. // This key was added before we knew the name. Update the name
  505. // now.
  506. //
  507. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString);
  508. CopyMemory (&workItem, cacheItem, minStructSize);
  509. end = StackStringCopyW (workItem.KeyString, KeyString);
  510. CharLowerW (workItem.KeyString);
  511. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  512. workItem.ClosesNeeded++;
  513. minStructSize += workItem.KeyStringBytes + sizeof (WCHAR);
  514. GrowListSetItem (&g_KeyCache, u, (PBYTE) &workItem, minStructSize);
  515. } else if (*KeyString == 0) {
  516. cacheItem->ClosesNeeded++;
  517. }
  518. return;
  519. }
  520. }
  521. //
  522. // Create the new cache item
  523. //
  524. workItem.Key = Key;
  525. workItem.Unicode = TRUE;
  526. workItem.RefCount = 1;
  527. workItem.ClosesNeeded = 1;
  528. workItem.Sam = Sam;
  529. end = StackStringCopyW (workItem.KeyString, KeyString);
  530. CharLowerW (workItem.KeyString);
  531. workItem.KeyStringBytes = (UINT) (HALF_PTR) ((PBYTE) end - (PBYTE) workItem.KeyString);
  532. minStructSize = sizeof (workItem) - sizeof (workItem.KeyString) + workItem.KeyStringBytes + sizeof (WCHAR);
  533. //
  534. // Put work item into grow list
  535. //
  536. if (count < g_MaxCacheCount) {
  537. g_CacheAddPos = count;
  538. GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  539. } else {
  540. //
  541. // Look for a closed key to discard. If cache is too full, then
  542. // increase the cache size. If the cache size hits 64, then don't
  543. // cache this add.
  544. //
  545. lastAddItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, g_CacheAddPos);
  546. if (lastAddItem) {
  547. for (pos = 0 ; pos < count ; pos++) {
  548. if (pos == g_CacheAddPos) {
  549. continue;
  550. }
  551. cacheItem = (PREGKEYCACHE) GrowListGetItem (&g_KeyCache, pos);
  552. if (cacheItem->KeyStringBytes >= lastAddItem->KeyStringBytes) {
  553. break;
  554. }
  555. if (!cacheItem->Unicode) {
  556. continue;
  557. }
  558. if (cacheItem->RefCount) {
  559. continue;
  560. }
  561. if (!StringPrefixW ((PCWSTR) lastAddItem->KeyString, (PCWSTR) cacheItem->KeyString)) {
  562. break;
  563. }
  564. }
  565. if (pos == count) {
  566. if (g_MaxCacheCount == 64) {
  567. DEBUGMSG ((DBG_REG, "Cache is full of open keys"));
  568. return;
  569. }
  570. g_MaxCacheCount++;
  571. GrowListAppend (&g_KeyCache, (PBYTE) &workItem, minStructSize);
  572. } else {
  573. pRemoveItemFromCache (pos);
  574. GrowListSetItem (&g_KeyCache, pos, (PBYTE) &workItem, minStructSize);
  575. }
  576. g_CacheAddPos = pos;
  577. }
  578. }
  579. }