Leaked source code of windows server 2003
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.

425 lines
13 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: qwiklist.cpp
  5. //
  6. // Description: Implementation of CQuickList
  7. //
  8. // Author: Mike Swafford (MikeSwa)
  9. //
  10. // History:
  11. // 6/15/98 - MikeSwa Created
  12. //
  13. // Copyright (C) 1998 Microsoft Corporation
  14. //
  15. //-----------------------------------------------------------------------------
  16. #include "aqprecmp.h"
  17. #include "qwiklist.h"
  18. CPool CQuickList::s_QuickListPool;
  19. //---[ CQuickList::CQuickList ]------------------------------------------------
  20. //
  21. //
  22. // Description:
  23. // Default contructor for CQuikList... initializes as head of list
  24. // Parameters:
  25. // -
  26. // Returns:
  27. // -
  28. // History:
  29. // 6/15/98 - MikeSwa Created
  30. //
  31. //-----------------------------------------------------------------------------
  32. CQuickList::CQuickList()
  33. {
  34. m_dwSignature = QUICK_LIST_SIG;
  35. //ASSERT constants
  36. _ASSERT(!(~QUICK_LIST_INDEX_MASK & QUICK_LIST_PAGE_SIZE));
  37. _ASSERT((~QUICK_LIST_INDEX_MASK + 1)== QUICK_LIST_PAGE_SIZE);
  38. m_dwCurrentIndexStart = 0;
  39. InitializeListHead(&m_liListPages);
  40. m_cItems = 0;
  41. ZeroMemory(m_rgpvData, QUICK_LIST_PAGE_SIZE*sizeof(PVOID));
  42. }
  43. //---[ CQuickList::CQuickList ]------------------------------------------------
  44. //
  45. //
  46. // Description:
  47. // Constructor for QQuickList, inserts it into the tail of current list
  48. // Parameters:
  49. //
  50. // Returns:
  51. //
  52. // History:
  53. // 6/15/98 - MikeSwa Created
  54. //
  55. //-----------------------------------------------------------------------------
  56. CQuickList::CQuickList(CQuickList *pqlstHead)
  57. {
  58. _ASSERT(pqlstHead);
  59. _ASSERT(pqlstHead->m_liListPages.Blink);
  60. CQuickList *pqlstTail = CONTAINING_RECORD(pqlstHead->m_liListPages.Blink, CQuickList, m_liListPages);
  61. _ASSERT(QUICK_LIST_SIG == pqlstTail->m_dwSignature);
  62. m_dwSignature = QUICK_LIST_SIG;
  63. m_dwCurrentIndexStart = pqlstTail->m_dwCurrentIndexStart + QUICK_LIST_PAGE_SIZE;
  64. m_cItems = QUICK_LIST_LEAF_PAGE;
  65. ZeroMemory(m_rgpvData, QUICK_LIST_PAGE_SIZE*sizeof(PVOID));
  66. InsertTailList(&(pqlstHead->m_liListPages), &m_liListPages);
  67. }
  68. //---[ CQuickList::~CQuickList ]-----------------------------------------------
  69. //
  70. //
  71. // Description:
  72. // CQuickList destructor
  73. // Parameters:
  74. // -
  75. // Returns:
  76. // -
  77. // History:
  78. // 6/15/98 - MikeSwa Created
  79. //
  80. //-----------------------------------------------------------------------------
  81. CQuickList::~CQuickList()
  82. {
  83. m_dwSignature = QUICK_LIST_SIG_DELETE;
  84. CQuickList *pqlstCurrent = NULL;
  85. CQuickList *pqlstNext = NULL;
  86. if (QUICK_LIST_LEAF_PAGE != m_cItems)
  87. {
  88. //head node... loop through every thing and delete leaf pages
  89. pqlstCurrent = CONTAINING_RECORD(m_liListPages.Flink,
  90. CQuickList, m_liListPages);
  91. while (this != pqlstCurrent)
  92. {
  93. pqlstNext = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink,
  94. CQuickList, m_liListPages);
  95. delete pqlstCurrent;
  96. pqlstCurrent = pqlstNext;
  97. }
  98. }
  99. }
  100. //---[ CQuickList::pvGetItem ]-------------------------------------------------
  101. //
  102. //
  103. // Description:
  104. // Looks up item at given index
  105. // Parameters:
  106. // IN dwIndex Index of item to lookup
  107. // IN OUT ppvContext Context for speeding up lookup
  108. // Returns:
  109. // Value of item at index
  110. // NULL if index is out of ranges
  111. // History:
  112. // 6/15/98 - MikeSwa Created
  113. //
  114. //-----------------------------------------------------------------------------
  115. PVOID CQuickList::pvGetItem(IN DWORD dwIndex, IN OUT PVOID *ppvContext)
  116. {
  117. _ASSERT(ppvContext);
  118. PVOID pvReturn = NULL;
  119. BOOL fSearchForwards = TRUE;
  120. DWORD dwForwardDist = 0;
  121. DWORD dwBackwardDist = 0;
  122. DWORD dwMaxStartingIndex = m_cItems & QUICK_LIST_INDEX_MASK;
  123. CQuickList *pqlstDirection = NULL;
  124. CQuickList *pqlstCurrent = (CQuickList *) *ppvContext;
  125. CQuickList *pqlstSentinal = NULL;
  126. DWORD cDbgItems = m_cItems;
  127. if (dwIndex >= m_cItems)
  128. return NULL;
  129. if (!pqlstCurrent)
  130. pqlstCurrent = this;
  131. pqlstSentinal = pqlstCurrent;
  132. //short circuit direction logic
  133. if (pqlstCurrent->fIsIndexOnThisPage(dwIndex))
  134. {
  135. pvReturn = pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK];
  136. *ppvContext = pqlstCurrent;
  137. _ASSERT((dwIndex < m_cItems) || (NULL == pvReturn));
  138. goto Exit;
  139. }
  140. //determine which direction to go in (we want to traverse the smallest # of pages
  141. //possible
  142. pqlstDirection = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink, CQuickList, m_liListPages);
  143. if (dwIndex > pqlstDirection->m_dwCurrentIndexStart)
  144. dwForwardDist = dwIndex - pqlstDirection->m_dwCurrentIndexStart;
  145. else
  146. dwForwardDist = pqlstDirection->m_dwCurrentIndexStart - dwIndex;
  147. pqlstDirection = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Blink, CQuickList, m_liListPages);
  148. if (dwIndex > pqlstDirection->m_dwCurrentIndexStart)
  149. dwBackwardDist = dwIndex - pqlstDirection->m_dwCurrentIndexStart;
  150. else
  151. dwBackwardDist = pqlstDirection->m_dwCurrentIndexStart - dwIndex;
  152. //fix up distances to account for going through the 0th page
  153. //max distance is dwMaxStartingIndex/2
  154. if (dwBackwardDist > dwMaxStartingIndex/2)
  155. dwBackwardDist -= dwMaxStartingIndex;
  156. if (dwForwardDist > dwMaxStartingIndex/2)
  157. dwForwardDist -= dwMaxStartingIndex;
  158. if (dwForwardDist > dwBackwardDist)
  159. fSearchForwards = FALSE;
  160. //$$NOTE: current lookup time is O(lg base{QUICK_LIST_PAGE_BASE} (n))/2.
  161. //Consecutive lookups will be O(1) (because of the hints)
  162. do
  163. {
  164. if (fSearchForwards)
  165. {
  166. //going forward is quicker
  167. pqlstCurrent = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Flink, CQuickList, m_liListPages);
  168. }
  169. else
  170. {
  171. //going backwards is quicker
  172. pqlstCurrent = CONTAINING_RECORD(pqlstCurrent->m_liListPages.Blink, CQuickList, m_liListPages);
  173. }
  174. _ASSERT(QUICK_LIST_SIG == pqlstCurrent->m_dwSignature);
  175. if (pqlstCurrent->fIsIndexOnThisPage(dwIndex))
  176. {
  177. pvReturn = pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK];
  178. _ASSERT((dwIndex < m_cItems) || (NULL == pvReturn));
  179. break;
  180. }
  181. } while (pqlstSentinal != pqlstCurrent); //stop when we return to list head
  182. *ppvContext = pqlstCurrent;
  183. _ASSERT((cDbgItems == m_cItems) && "Non-threadsafe access to CQuickList");
  184. Exit:
  185. return pvReturn;
  186. }
  187. //---[ CQuickList::pvDeleteItem ]----------------------------------------------
  188. //
  189. //
  190. // Description:
  191. // Will remove item at a given index from the quick list.
  192. //
  193. // If you need to determine the data at that has been moved into this
  194. // index, call pvGetItem with the returned context, and it will return
  195. // without needing to search.
  196. // Parameters:
  197. // IN dwIndex Index of item to lookup
  198. // IN OUT ppvContext Context for speeding up lookup
  199. // Returns:
  200. // Value of item that was removed.
  201. // NULL if index is out of ranges
  202. // History:
  203. // 9/10/98 - MikeSwa Created
  204. // 11/5/98 - MikeSwa Fixed problem about not always marking deleted
  205. // entry NULL.
  206. //
  207. //-----------------------------------------------------------------------------
  208. PVOID CQuickList::pvDeleteItem(IN DWORD dwIndex, IN OUT PVOID *ppvContext)
  209. {
  210. PVOID pvItem = NULL;
  211. PVOID pvLast = NULL; //last item in list
  212. CQuickList *pqlstCurrent = NULL; //page that index is on
  213. CQuickList *pqlstLast = NULL; //last page
  214. DWORD dwLastIndex = m_cItems - 1;
  215. //Check to make sure that the index is valid
  216. if (dwIndex >= m_cItems)
  217. goto Exit;
  218. //Use single function to look up index and find quick list page
  219. pvItem = pvGetItem(dwIndex, (PVOID *) &pqlstCurrent);
  220. _ASSERT(pvItem); //we checked the index... and we cannot insert NULL ptrs
  221. _ASSERT(QUICK_LIST_SIG == pqlstCurrent->m_dwSignature);
  222. _ASSERT(pqlstCurrent->fIsIndexOnThisPage(dwIndex));
  223. //Now that we know the index and the page... we can move that last
  224. //entry in the list to this index.
  225. pqlstLast = CONTAINING_RECORD(m_liListPages.Blink, CQuickList, m_liListPages);
  226. _ASSERT(QUICK_LIST_SIG == pqlstLast->m_dwSignature);
  227. _ASSERT(pqlstLast->fIsIndexOnThisPage(dwLastIndex));
  228. if (dwLastIndex != dwIndex)
  229. {
  230. //In general if we are not deleting the last entry... we need to move
  231. //the last entry to the current index
  232. pvLast = pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK];
  233. _ASSERT(pvLast); //shouldn't be NULL!
  234. //Now that we have last item... write it!
  235. pqlstCurrent->m_rgpvData[dwIndex & ~QUICK_LIST_INDEX_MASK] = pvLast;
  236. //NULL the old last entry
  237. pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK] = NULL;
  238. }
  239. else
  240. {
  241. //if we deleted that last entry... the current and last pages
  242. //should be the same
  243. _ASSERT(pqlstLast == pqlstCurrent);
  244. //Set emptied data pointer to NULL
  245. pqlstLast->m_rgpvData[dwLastIndex & ~QUICK_LIST_INDEX_MASK] = NULL;
  246. }
  247. //Decrement total count
  248. m_cItems--;
  249. _ASSERT(QUICK_LIST_LEAF_PAGE != m_cItems);
  250. //Determine if it is neccessary to delete the last page
  251. if (!(dwLastIndex & ~QUICK_LIST_INDEX_MASK) && m_cItems)
  252. {
  253. //dwLastIndex was the only entry on the last page & it wasn't the head page
  254. //Unless the above test is wrong... the last page is *not* the head page
  255. _ASSERT(QUICK_LIST_LEAF_PAGE == pqlstLast->m_cItems);
  256. _ASSERT(this != pqlstLast);
  257. //Remove from list
  258. RemoveEntryList(&(pqlstLast->m_liListPages));
  259. if (pqlstCurrent == pqlstLast) //we cannot return a deleted context
  260. pqlstCurrent = this;
  261. delete pqlstLast;
  262. }
  263. //Safety check to make sure another thread hasn't come along
  264. _ASSERT(m_cItems == dwLastIndex);
  265. Exit:
  266. if (ppvContext)
  267. *ppvContext = pqlstCurrent;
  268. return pvItem;
  269. }
  270. //---[ CQuickList::HrAppendItem ]-----------------------------------------------
  271. //
  272. //
  273. // Description:
  274. // Appends new data item to end of array
  275. // Parameters:
  276. // IN pvData - Data to insert
  277. // OUT pdwIndex - Index data was inserted at
  278. // Returns:
  279. // E_OUTOFMEMORY if unable to allocate another page
  280. // E_INVALIDARG if pvData is NULL
  281. // History:
  282. // 6/15/98 - MikeSwa Created
  283. // 9/9/98 - MikeSwa - Added pdwIndex OUT param
  284. //
  285. //-----------------------------------------------------------------------------
  286. HRESULT CQuickList::HrAppendItem(IN PVOID pvData, OUT DWORD *pdwIndex)
  287. {
  288. HRESULT hr = S_OK;
  289. CQuickList *pqlstCurrent = NULL;
  290. _ASSERT(pvData && "Cannot insert NULL pointers");
  291. if (!pvData)
  292. {
  293. hr = E_INVALIDARG;
  294. goto Exit;
  295. }
  296. if (m_cItems && !(m_cItems & ~QUICK_LIST_INDEX_MASK)) //on page boundary
  297. {
  298. //there is not room on the last page
  299. pqlstCurrent = new CQuickList(this);
  300. if (!pqlstCurrent)
  301. {
  302. hr = E_OUTOFMEMORY;
  303. goto Exit;
  304. }
  305. }
  306. else if (!(m_cItems & QUICK_LIST_INDEX_MASK))
  307. {
  308. pqlstCurrent = this;
  309. }
  310. else
  311. {
  312. pqlstCurrent = CONTAINING_RECORD(m_liListPages.Blink, CQuickList, m_liListPages);
  313. }
  314. _ASSERT(pqlstCurrent->fIsIndexOnThisPage(m_cItems));
  315. pqlstCurrent->m_rgpvData[m_cItems & ~QUICK_LIST_INDEX_MASK] = pvData;
  316. //Set OUT param to index (before we increment the count)
  317. if (pdwIndex)
  318. *pdwIndex = m_cItems;
  319. m_cItems++;
  320. _ASSERT(QUICK_LIST_LEAF_PAGE != m_cItems);
  321. Exit:
  322. return hr;
  323. }
  324. //---[ CQuickList::Clone ]-----------------------------------------------------
  325. //
  326. //
  327. // Description:
  328. // Copies the contents of this CQuickList into a new CQuickList - caller
  329. // is responsible for deleting the CQuickList we create here
  330. // Parameters:
  331. // IN / OUT pqlClone - pointer to destination CQuickList pointer
  332. // Returns:
  333. // S_OK - cloned successfully
  334. // E_OUTOFMEMORY - failed to allocate CQuickList
  335. // History:
  336. // 11/9/2000 - dbraun - created
  337. //
  338. //-----------------------------------------------------------------------------
  339. HRESULT CQuickList::Clone (CQuickList **ppqlClone)
  340. {
  341. HRESULT hr = S_OK;
  342. DWORD dwIndex = 0;
  343. PVOID pvItem = NULL;
  344. PVOID pvContext = NULL;
  345. DWORD dwNewIndex = 0;
  346. CQuickList *pql = NULL;
  347. _ASSERT(ppqlClone);
  348. pql = new CQuickList();
  349. if (!pql)
  350. {
  351. hr = E_OUTOFMEMORY;
  352. goto Exit;
  353. }
  354. // Now copy the contents of this CQuickList
  355. for (dwIndex = 0; dwIndex < m_cItems; dwIndex++)
  356. {
  357. pvItem = pvGetItem(dwIndex, &pvContext);
  358. _ASSERT(pvItem);
  359. hr = pql->HrAppendItem(pvItem, &dwNewIndex);
  360. if (FAILED(hr))
  361. goto Exit; // Report the failure to our caller
  362. }
  363. *ppqlClone = pql;
  364. pql = NULL;
  365. Exit:
  366. if (pql)
  367. delete pql;
  368. return hr;
  369. }