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.

645 lines
14 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module - FORMAT.C
  5. * CCharFormatArray and CParaFormatArray classes |
  6. *
  7. * Authors:
  8. * Original RichEdit code: David R. Fulmer
  9. * Christian Fortini
  10. * Murray Sargent
  11. *
  12. * Copyright (c) 1995-2000, Microsoft Corporation. All rights reserved.
  13. */
  14. #include "_common.h"
  15. #include "_format.h"
  16. ASSERTDATA
  17. // =============================== CFixArrayBase =================================
  18. CFixArrayBase::CFixArrayBase(
  19. LONG cbElem)
  20. {
  21. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::CFixArrayBase");
  22. _prgel = NULL;
  23. _cel = 0;
  24. _ielFirstFree = 0;
  25. #ifdef _WIN64
  26. // Make sure each element + Ref. count is 64-bit aligned.
  27. LONG cbTemp = (cbElem + 4) & 7; // Do a Mod 8
  28. _cbPad = 0;
  29. if (cbTemp) // Need padding?
  30. _cbPad = 8 - cbTemp;
  31. #endif
  32. _cbElem = cbElem + 4 + _cbPad; // 4 is for reference count
  33. }
  34. /*
  35. * CFixArrayBase::Add()
  36. *
  37. * @mfunc
  38. * Return index of new element, reallocing if necessary
  39. *
  40. * @rdesc
  41. * Index of new element.
  42. *
  43. * @comm
  44. * Free elements are maintained in place as a linked list indexed
  45. * by a chain of ref-count entries with their sign bits set and the
  46. * rest of the entry giving the index of the next element on the
  47. * free list. The list is terminated by a 0 entry. This approach
  48. * enables element 0 to be on the free list.
  49. */
  50. LONG CFixArrayBase::Add()
  51. {
  52. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Add");
  53. char *pel;
  54. LONG iel, ielRet;
  55. if(_ielFirstFree) // Return first element of free list
  56. {
  57. ielRet = _ielFirstFree & ~FLBIT;
  58. _ielFirstFree = RefCount(ielRet);
  59. }
  60. else // All lower positions taken: need
  61. { // to add another celGrow elements
  62. pel = (char*)PvReAlloc(_prgel, (_cel + celGrow) * _cbElem);
  63. if(!pel)
  64. return -1;
  65. // Clear out the *end* of the newly allocated memory
  66. ZeroMemory(pel + _cel*_cbElem, celGrow*_cbElem);
  67. _prgel = pel;
  68. ielRet = _cel; // Return first one added
  69. iel = _cel + 1;
  70. _cel += celGrow;
  71. // Add elements _cel+1 thru _cel+celGrow-1 to free list. The last
  72. // of these retains a 0, stored by fZeroFill in Alloc
  73. _ielFirstFree = iel | FLBIT;
  74. for(pel = (char *)&RefCount(iel);
  75. ++iel < _cel;
  76. pel += _cbElem)
  77. {
  78. *(INT *)pel = iel | FLBIT;
  79. }
  80. }
  81. return ielRet;
  82. }
  83. void CFixArrayBase::Free(
  84. LONG iel)
  85. {
  86. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Free(iel)");
  87. // Simply add it to free list
  88. RefCount(iel) = _ielFirstFree;
  89. _ielFirstFree = iel | FLBIT;
  90. }
  91. void CFixArrayBase::Free()
  92. {
  93. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::Free()");
  94. #if defined(DEBUG) && !defined(NOFULLDEBUG)
  95. // Only do this validation if all the ped's are gone. Visual basic shutsdown apps
  96. // without freeing all the resources so this safety check is necessary.
  97. if (0 == W32->GetRefs())
  98. {
  99. // Display message if any CCharFormats, CParaFormats, or CTabs have
  100. // reference counts > 0. This only happens if an error has occurred.
  101. // Since this is called as the RichEdit dll is unloading, we can't use
  102. // the usual AssertSz() macros.
  103. BOOL fComplained = FALSE;
  104. for(LONG iel = 0; iel < Count(); iel++)
  105. {
  106. while(RefCount(iel) > 0)
  107. {
  108. if (!fComplained)
  109. {
  110. fComplained = TRUE;
  111. AssertSz(FALSE, (_cbElem - _cbPad) == sizeof(CCharFormat) + 4 ? "CCharFormat not free" :
  112. (_cbElem - _cbPad) == sizeof(CParaFormat) + 4 ? "CParaFormat not free" :
  113. "CTabs not free");
  114. }
  115. Release(iel);
  116. }
  117. }
  118. }
  119. #endif
  120. FreePv(_prgel);
  121. _prgel = NULL;
  122. _cel = 0;
  123. _ielFirstFree = 0;
  124. }
  125. HRESULT CFixArrayBase::Deref(
  126. LONG iel,
  127. const void **ppel) const
  128. {
  129. Assert(ppel);
  130. AssertSz(iel >= 0,
  131. "CFixArrayBase::Deref: bad element index" );
  132. AssertSz(*(LONG *)(_prgel + (iel + 1) * _cbElem - 4) > 0,
  133. "CFixArrayBase::Deref: element index has bad ref count");
  134. if(!ppel)
  135. return E_INVALIDARG;
  136. *ppel = Elem(iel);
  137. return S_OK;
  138. }
  139. /*
  140. * CFixArrayBase::RefCount(iel)
  141. *
  142. * @mfunc
  143. * The reference count for an element is stored as a LONG immediately
  144. * following the element in the CFixArray. If the element isn't used
  145. * i.e., is free, then the reference count is used as a link to the
  146. * next free element. The last free element in this list has a 0
  147. * "reference count", which terminates the list.
  148. *
  149. * The ref count follows the element instead of preceding it because
  150. * this allows Elem(iel) to avoid an extra addition. Elem() is used
  151. * widely in the code.
  152. *
  153. * @rdesc
  154. * Ptr to reference count
  155. */
  156. LONG & CFixArrayBase::RefCount(
  157. LONG iel)
  158. {
  159. Assert(iel < Count());
  160. return (LONG &)(*(_prgel + (iel + 1) * _cbElem - 4));
  161. }
  162. LONG CFixArrayBase::Release(
  163. LONG iel)
  164. {
  165. LONG cRef = -1;
  166. if(iel >= 0) // Ignore default iel
  167. {
  168. CLock lock;
  169. CheckFreeChain();
  170. AssertSz(RefCount(iel) > 0, "CFixArrayBase::Release(): already free");
  171. cRef = --RefCount(iel);
  172. if(!cRef) // Entry no longer referenced
  173. Free(iel); // Add it to the free chain
  174. }
  175. return cRef;
  176. }
  177. LONG CFixArrayBase::AddRef(
  178. LONG iel)
  179. {
  180. LONG cRef = -1;
  181. if(iel >= 0)
  182. {
  183. CLock lock;
  184. CheckFreeChain();
  185. AssertSz(RefCount(iel) > 0, "CFixArrayBase::AddRef(): add ref to free elem");
  186. cRef = ++RefCount(iel);
  187. }
  188. return cRef;
  189. }
  190. LONG CFixArrayBase::Find(
  191. const void *pel)
  192. {
  193. CheckFreeChain();
  194. for(LONG iel = 0; iel < Count(); iel++)
  195. {
  196. // RefCount < 0 means entry not in use and is index of next free entry.
  197. // RefCount = 0 marks last free element in list. _cbElem = sizeof(ELEM)
  198. // plus sizeof(RefCount), which is a LONG.
  199. if (RefCount(iel) > 0 &&
  200. !CompareMemory(Elem(iel), pel, _cbElem - sizeof(LONG) - _cbPad))
  201. {
  202. return iel;
  203. }
  204. }
  205. return -1;
  206. }
  207. HRESULT CFixArrayBase::Cache(
  208. const void *pel,
  209. LONG * piel)
  210. {
  211. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Cache");
  212. CLock lock;
  213. LONG iel = Find(pel);
  214. if(iel >= 0)
  215. RefCount(iel)++;
  216. else
  217. {
  218. iel = Add();
  219. if(iel < 0)
  220. return E_OUTOFMEMORY;
  221. CopyMemory(Elem(iel), pel, _cbElem - sizeof(LONG) - _cbPad);
  222. RefCount(iel) = 1;
  223. }
  224. CheckFreeChain();
  225. if(piel)
  226. *piel = iel;
  227. return S_OK;
  228. }
  229. #ifdef DEBUG
  230. void CFixArrayBase::CheckFreeChainFn(
  231. LPSTR szFile,
  232. INT nLine)
  233. {
  234. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CFixArrayBase::CheckFreeChainFn");
  235. LONG cel = 0;
  236. LONG iel = _ielFirstFree;
  237. LONG ielT;
  238. while(iel)
  239. {
  240. Assert(iel < 0);
  241. ielT = RefCount(iel & ~FLBIT);
  242. if((LONG)(ielT & ~FLBIT) > _cel)
  243. Tracef(TRCSEVERR, "AttCheckFreeChainCF(): elem %ld points to out of range elem %ld", iel, ielT);
  244. iel = ielT;
  245. if(++cel > _cel)
  246. {
  247. AssertSzFn("CFixArrayBase::CheckFreeChain() - CF free chain seems to contain an infinite loop", szFile, nLine);
  248. return;
  249. }
  250. }
  251. }
  252. #endif
  253. // =========================== CCharFormatArray ===========================================
  254. HRESULT CCharFormatArray::Deref(
  255. LONG iCF,
  256. const CCharFormat **ppCF) const
  257. {
  258. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Deref");
  259. return CFixArrayBase::Deref(iCF, (const void **)ppCF);
  260. }
  261. LONG CCharFormatArray::Release(
  262. LONG iCF)
  263. {
  264. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::ReleaseFormat");
  265. return CFixArrayBase::Release(iCF);
  266. }
  267. LONG CCharFormatArray::AddRef(
  268. LONG iCF)
  269. {
  270. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::AddRefFormat");
  271. return CFixArrayBase::AddRef(iCF);
  272. }
  273. void CCharFormatArray::Destroy()
  274. {
  275. delete this;
  276. }
  277. LONG CCharFormatArray::Find(
  278. const CCharFormat *pCF)
  279. {
  280. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Find");
  281. LONG iCF;
  282. #define QUICKCRCSEARCHSIZE 15 // Must be 2^n - 1 for quick MOD
  283. // operation, it is a simple hash.
  284. static struct {
  285. BYTE bCRC;
  286. LONG iCF;
  287. } quickCrcSearch[QUICKCRCSEARCHSIZE+1];
  288. BYTE bCRC;
  289. WORD hashKey;
  290. CheckFreeChain();
  291. // Check our cache before going sequential
  292. bCRC = (BYTE)pCF->_iFont;
  293. hashKey = (WORD)(bCRC & QUICKCRCSEARCHSIZE);
  294. if(bCRC == quickCrcSearch[hashKey].bCRC)
  295. {
  296. iCF = quickCrcSearch[hashKey].iCF - 1;
  297. if (iCF >= 0 && iCF < Count() && RefCount(iCF) > 0 &&
  298. !CompareMemory(Elem(iCF), pCF, sizeof(CCharFormat)))
  299. {
  300. return iCF;
  301. }
  302. }
  303. for(iCF = 0; iCF < Count(); iCF++)
  304. {
  305. if(RefCount(iCF) > 0 && !CompareMemory(Elem(iCF), pCF, sizeof(CCharFormat)))
  306. {
  307. quickCrcSearch[hashKey].bCRC = bCRC;
  308. quickCrcSearch[hashKey].iCF = iCF + 1;
  309. return iCF;
  310. }
  311. }
  312. return -1;
  313. }
  314. HRESULT CCharFormatArray::Cache(
  315. const CCharFormat *pCF,
  316. LONG* piCF)
  317. {
  318. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CCharFormatArray::Cache");
  319. CLock lock;
  320. LONG iCF = Find(pCF);
  321. if(iCF >= 0)
  322. RefCount(iCF)++;
  323. else
  324. {
  325. iCF = Add();
  326. if(iCF < 0)
  327. return E_OUTOFMEMORY;
  328. *Elem(iCF) = *pCF; // Set entry iCF to *pCF
  329. RefCount(iCF) = 1;
  330. }
  331. CheckFreeChain();
  332. if(piCF)
  333. *piCF = iCF;
  334. return S_OK;
  335. }
  336. // =============================== CParaFormatArray ===========================================
  337. HRESULT CParaFormatArray::Deref(
  338. LONG iPF,
  339. const CParaFormat **ppPF) const
  340. {
  341. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Deref");
  342. return CFixArrayBase::Deref(iPF, (const void **)ppPF);
  343. }
  344. LONG CParaFormatArray::Release(
  345. LONG iPF)
  346. {
  347. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::ReleaseFormat");
  348. CLock lock;
  349. LONG cRef = CFixArrayBase::Release(iPF);
  350. #ifdef TABS
  351. if(!cRef)
  352. GetTabsCache()->Release(Elem(iPF)->_iTabs);
  353. #endif
  354. return cRef;
  355. }
  356. LONG CParaFormatArray::AddRef(
  357. LONG iPF)
  358. {
  359. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::AddRefFormat");
  360. return CFixArrayBase::AddRef(iPF);
  361. }
  362. void CParaFormatArray::Destroy()
  363. {
  364. delete this;
  365. }
  366. HRESULT CParaFormatArray::Cache(
  367. const CParaFormat *pPF,
  368. LONG *piPF)
  369. {
  370. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CParaFormatArray::Cache");
  371. HRESULT hr = CFixArrayBase::Cache((const void *)pPF, piPF);
  372. #ifdef TABS
  373. if(hr == NOERROR && RefCount(*piPF) == 1)
  374. GetTabsCache()->AddRef(pPF->_iTabs);
  375. #endif
  376. return hr;
  377. }
  378. // =============================== CTabsArray ===========================================
  379. CTabsArray::~CTabsArray()
  380. {
  381. for(LONG iTabs = 0; iTabs < Count(); iTabs++)
  382. {
  383. // It shouldn't be necessary to release any tabs, since when all
  384. // controls are gone, no reference counts should be > 0.
  385. while(RefCount(iTabs) > 0)
  386. {
  387. #ifdef DEBUG
  388. // Only do this validation if all the ped's are gone. Visual basic shutsdown apps
  389. // without freeing all the resources so this safety check is necessary.
  390. AssertSz(0 != W32->GetRefs(), "CTabs not free");
  391. #endif
  392. Release(iTabs);
  393. }
  394. }
  395. }
  396. const LONG *CTabsArray::Deref(
  397. LONG iTabs) const
  398. {
  399. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Deref");
  400. return iTabs >= 0 ? Elem(iTabs)->_prgxTabs : NULL;
  401. }
  402. LONG CTabsArray::Release(
  403. LONG iTabs)
  404. {
  405. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Release");
  406. LONG cRef = CFixArrayBase::Release(iTabs);
  407. if(!cRef)
  408. FreePv(Elem(iTabs)->_prgxTabs);
  409. return cRef;
  410. }
  411. LONG CTabsArray::AddRef(
  412. LONG iTabs)
  413. {
  414. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::AddRef");
  415. return CFixArrayBase::AddRef(iTabs);
  416. }
  417. LONG CTabsArray::Find(
  418. const LONG *prgxTabs, //@parm Array of tab/cell data
  419. LONG cTab) //@parm # tabs or LONGs in cells
  420. {
  421. CheckFreeChain();
  422. CTabs *pTab;
  423. LONG cb = cTab*sizeof(LONG);
  424. for(LONG iel = 0; iel < Count(); iel++)
  425. {
  426. // RefCount < 0 means entry not in use and is index of next free entry.
  427. // RefCount = 0 marks last free element in list. _cbElem = sizeof(ELEM)
  428. // plus sizeof(RefCount), which is a LONG.
  429. if(RefCount(iel) > 0)
  430. {
  431. pTab = Elem(iel);
  432. if (pTab->_cTab == cTab &&
  433. !CompareMemory(pTab->_prgxTabs, prgxTabs, cb))
  434. {
  435. return iel;
  436. }
  437. }
  438. }
  439. return -1;
  440. }
  441. LONG CTabsArray::Cache(
  442. const LONG *prgxTabs, //@parm Array of tab/cell data
  443. LONG cTab) //@parm # tabs or LONGs in cells
  444. {
  445. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CTabsArray::Cache");
  446. if(!cTab)
  447. return -1; // No tabs defined: use default
  448. CLock lock;
  449. LONG iTabs = Find(prgxTabs, cTab);
  450. if(iTabs >= 0)
  451. RefCount(iTabs)++;
  452. else
  453. {
  454. iTabs = Add();
  455. if(iTabs < 0) // Out of memory: use default
  456. return -1;
  457. CTabs *pTabs = Elem(iTabs);
  458. LONG cb = sizeof(LONG)*cTab;
  459. pTabs->_prgxTabs = (LONG *)PvAlloc(cb, GMEM_FIXED);
  460. if(!pTabs->_prgxTabs)
  461. return -1; // Out of memory: use default
  462. CopyMemory(pTabs->_prgxTabs, prgxTabs, cb);
  463. pTabs->_cTab = cTab;
  464. RefCount(iTabs) = 1;
  465. }
  466. return iTabs;
  467. }
  468. // ================================== Factories ===========================================
  469. static ICharFormatCache *pCFCache = NULL; // CCharFormat cache
  470. static IParaFormatCache *pPFCache = NULL; // CParaFormat cache
  471. static CTabsArray * pTabsCache = NULL; // CTabs cache
  472. ICharFormatCache *GetCharFormatCache()
  473. {
  474. return pCFCache;
  475. }
  476. IParaFormatCache *GetParaFormatCache()
  477. {
  478. return pPFCache;
  479. }
  480. CTabsArray *GetTabsCache()
  481. {
  482. return pTabsCache;
  483. }
  484. HRESULT CreateFormatCaches() // Called by DllMain()
  485. {
  486. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "CreateFormatCaches");
  487. CLock lock;
  488. pCFCache = new CCharFormatArray();
  489. if(!pCFCache)
  490. return E_OUTOFMEMORY;
  491. pPFCache = new CParaFormatArray();
  492. if(!pPFCache)
  493. {
  494. delete pCFCache;
  495. return E_OUTOFMEMORY;
  496. }
  497. pTabsCache = new CTabsArray();
  498. if(!pTabsCache)
  499. {
  500. delete pCFCache;
  501. delete pPFCache;
  502. return E_OUTOFMEMORY;
  503. }
  504. return S_OK;
  505. }
  506. HRESULT DestroyFormatCaches() // Called by DllMain()
  507. {
  508. TRACEBEGIN(TRCSUBSYSBACK, TRCSCOPEINTERN, "DeleteFormatCaches");
  509. if (pCFCache)
  510. pCFCache->Destroy();
  511. if (pPFCache)
  512. pPFCache->Destroy();
  513. if (pTabsCache)
  514. delete pTabsCache;
  515. return NOERROR;
  516. }
  517. /*
  518. * ReleaseFormats(iCF, iPF)
  519. *
  520. * @mfunc
  521. * Release char and para formats corresponding to the indices <p iCF>
  522. * and <p iPF>, respectively
  523. */
  524. void ReleaseFormats (
  525. LONG iCF, //@parm CCharFormat index for releasing
  526. LONG iPF) //@parm CParaFormat index for releasing
  527. {
  528. AssertSz(pCFCache && pPFCache,
  529. "ReleaseFormats: uninitialized format caches");
  530. if (iCF != -1)
  531. pCFCache->Release(iCF);
  532. if (iPF != -1)
  533. pPFCache->Release(iPF);
  534. }