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.

758 lines
19 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: atoms.c
  6. //
  7. // This files contains the atom list code.
  8. //
  9. // History:
  10. // 01-31-94 ScottH Moved from cache.c
  11. //
  12. //---------------------------------------------------------------------------
  13. ///////////////////////////////////////////////////// INCLUDES
  14. #include "brfprv.h" // common headers
  15. ///////////////////////////////////////////////////// TYPEDEFS
  16. typedef struct tagA_ITEM
  17. {
  18. int atom; // index into hdsa
  19. LPTSTR psz; // allocated
  20. UINT ucRef;
  21. } A_ITEM; // item for atom table
  22. typedef struct tagATOMTABLE
  23. {
  24. CRITICAL_SECTION cs;
  25. HDSA hdsa; // Actual list of A_ITEMs
  26. HDPA hdpa; // List into hdsa (sorted). Values are indexes, not pointers
  27. HDPA hdpaFree; // Free list. Values are indexes, not pointers.
  28. } ATOMTABLE;
  29. #define Atom_EnterCS(this) EnterCriticalSection(&(this)->cs)
  30. #define Atom_LeaveCS(this) LeaveCriticalSection(&(this)->cs)
  31. #define ATOM_GROW 32
  32. #define Cache_Bogus(this) (!(this)->hdpa || !(this)->hdpaFree || !(this)->hdsa)
  33. // Given an index into the DPA, get the pointer to the DSA
  34. //
  35. #define MyGetPtr(this, idpa) DSA_GetItemPtr((this)->hdsa, PtrToUlong(DPA_FastGetPtr((this)->hdpa, idpa)))
  36. ///////////////////////////////////////////////////// MODULE DATA
  37. static ATOMTABLE s_atomtable;
  38. #ifdef DEBUG
  39. /*----------------------------------------------------------
  40. Purpose: Validates the given atom is within the atomtable's range
  41. Returns: --
  42. Cond: --
  43. */
  44. void PUBLIC Atom_ValidateFn(
  45. int atom)
  46. {
  47. ATOMTABLE * this = &s_atomtable;
  48. BOOL bError = FALSE;
  49. Atom_EnterCS(this);
  50. {
  51. if (atom >= DSA_GetItemCount(this->hdsa) ||
  52. atom < 0)
  53. {
  54. bError = TRUE;
  55. }
  56. }
  57. Atom_LeaveCS(this);
  58. if (bError)
  59. {
  60. // This is a problem!
  61. //
  62. DEBUG_MSG(TF_ERROR, TEXT("err BRIEFCASE: atom %d is out of range!"), atom);
  63. DEBUG_BREAK(BF_ONVALIDATE);
  64. }
  65. }
  66. /*----------------------------------------------------------
  67. Purpose: Dump the table contents
  68. Returns: --
  69. Cond: For debugging purposes
  70. */
  71. void PUBLIC Atom_DumpAll()
  72. {
  73. ATOMTABLE * this = &s_atomtable;
  74. Atom_EnterCS(this);
  75. {
  76. if (IsFlagSet(g_uDumpFlags, DF_ATOMS))
  77. {
  78. A_ITEM * pitem;
  79. int idpa;
  80. int cItem;
  81. ASSERT(this);
  82. ASSERT(this->hdsa != NULL);
  83. cItem = DPA_GetPtrCount(this->hdpa);
  84. for (idpa = 0; idpa < cItem; idpa++)
  85. {
  86. pitem = MyGetPtr(this, idpa);
  87. // The zero'th entry is reserved, so skip it
  88. if (pitem->atom == 0)
  89. continue;
  90. TRACE_MSG(TF_ALWAYS, TEXT("ATOM: Atom %d [%u]: %s"),
  91. pitem->atom, pitem->ucRef, pitem->psz);
  92. }
  93. }
  94. }
  95. Atom_LeaveCS(this);
  96. }
  97. #endif
  98. /*----------------------------------------------------------
  99. Purpose: Compare A_ITEMs
  100. Returns: -1 if <, 0 if ==, 1 if >
  101. Cond: --
  102. */
  103. int CALLBACK _export Atom_CompareIndexes(
  104. LPVOID lpv1,
  105. LPVOID lpv2,
  106. LPARAM lParam)
  107. {
  108. int i1 = PtrToUlong(lpv1);
  109. int i2 = PtrToUlong(lpv2);
  110. HDSA hdsa = (HDSA)lParam;
  111. A_ITEM * pitem1 = DSA_GetItemPtr(hdsa, i1);
  112. A_ITEM * pitem2 = DSA_GetItemPtr(hdsa, i2);
  113. ASSERT(pitem1);
  114. ASSERT(pitem2);
  115. return lstrcmpi(pitem1->psz, pitem2->psz);
  116. }
  117. /*----------------------------------------------------------
  118. Purpose: Compare A_ITEMs
  119. Returns: -1 if <, 0 if ==, 1 if >
  120. Cond: --
  121. */
  122. int CALLBACK _export Atom_Compare(
  123. LPVOID lpv1,
  124. LPVOID lpv2,
  125. LPARAM lParam)
  126. {
  127. // HACK: we know the first param is the address to a struct
  128. // that contains the search criteria. The second is an index
  129. // into the DSA.
  130. //
  131. int i2 = PtrToUlong(lpv2);
  132. HDSA hdsa = (HDSA)lParam;
  133. A_ITEM * pitem1 = (A_ITEM *)lpv1;
  134. A_ITEM * pitem2 = DSA_GetItemPtr(hdsa, i2);
  135. ASSERT(pitem1);
  136. ASSERT(pitem2);
  137. return lstrcmpi(pitem1->psz, pitem2->psz);
  138. }
  139. /*----------------------------------------------------------
  140. Purpose: Initialize the atom table
  141. Returns: TRUE on success
  142. Cond: --
  143. */
  144. BOOL PUBLIC Atom_Init()
  145. {
  146. BOOL bRet;
  147. ATOMTABLE * this = &s_atomtable;
  148. ASSERT(this);
  149. ZeroInit(this, ATOMTABLE);
  150. InitializeCriticalSection(&this->cs);
  151. Atom_EnterCS(this);
  152. {
  153. if ((this->hdsa = DSA_Create(sizeof(A_ITEM), ATOM_GROW)) != NULL)
  154. {
  155. if ((this->hdpa = DPA_Create(ATOM_GROW)) == NULL)
  156. {
  157. DSA_Destroy(this->hdsa);
  158. this->hdsa = NULL;
  159. }
  160. else
  161. {
  162. if ((this->hdpaFree = DPA_Create(ATOM_GROW)) == NULL)
  163. {
  164. DPA_Destroy(this->hdpa);
  165. DSA_Destroy(this->hdsa);
  166. this->hdpa = NULL;
  167. this->hdsa = NULL;
  168. }
  169. else
  170. {
  171. // We've successfully initialized. Keep the zero'th
  172. // atom reserved. This way null atoms will not accidentally
  173. // munge data.
  174. //
  175. int atom = Atom_Add(TEXT("SHDD"));
  176. ASSERT(atom == 0);
  177. }
  178. }
  179. }
  180. bRet = this->hdsa != NULL;
  181. }
  182. Atom_LeaveCS(this);
  183. return bRet;
  184. }
  185. /*----------------------------------------------------------
  186. Purpose: Destroy the atom table
  187. Returns: --
  188. Cond: --
  189. */
  190. void PUBLIC Atom_Term()
  191. {
  192. ATOMTABLE * this = &s_atomtable;
  193. Atom_EnterCS(this);
  194. {
  195. if (this->hdpa != NULL)
  196. {
  197. A_ITEM * pitem;
  198. int idpa;
  199. int cItem;
  200. ASSERT(this->hdsa != NULL);
  201. cItem = DPA_GetPtrCount(this->hdpa);
  202. for (idpa = 0; idpa < cItem; idpa++)
  203. {
  204. pitem = MyGetPtr(this, idpa);
  205. // The zero'th entry is reserved, so skip it
  206. if (pitem->atom == 0)
  207. continue;
  208. Str_SetPtr(&pitem->psz, NULL);
  209. }
  210. DPA_Destroy(this->hdpa);
  211. this->hdpa = NULL;
  212. }
  213. if (this->hdpaFree != NULL)
  214. {
  215. DPA_Destroy(this->hdpaFree);
  216. this->hdpaFree = NULL;
  217. }
  218. if (this->hdsa != NULL)
  219. {
  220. DSA_Destroy(this->hdsa);
  221. this->hdsa = NULL;
  222. }
  223. }
  224. Atom_LeaveCS(this);
  225. DeleteCriticalSection(&this->cs);
  226. }
  227. /*----------------------------------------------------------
  228. Purpose: Add a string to the atom table. If the string already
  229. exists, return its atom.
  230. Returns: Atom
  231. ATOM_ERR on failure
  232. Cond: Reference count is incremented always
  233. */
  234. int PUBLIC Atom_Add(
  235. LPCTSTR psz)
  236. {
  237. ATOMTABLE * this = &s_atomtable;
  238. A_ITEM * pitem = NULL;
  239. A_ITEM item;
  240. int atomRet = ATOM_ERR;
  241. int idpa;
  242. int cItem;
  243. int cFree;
  244. ASSERT(psz);
  245. Atom_EnterCS(this);
  246. {
  247. int iItem;
  248. DEBUG_CODE( iItem = -1; )
  249. // Search for the string in the atom table first.
  250. // If we find it, return the atom.
  251. //
  252. item.psz = (LPTSTR)(LPVOID)psz;
  253. idpa = DPA_Search(this->hdpa, &item, 0, Atom_Compare, (LPARAM)this->hdsa, DPAS_SORTED);
  254. if (idpa != -1)
  255. {
  256. // String is already in table
  257. //
  258. pitem = MyGetPtr(this, idpa);
  259. pitem->ucRef++;
  260. atomRet = pitem->atom;
  261. ASSERT(IsSzEqual(psz, pitem->psz));
  262. VALIDATE_ATOM(pitem->atom);
  263. }
  264. else
  265. {
  266. // Add the string to the table. Take any available entry
  267. // from the free list first. Otherwise allocate more space
  268. // in the table. Then add a ptr to the sorted ptr list.
  269. //
  270. cFree = DPA_GetPtrCount(this->hdpaFree);
  271. if (cFree > 0)
  272. {
  273. // Use a free entry
  274. //
  275. cFree--;
  276. iItem = PtrToUlong(DPA_DeletePtr(this->hdpaFree, cFree));
  277. pitem = DSA_GetItemPtr(this->hdsa, iItem);
  278. // atom field for pitem should already be set
  279. VALIDATE_ATOM(pitem->atom);
  280. }
  281. else
  282. {
  283. // Allocate a new entry. item has bogus data in it.
  284. // That's okay, we fill in good stuff below.
  285. //
  286. cItem = DSA_GetItemCount(this->hdsa);
  287. if ((iItem = DSA_InsertItem(this->hdsa, cItem+1, &item)) != -1)
  288. {
  289. pitem = DSA_GetItemPtr(this->hdsa, iItem);
  290. pitem->atom = iItem;
  291. VALIDATE_ATOM(pitem->atom);
  292. }
  293. }
  294. // Fill in the info
  295. //
  296. if (pitem)
  297. {
  298. pitem->ucRef = 1;
  299. pitem->psz = 0;
  300. if (!Str_SetPtr(&pitem->psz, psz))
  301. goto Add_Fail;
  302. // Add the new entry to the ptr list and sort
  303. //
  304. cItem = DPA_GetPtrCount(this->hdpa);
  305. if (DPA_InsertPtr(this->hdpa, cItem+1, IntToPtr(iItem)) == -1)
  306. goto Add_Fail;
  307. DPA_Sort(this->hdpa, Atom_CompareIndexes, (LPARAM)this->hdsa);
  308. atomRet = pitem->atom;
  309. TRACE_MSG(TF_ATOM, TEXT("ATOM Adding %d [%u]: %s"), atomRet, pitem->ucRef, pitem->psz);
  310. }
  311. }
  312. Add_Fail:
  313. // Add the entry to the free list and fail. If even this fails,
  314. // then we simply lose some slight efficiency, but this is not
  315. // a memory leak.
  316. //
  317. #ifdef DEBUG
  318. if (atomRet == ATOM_ERR)
  319. TRACE_MSG(TF_ATOM, TEXT("ATOM **Failed adding %s"), psz);
  320. #endif
  321. if (atomRet == ATOM_ERR && pitem)
  322. {
  323. ASSERT(iItem != -1);
  324. DPA_InsertPtr(this->hdpaFree, cFree+1, IntToPtr(iItem));
  325. }
  326. }
  327. Atom_LeaveCS(this);
  328. return atomRet;
  329. }
  330. /*----------------------------------------------------------
  331. Purpose: Increment the reference count of this atom.
  332. Returns: Last count
  333. 0 if the atom doesn't exist
  334. Cond: --
  335. */
  336. UINT PUBLIC Atom_AddRef(
  337. int atom)
  338. {
  339. ATOMTABLE * this = &s_atomtable;
  340. UINT cRef;
  341. if (!Atom_IsValid(atom))
  342. {
  343. ASSERT(0);
  344. return 0;
  345. }
  346. VALIDATE_ATOM(atom);
  347. Atom_EnterCS(this);
  348. {
  349. A_ITEM * pitem = DSA_GetItemPtr(this->hdsa, atom);
  350. if (pitem)
  351. {
  352. cRef = pitem->ucRef++;
  353. }
  354. else
  355. {
  356. cRef = 0;
  357. }
  358. }
  359. Atom_LeaveCS(this);
  360. return cRef;
  361. }
  362. /*----------------------------------------------------------
  363. Purpose: Delete a string from the atom table.
  364. If the reference count is not zero, we do nothing.
  365. Returns: --
  366. Cond: N.b. Decrements the reference count.
  367. */
  368. void PUBLIC Atom_Delete(
  369. int atom)
  370. {
  371. ATOMTABLE * this = &s_atomtable;
  372. A_ITEM * pitem;
  373. if (!Atom_IsValid(atom))
  374. {
  375. ASSERT(0);
  376. return;
  377. }
  378. VALIDATE_ATOM(atom);
  379. Atom_EnterCS(this);
  380. {
  381. pitem = DSA_GetItemPtr(this->hdsa, atom);
  382. if (pitem)
  383. {
  384. int idpa;
  385. int cFree;
  386. ASSERT(pitem->atom == atom);
  387. // Is the reference count already at zero?
  388. if (0 == pitem->ucRef)
  389. {
  390. // Yes; somebody is calling Atom_Delete one-too-many times!
  391. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Deleting %d once-too-many!!"),
  392. pitem->atom); )
  393. ASSERT(0);
  394. }
  395. else if (0 == --pitem->ucRef)
  396. {
  397. // Yes
  398. idpa = DPA_GetPtrIndex(this->hdpa, IntToPtr(atom)); // linear search
  399. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Deleting %d: %s"),
  400. pitem->atom, pitem->psz ? pitem->psz : (LPCTSTR)TEXT("NULL")); )
  401. ASSERT(atom == (int)DPA_GetPtr(this->hdpa, idpa));
  402. if (DPA_ERR != idpa)
  403. {
  404. DPA_DeletePtr(this->hdpa, idpa);
  405. ASSERT(pitem->psz);
  406. Str_SetPtr(&pitem->psz, NULL);
  407. DEBUG_CODE( pitem->psz = NULL; )
  408. }
  409. else
  410. {
  411. ASSERT(0); // Should never get here
  412. }
  413. // Add ptr to the free list. If this fails, we simply
  414. // lose some efficiency in reusing this portion of the cache.
  415. // This is not a memory leak.
  416. //
  417. cFree = DPA_GetPtrCount(this->hdpaFree);
  418. DPA_InsertPtr(this->hdpaFree, cFree+1, IntToPtr(atom));
  419. }
  420. }
  421. }
  422. Atom_LeaveCS(this);
  423. }
  424. /*----------------------------------------------------------
  425. Purpose: Replace the string corresponding with the atom with
  426. another string. The atom will not change.
  427. Returns: TRUE on success
  428. Cond: --
  429. */
  430. BOOL PUBLIC Atom_Replace(
  431. int atom,
  432. LPCTSTR pszNew)
  433. {
  434. ATOMTABLE * this = &s_atomtable;
  435. BOOL bRet = FALSE;
  436. A_ITEM * pitem;
  437. ASSERT(pszNew);
  438. if (!Atom_IsValid(atom))
  439. {
  440. return FALSE;
  441. }
  442. VALIDATE_ATOM(atom);
  443. Atom_EnterCS(this);
  444. {
  445. pitem = DSA_GetItemPtr(this->hdsa, atom);
  446. if (pitem)
  447. {
  448. ASSERT(atom == pitem->atom);
  449. ASSERT(pitem->psz);
  450. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Change %d [%u]: %s -> %s"),
  451. atom, pitem->ucRef, pitem->psz, pszNew); )
  452. if (Str_SetPtr(&pitem->psz, pszNew))
  453. {
  454. DPA_Sort(this->hdpa, Atom_CompareIndexes, (LPARAM)this->hdsa);
  455. bRet = TRUE;
  456. }
  457. #ifdef DEBUG
  458. else
  459. TRACE_MSG(TF_ATOM, TEXT("ATOM **Change failed"));
  460. #endif
  461. }
  462. }
  463. Atom_LeaveCS(this);
  464. return bRet;
  465. }
  466. /*----------------------------------------------------------
  467. Purpose: Translate all atoms with that contain the partial
  468. string atomOld with the partial string atomNew.
  469. Returns: TRUE on success
  470. Cond: --
  471. */
  472. BOOL PUBLIC Atom_Translate(
  473. int atomOld,
  474. int atomNew)
  475. {
  476. BOOL bRet = FALSE;
  477. ATOMTABLE * this = &s_atomtable;
  478. A_ITEM * pitem;
  479. int idpa;
  480. int cItem;
  481. int atomSave = 0;
  482. int cchOld;
  483. LPCTSTR psz;
  484. LPCTSTR pszOld;
  485. LPCTSTR pszNew;
  486. LPCTSTR pszRest;
  487. TCHAR sz[MAXPATHLEN];
  488. if ( !(Atom_IsValid(atomOld) && Atom_IsValid(atomNew)) )
  489. {
  490. return FALSE;
  491. }
  492. Atom_EnterCS(this);
  493. {
  494. pszOld = Atom_GetName(atomOld);
  495. cchOld = lstrlen(pszOld);
  496. pszNew = Atom_GetName(atomNew);
  497. cItem = DPA_GetPtrCount(this->hdpa);
  498. for (idpa = 0; idpa < cItem; idpa++)
  499. {
  500. pitem = MyGetPtr(this, idpa);
  501. ASSERT(pitem);
  502. if (pitem->atom == 0)
  503. continue; // skip reserved atom
  504. if (atomOld == pitem->atom)
  505. {
  506. atomSave = pitem->atom; // Save this one for last
  507. continue;
  508. }
  509. psz = Atom_GetName(pitem->atom);
  510. ASSERT(psz);
  511. if (PathIsPrefix(psz, pszOld) && lstrlen(psz) >= cchOld)
  512. {
  513. // Translate this atom
  514. //
  515. pszRest = psz + cchOld; // whack up the path
  516. PathCombine(sz, pszNew, pszRest);
  517. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Translate %d [%u]: %s -> %s"),
  518. pitem->atom, pitem->ucRef, pitem->psz, (LPCTSTR)sz); )
  519. if (!Str_SetPtr(&pitem->psz, sz))
  520. goto Translate_Fail;
  521. }
  522. }
  523. ASSERT(Atom_IsValid(atomSave)); // this means trouble
  524. VALIDATE_ATOM(atomSave);
  525. pitem = DSA_GetItemPtr(this->hdsa, atomSave);
  526. if (pitem)
  527. {
  528. ASSERT(atomSave == pitem->atom);
  529. ASSERT(pitem->psz);
  530. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Translate %d [%u]: %s -> %s"),
  531. pitem->atom, pitem->ucRef, pitem->psz, pszNew); )
  532. if (!Str_SetPtr(&pitem->psz, pszNew))
  533. goto Translate_Fail;
  534. }
  535. bRet = TRUE;
  536. Translate_Fail:
  537. ASSERT(bRet);
  538. // Sort here, even on a fail, so we correctly sort whatever
  539. // got translated before the failure.
  540. //
  541. DPA_Sort(this->hdpa, Atom_CompareIndexes, (LPARAM)this->hdsa);
  542. }
  543. Atom_LeaveCS(this);
  544. return bRet;
  545. }
  546. /*----------------------------------------------------------
  547. Purpose: Search for a string in the atom table and return the atom
  548. Returns: Atom
  549. ATOM_ERR if the string is not in the table
  550. Cond: Reference count is NOT incremented
  551. */
  552. int PUBLIC Atom_Find(
  553. LPCTSTR psz)
  554. {
  555. ATOMTABLE * this = &s_atomtable;
  556. A_ITEM item;
  557. A_ITEM * pitem;
  558. int atomRet = ATOM_ERR;
  559. int idpa;
  560. ASSERT(psz);
  561. Atom_EnterCS(this);
  562. {
  563. item.psz = (LPTSTR)(LPVOID)psz;
  564. idpa = DPA_Search(this->hdpa, &item, 0, Atom_Compare, (LPARAM)this->hdsa,
  565. DPAS_SORTED);
  566. if (idpa != -1)
  567. {
  568. pitem = MyGetPtr(this, idpa);
  569. atomRet = pitem->atom;
  570. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Find %s. Found %d [%u]: %s"),
  571. psz, pitem->atom, pitem->ucRef, pitem->psz); )
  572. ASSERT(IsSzEqual(psz, pitem->psz));
  573. }
  574. #ifdef DEBUG
  575. else
  576. TRACE_MSG(TF_ATOM, TEXT("ATOM **Not found %s"), psz);
  577. #endif
  578. }
  579. Atom_LeaveCS(this);
  580. return atomRet;
  581. }
  582. /*----------------------------------------------------------
  583. Purpose: Get the string for this atom
  584. Returns: Ptr to the string
  585. NULL if the atom is bogus
  586. Cond: The caller must serialize this.
  587. */
  588. LPCTSTR PUBLIC Atom_GetName(
  589. int atom)
  590. {
  591. ATOMTABLE * this = &s_atomtable;
  592. LPCTSTR pszRet = NULL;
  593. A_ITEM * pitem;
  594. VALIDATE_ATOM(atom);
  595. Atom_EnterCS(this);
  596. {
  597. pitem = DSA_GetItemPtr(this->hdsa, atom);
  598. if (pitem)
  599. {
  600. pszRet = pitem->psz;
  601. DEBUG_CODE( TRACE_MSG(TF_ATOM, TEXT("ATOM Getting name %d [%u]: %s"),
  602. atom, pitem->ucRef, pszRet); )
  603. ASSERT(atom == pitem->atom);
  604. }
  605. #ifdef DEBUG
  606. else
  607. TRACE_MSG(TF_ATOM, TEXT("ATOM **Cannot get %d"), atom);
  608. #endif
  609. }
  610. Atom_LeaveCS(this);
  611. return pszRet;
  612. }
  613. /*----------------------------------------------------------
  614. Purpose: Return TRUE if atom2 is a partial path match of atom1.
  615. Returns: boolean
  616. Cond: Requires atom1 and atom2 to be valid.
  617. */
  618. BOOL PUBLIC Atom_IsPartialMatch(
  619. int atom1,
  620. int atom2)
  621. {
  622. LPCTSTR psz1 = Atom_GetName(atom1);
  623. LPCTSTR psz2 = Atom_GetName(atom2);
  624. ASSERT(psz1);
  625. ASSERT(psz2);
  626. return PathIsPrefix(psz2, psz1);
  627. }