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.

533 lines
12 KiB

  1. /*---------------------------------------------------------------------------
  2. File: VarMapIndex.cpp
  3. Comments: Helper class for CMapStringToVar.
  4. CIndexTree implements a sorted, balanced binary tree. This is used by CMapStringToVar
  5. to provide enumeration in sorted order by key.
  6. CIndexTree is currently implemented as a Red-Black tree.
  7. (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  8. Proprietary and confidential to Mission Critical Software, Inc.
  9. REVISION LOG ENTRY
  10. Revision By: Christy Boles
  11. Revised on 11/19/98 18:17:47
  12. ---------------------------------------------------------------------------
  13. */
  14. #include "stdafx.h"
  15. #include "VarMap.h"
  16. #include "VarNdx.h"
  17. #ifdef STRIPPED_VARSET
  18. #include "NoMcs.h"
  19. #else
  20. #pragma warning (push,3)
  21. #include "McString.h"
  22. #include "McLog.h"
  23. #pragma warning (pop)
  24. using namespace McString;
  25. #endif
  26. #ifdef _DEBUG
  27. #define new DEBUG_NEW
  28. #undef THIS_FILE
  29. static char THIS_FILE[] = __FILE__;
  30. #endif
  31. // Comparison functions used for sorting and searching
  32. int CompareItems(CIndexItem* i1, CIndexItem* i2)
  33. {
  34. ASSERT(i1 && i2);
  35. int result;
  36. result = i1->GetKey().Compare(i2->GetKey());
  37. return result;
  38. }
  39. int CompareStringToItem(CString s, CIndexItem *i)
  40. {
  41. ASSERT(i);
  42. int result;
  43. result = s.Compare(i->GetKey());
  44. return result;
  45. }
  46. int CompareItemsNoCase(CIndexItem* i1, CIndexItem* i2)
  47. {
  48. ASSERT(i1 && i2);
  49. int result;
  50. result = i1->GetKey().CompareNoCase(i2->GetKey());
  51. return result;
  52. }
  53. int CompareStringToItemNoCase(CString s, CIndexItem *i)
  54. {
  55. ASSERT(i);
  56. int result;
  57. // this assumes i->Data is not null
  58. result = s.CompareNoCase(i->GetKey());
  59. return result;
  60. }
  61. CVarData *
  62. CIndexItem::GetValue()
  63. {
  64. if ( pData )
  65. {
  66. return pData->value;
  67. }
  68. else
  69. {
  70. return NULL;
  71. }
  72. }
  73. CString
  74. CIndexItem::GetKey()
  75. {
  76. if ( pData )
  77. {
  78. return pData->key;
  79. }
  80. else
  81. {
  82. return _T("");
  83. }
  84. }
  85. /////////////////////////////////////////////////////////////////////////////
  86. /// Implementation of Red-Black Tree
  87. CIndexItem * // ret- pointer to node in index
  88. CIndexTree::Insert(
  89. CHashItem * data // in - item from hash table
  90. )
  91. {
  92. CIndexItem * item = new CIndexItem(data);
  93. CIndexItem * curr;
  94. CIndexItem * parent;
  95. int compResult=0;
  96. if ( ! m_root )
  97. {
  98. m_root = item;
  99. }
  100. else
  101. {
  102. curr = m_root;
  103. parent = NULL;
  104. while ( curr )
  105. {
  106. parent = curr;
  107. compResult = (*m_Compare)(item,curr);
  108. if ( compResult < 0 )
  109. {
  110. curr = curr->Left();
  111. }
  112. else if ( compResult > 0 )
  113. {
  114. curr = curr->Right();
  115. }
  116. else
  117. {
  118. // The same key should not appear multiple times in the hash table
  119. // this is a bug
  120. ASSERT(FALSE);
  121. delete item;
  122. curr->Data(data);
  123. }
  124. }
  125. if ( ! curr )
  126. {
  127. // The item was not in the tree
  128. ASSERT(compResult!=0);
  129. item->Parent(parent);
  130. // Add the item in the appropriate place
  131. if ( compResult < 0 )
  132. {
  133. parent->Left(item);
  134. }
  135. else
  136. {
  137. parent->Right(item);
  138. }
  139. // now rebalance the tree
  140. CIndexItem * uncle;
  141. BOOL uncleIsRight;
  142. item->Black();
  143. while ( item != m_root && parent->IsRed() )
  144. {
  145. // we don't have to worry about grandparent being null, since parent is red, and
  146. // the root is always black.
  147. // is the parent a left or right child? (algorithm is symmetric)
  148. if ( parent == parent->Parent()->Left() )
  149. {
  150. uncle = parent->Parent()->Right();
  151. uncleIsRight = TRUE;
  152. }
  153. else
  154. {
  155. uncle = parent->Parent()->Left();
  156. uncleIsRight = FALSE;
  157. }
  158. if ( uncleIsRight )
  159. {
  160. if ( uncle && uncle->IsRed() )
  161. {
  162. parent->Black();
  163. uncle->Black();
  164. item = parent->Parent();
  165. item->Red();
  166. }
  167. else if ( item == parent->Right() )
  168. {
  169. item = parent;
  170. LeftRotate(item);
  171. }
  172. parent->Black();
  173. parent->Parent()->Red();
  174. RightRotate(parent->Parent());
  175. }
  176. else // same as above, except swap left and right
  177. {
  178. if ( uncle && uncle->IsRed() )
  179. {
  180. parent->Black();
  181. uncle->Black();
  182. item = parent->Parent();
  183. item->Red();
  184. }
  185. else if ( item == parent->Left() )
  186. {
  187. item = parent;
  188. RightRotate(item);
  189. }
  190. parent->Black();
  191. parent->Parent()->Red();
  192. LeftRotate(parent->Parent());
  193. }
  194. }
  195. }
  196. }
  197. m_root->Black(); // see, the root is always black
  198. return item;
  199. }
  200. void
  201. CIndexTree::RightRotate(
  202. CIndexItem * item // in - item to rotate from
  203. )
  204. {
  205. CIndexItem * y = item->Right();
  206. if ( y )
  207. {
  208. // turn y's left subtree into x's right subtree
  209. item->Right(y->Left());
  210. if ( y->Left() )
  211. {
  212. y->Left()->Parent(item);
  213. }
  214. y->Parent(item->Parent()); // link item's parent to y
  215. if (! item->Parent() )
  216. {
  217. m_root = y;
  218. }
  219. else if ( item == item->Parent()->Left() )
  220. {
  221. item->Parent()->Left(y);
  222. }
  223. else
  224. {
  225. item->Parent()->Right(y);
  226. }
  227. // put item on y's left
  228. y->Left(item);
  229. item->Parent(y);
  230. }
  231. }
  232. void
  233. CIndexTree::LeftRotate(
  234. CIndexItem * item // in - item to rotate from
  235. )
  236. {
  237. CIndexItem * y = item->Left();
  238. if ( y )
  239. {
  240. // turn y's right subtree into x's left subtree
  241. item->Left(y->Right());
  242. if ( y->Right() )
  243. {
  244. y->Right()->Parent(item);
  245. }
  246. // link item's parent to y
  247. y->Parent(item->Parent());
  248. if ( ! item->Parent() )
  249. {
  250. m_root = y;
  251. }
  252. else if ( item == item->Parent()->Right() )
  253. {
  254. item->Parent()->Right(y);
  255. }
  256. else
  257. {
  258. item->Parent()->Left(y);
  259. }
  260. // put item on y's right
  261. y->Right(item);
  262. item->Parent(y);
  263. }
  264. }
  265. CIndexItem * // ret- the node immediately preceding the given node
  266. CIndexTree::GetPrevItem(
  267. CIndexItem * item // in - a node in the index tree
  268. ) const
  269. {
  270. CIndexItem * curr;
  271. if ( item->Left() )
  272. {
  273. curr = item->Left();
  274. while ( curr->Right() )
  275. {
  276. curr = curr->Right();
  277. }
  278. }
  279. else
  280. {
  281. curr = item;
  282. while ( curr->Parent() && curr->Parent()->Left() == curr )
  283. {
  284. curr = curr->Parent();
  285. }
  286. curr = curr->Parent();
  287. }
  288. return curr;
  289. }
  290. CIndexItem * // ret- the node immediately following the given node
  291. CIndexTree::GetNextItem(
  292. CIndexItem * item // in - a node in the index tree
  293. ) const
  294. {
  295. CIndexItem * curr;
  296. if ( item->Right() )
  297. {
  298. curr = item->Right();
  299. while ( curr->Left() )
  300. {
  301. curr = curr->Left();
  302. }
  303. }
  304. else
  305. {
  306. curr = item;
  307. while ( curr->Parent() && curr->Parent()->Right() == curr )
  308. {
  309. curr = curr->Parent();
  310. }
  311. curr = curr->Parent();
  312. }
  313. return curr;
  314. }
  315. void
  316. CIndexTree::RemoveAll()
  317. {
  318. // do a post-order traversal, removing each node
  319. if ( m_root )
  320. {
  321. RemoveHelper(m_root);
  322. m_root = NULL;
  323. }
  324. }
  325. // helper function for removing all items in the tree
  326. void
  327. CIndexTree::RemoveHelper(
  328. CIndexItem * curr // in - current node
  329. )
  330. {
  331. // our tree currently does not support removing a single item, so we'll use a brute force method
  332. // recursively delete children, then delete the current node
  333. if ( curr->Left() )
  334. {
  335. RemoveHelper(curr->Left());
  336. }
  337. if ( curr->Right() )
  338. {
  339. RemoveHelper(curr->Right());
  340. }
  341. delete curr;
  342. }
  343. void
  344. CIndexItem::McLogInternalDiagnostics(CString keyName, int depth)
  345. {
  346. CString key;
  347. CString strLeft;
  348. CString strRight;
  349. CString strParent;
  350. if ( ! keyName.IsEmpty() )
  351. {
  352. key = keyName + ".";
  353. }
  354. if ( pData )
  355. {
  356. key = key + pData->key;
  357. }
  358. else
  359. {
  360. MC_LOG("data is NULL");
  361. }
  362. MC_LOG("address="<<makeStr(this,L"0x%lx") << " pData="<< makeStr(pData,L"0x%lx") << " pLeft="<<makeStr(pLeft,L"0x%lx")<<" pRight="<<makeStr(pRight,L"0x%lx")<< " pParent="<<makeStr(pParent,L"0x%lx") << " red="<<makeStr(red,L"0x%lx") << " depth="<<makeStr(depth));
  363. if ( pLeft )
  364. strLeft = pLeft->GetKey();
  365. if ( pRight )
  366. strRight = pRight->GetKey();
  367. if ( pParent )
  368. strParent = pParent->GetKey();
  369. MC_LOG(" Key=" << String(key) << " Left=" << String(strLeft) << " Right=" << String(strRight) << " Parent="<< String(strParent) );
  370. if ( pLeft )
  371. pLeft->McLogInternalDiagnostics(keyName,depth+1);
  372. if ( pRight )
  373. pRight->McLogInternalDiagnostics(keyName,depth+1);
  374. }
  375. CIndexItem * // ret- smallest node in the index that is >= value
  376. CIndexTree::GetFirstAfter(
  377. CString value // in - string to compare keys to
  378. ) const
  379. {
  380. CIndexItem * item = m_root;
  381. CIndexItem * result = NULL;
  382. int cRes;
  383. while ( item )
  384. {
  385. cRes = m_CompareKey(value,item);
  386. if ( ! cRes )
  387. {
  388. break;
  389. }
  390. if ( cRes > 0 )
  391. {
  392. item = item->Left();
  393. }
  394. else
  395. {
  396. result = item;
  397. item = item->Right();
  398. }
  399. }
  400. return result;
  401. }
  402. void CIndexTree::McLogInternalDiagnostics(CString keyName)
  403. {
  404. CString blockname;
  405. blockname = "Index of "+ keyName;
  406. CString compareFn;
  407. CString compareKey;
  408. if ( m_Compare == &CompareItems )
  409. {
  410. compareFn = "CompareItems";
  411. }
  412. else if ( m_Compare == &CompareItemsNoCase )
  413. {
  414. compareFn = "CompareItemsNoCase";
  415. }
  416. else
  417. {
  418. compareFn.Format(_T("Unknown function, address=%lx"),m_Compare);
  419. }
  420. if ( m_CompareKey == &CompareStringToItem )
  421. {
  422. compareKey = "CompareStringToItem";
  423. }
  424. else if ( m_CompareKey == &CompareStringToItemNoCase )
  425. {
  426. compareKey = "CompareStringToItemNoCase";
  427. }
  428. else
  429. {
  430. compareKey.Format(_T("Unknown function, address=%lx"),m_CompareKey);
  431. }
  432. MC_LOG(String(blockname) << " CaseSensitive=" << makeStr(m_CaseSensitive) << " Compare Function="<<String(compareFn)<< "Compare Key Function=" << String(compareKey) );
  433. if ( m_root )
  434. {
  435. MC_LOG("Beginning preorder dump of index");
  436. m_root->McLogInternalDiagnostics(keyName,0);
  437. }
  438. else
  439. {
  440. MC_LOG("Root of index is NULL");
  441. }
  442. }
  443. #ifdef _DEBUG
  444. BOOL CIndexTree::AssertValid(int nItems) const
  445. {
  446. BOOL bValid = TRUE;
  447. int i;
  448. CIndexItem * curr = GetFirstItem();
  449. CIndexItem * prev = NULL;
  450. for ( i = 0 ; i < nItems ; i++ )
  451. {
  452. ASSERT(curr);
  453. if ( prev && curr )
  454. {
  455. ASSERT(m_Compare(prev,curr) <= 0 );
  456. }
  457. prev = curr;
  458. curr = GetNextItem(curr);
  459. }
  460. ASSERT(curr == NULL); // we should have reached the end
  461. for ( i = 0 ; i < nItems -1 ; i++ )
  462. {
  463. prev = GetPrevItem(prev);
  464. ASSERT(prev);
  465. }
  466. ASSERT(prev == GetFirstItem());
  467. return bValid;
  468. }
  469. #endif