Team Fortress 2 Source Code as on 22/4/2020
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.

394 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bi-directional set. A Bucket knows about the elements that lie
  4. // in it, and the elements know about the buckets they lie in.
  5. //
  6. // $Revision: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #ifndef UTLBIDIRECTIONALSET_H
  10. #define UTLBIDIRECTIONALSET_H
  11. #ifdef _WIN32
  12. #pragma once
  13. #endif
  14. #include "tier0/dbg.h"
  15. #include "utllinkedlist.h"
  16. //-----------------------------------------------------------------------------
  17. // Templatized helper class to deal with the kinds of things that spatial
  18. // partition code always seems to have; buckets with lists of lots of elements
  19. // and elements that live in lots of buckets. This makes it really quick to
  20. // add and remove elements, and to iterate over all elements in a bucket.
  21. //
  22. // For this to work, you must initialize the set with two functions one that
  23. // maps from bucket to the index of the first element in that bucket, and one
  24. // that maps from element to the index of the first bucket that element lies in.
  25. // The set will completely manage the index, it's just expected that those
  26. // indices will be stored outside the set.
  27. //
  28. // S is the storage type of the index; it is the type that you may use to
  29. // save indices into memory. I is the local iterator type, which you should
  30. // use in any local scope (eg, inside a for() loop.) The reason for this is
  31. // that you may wish to use unsigned shorts inside the structs you are
  32. // saving with a CBidirectionalSet; but 16-bit arithmetic is catastrophically
  33. // slow on a PowerPC -- during testing we saw CBidirectionalSet:: operations
  34. // consume as much as 8% of the frame.
  35. //
  36. // For this reason, on the 360, the handles have been typedef'd to native
  37. // register types (U32) which are accepted as parameters by the functions.
  38. // The implicit assumption is that CBucketHandle and CElementHandle can
  39. // be safely cast to ints! You can increase to U64 without performance
  40. // penalty if necessary; the PowerPC is a 64-bit processor.
  41. //-----------------------------------------------------------------------------
  42. template< class CBucketHandle, class CElementHandle, class S, class I = S >
  43. class CBidirectionalSet
  44. {
  45. public:
  46. // Install methods to get at the first bucket given a element
  47. // and vice versa...
  48. typedef S& (*FirstElementFunc_t)(CBucketHandle);
  49. typedef S& (*FirstBucketFunc_t)(CElementHandle);
  50. #ifdef _X360
  51. typedef uint32 CBucketHandlePram;
  52. typedef uint32 CElementHandlePram;
  53. #else
  54. typedef CBucketHandle CBucketHandlePram;
  55. typedef CElementHandle CElementHandlePram;
  56. #endif
  57. // Constructor
  58. CBidirectionalSet();
  59. // Call this before using the set
  60. void Init( FirstElementFunc_t elemFunc, FirstBucketFunc_t bucketFunc );
  61. // Add an element to a particular bucket
  62. void AddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element );
  63. // Prevalidate an add to a particular bucket
  64. // NOTE: EXPENSIVE!!!
  65. void ValidateAddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element );
  66. // Test if an element is in a particular bucket.
  67. // NOTE: EXPENSIVE!!!
  68. bool IsElementInBucket( CBucketHandlePram bucket, CElementHandlePram element );
  69. // Remove an element from a particular bucket
  70. void RemoveElementFromBucket( CBucketHandlePram bucket, CElementHandlePram element );
  71. // Remove an element from all buckets
  72. void RemoveElement( CElementHandlePram element );
  73. void RemoveBucket( CBucketHandlePram element );
  74. // Used to iterate elements in a bucket; I is the iterator
  75. I FirstElement( CBucketHandlePram bucket ) const;
  76. I NextElement( I idx ) const;
  77. CElementHandle Element( I idx ) const;
  78. // Used to iterate buckets associated with an element; I is the iterator
  79. I FirstBucket( CElementHandlePram bucket ) const;
  80. I NextBucket( I idx ) const;
  81. CBucketHandle Bucket( I idx ) const;
  82. static S InvalidIndex();
  83. // Ensure capacity
  84. void EnsureCapacity( int count );
  85. // Deallocate....
  86. void Purge();
  87. int NumAllocated( void ) const;
  88. private:
  89. struct BucketListInfo_t
  90. {
  91. CElementHandle m_Element;
  92. S m_BucketListIndex; // what's the m_BucketsUsedByElement index of the entry?
  93. };
  94. struct ElementListInfo_t
  95. {
  96. CBucketHandle m_Bucket;
  97. S m_ElementListIndex; // what's the m_ElementsInBucket index of the entry?
  98. };
  99. // Maintains a list of all elements in a particular bucket
  100. CUtlLinkedList< BucketListInfo_t, S, true, I > m_ElementsInBucket;
  101. // Maintains a list of all buckets a particular element lives in
  102. CUtlLinkedList< ElementListInfo_t, S, true, I > m_BucketsUsedByElement;
  103. FirstBucketFunc_t m_FirstBucket;
  104. FirstElementFunc_t m_FirstElement;
  105. };
  106. //-----------------------------------------------------------------------------
  107. // Constructor
  108. //-----------------------------------------------------------------------------
  109. template< class CBucketHandle, class CElementHandle, class S, class I >
  110. CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::CBidirectionalSet( )
  111. {
  112. m_FirstBucket = NULL;
  113. m_FirstElement = NULL;
  114. }
  115. //-----------------------------------------------------------------------------
  116. // Call this before using the set
  117. //-----------------------------------------------------------------------------
  118. template< class CBucketHandle, class CElementHandle, class S, class I >
  119. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::Init( FirstElementFunc_t elemFunc, FirstBucketFunc_t bucketFunc )
  120. {
  121. m_FirstBucket = bucketFunc;
  122. m_FirstElement = elemFunc;
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Adds an element to the bucket
  126. //-----------------------------------------------------------------------------
  127. template< class CBucketHandle, class CElementHandle, class S, class I >
  128. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::ValidateAddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element )
  129. {
  130. #ifdef _DEBUG
  131. // Make sure that this element doesn't already exist in the list of elements in the bucket
  132. I elementInBucket = m_FirstElement( bucket );
  133. while( elementInBucket != m_ElementsInBucket.InvalidIndex() )
  134. {
  135. // If you hit an Assert here, fix the calling code. It's too expensive to ensure
  136. // that each item only shows up once here. Hopefully you can do something better
  137. // outside of here.
  138. Assert( m_ElementsInBucket[elementInBucket].m_Element != element );
  139. elementInBucket = m_ElementsInBucket.Next( elementInBucket );
  140. }
  141. // Make sure that this bucket doesn't already exist in the element's list of buckets.
  142. I bucketInElement = m_FirstBucket( element );
  143. while( bucketInElement != m_BucketsUsedByElement.InvalidIndex() )
  144. {
  145. // If you hit an Assert here, fix the calling code. It's too expensive to ensure
  146. // that each item only shows up once here. Hopefully you can do something better
  147. // outside of here.
  148. Assert( m_BucketsUsedByElement[bucketInElement].m_Bucket != bucket );
  149. bucketInElement = m_BucketsUsedByElement.Next( bucketInElement );
  150. }
  151. #endif
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Adds an element to the bucket
  155. //-----------------------------------------------------------------------------
  156. template< class CBucketHandle, class CElementHandle, class S, class I >
  157. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::AddElementToBucket( CBucketHandlePram bucket, CElementHandlePram element )
  158. {
  159. Assert( m_FirstBucket && m_FirstElement );
  160. // Allocate new element + bucket entries
  161. I idx = m_ElementsInBucket.Alloc(true);
  162. I list = m_BucketsUsedByElement.Alloc( true );
  163. // Store off the element data
  164. m_ElementsInBucket[idx].m_Element = element;
  165. m_ElementsInBucket[idx].m_BucketListIndex = list;
  166. // Here's the bucket data
  167. m_BucketsUsedByElement[list].m_Bucket = bucket;
  168. m_BucketsUsedByElement[list].m_ElementListIndex = idx;
  169. // Insert the element into the list of elements in the bucket
  170. S& firstElementInBucket = m_FirstElement( bucket );
  171. if ( firstElementInBucket != m_ElementsInBucket.InvalidIndex() )
  172. m_ElementsInBucket.LinkBefore( firstElementInBucket, idx );
  173. firstElementInBucket = idx;
  174. // Insert the bucket into the element's list of buckets
  175. S& firstBucketInElement = m_FirstBucket( element );
  176. if ( firstBucketInElement != m_BucketsUsedByElement.InvalidIndex() )
  177. m_BucketsUsedByElement.LinkBefore( firstBucketInElement, list );
  178. firstBucketInElement = list;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Test if an element is in a particular bucket.
  182. // NOTE: EXPENSIVE!!!
  183. //-----------------------------------------------------------------------------
  184. template< class CBucketHandle, class CElementHandle, class S, class I >
  185. bool CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::IsElementInBucket( CBucketHandlePram bucket, CElementHandlePram element )
  186. {
  187. // Search through all elements in this bucket to see if element is in there.
  188. I elementInBucket = m_FirstElement( bucket );
  189. while( elementInBucket != m_ElementsInBucket.InvalidIndex() )
  190. {
  191. if( m_ElementsInBucket[elementInBucket].m_Element == element )
  192. {
  193. return true;
  194. }
  195. elementInBucket = m_ElementsInBucket.Next( elementInBucket );
  196. }
  197. return false;
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Remove an element from a particular bucket
  201. //-----------------------------------------------------------------------------
  202. template< class CBucketHandle, class CElementHandle, class S, class I >
  203. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::RemoveElementFromBucket( CBucketHandlePram bucket, CElementHandlePram element )
  204. {
  205. // FIXME: Implement me!
  206. Assert(0);
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Removes an element from all buckets
  210. //-----------------------------------------------------------------------------
  211. template< class CBucketHandle, class CElementHandle, class S, class I >
  212. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::RemoveElement( CElementHandlePram element )
  213. {
  214. Assert( m_FirstBucket && m_FirstElement );
  215. // Iterate over the list of all buckets the element is in
  216. I i = m_FirstBucket( element );
  217. while (i != m_BucketsUsedByElement.InvalidIndex())
  218. {
  219. CBucketHandlePram bucket = m_BucketsUsedByElement[i].m_Bucket;
  220. I elementListIndex = m_BucketsUsedByElement[i].m_ElementListIndex;
  221. // Unhook the element from the bucket's list of elements
  222. if (elementListIndex == m_FirstElement(bucket))
  223. m_FirstElement(bucket) = m_ElementsInBucket.Next(elementListIndex);
  224. m_ElementsInBucket.Free(elementListIndex);
  225. I prevNode = i;
  226. i = m_BucketsUsedByElement.Next(i);
  227. m_BucketsUsedByElement.Free(prevNode);
  228. }
  229. // Mark the list as empty
  230. m_FirstBucket( element ) = m_BucketsUsedByElement.InvalidIndex();
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Removes a bucket from all elements
  234. //-----------------------------------------------------------------------------
  235. template< class CBucketHandle, class CElementHandle, class S, class I >
  236. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::RemoveBucket( CBucketHandlePram bucket )
  237. {
  238. // Iterate over the list of all elements in the bucket
  239. I i = m_FirstElement( bucket );
  240. while (i != m_ElementsInBucket.InvalidIndex())
  241. {
  242. CElementHandlePram element = m_ElementsInBucket[i].m_Element;
  243. I bucketListIndex = m_ElementsInBucket[i].m_BucketListIndex;
  244. // Unhook the bucket from the element's list of buckets
  245. if (bucketListIndex == m_FirstBucket(element))
  246. m_FirstBucket(element) = m_BucketsUsedByElement.Next(bucketListIndex);
  247. m_BucketsUsedByElement.Free(bucketListIndex);
  248. // Remove the list element
  249. I prevNode = i;
  250. i = m_ElementsInBucket.Next(i);
  251. m_ElementsInBucket.Free(prevNode);
  252. }
  253. // Mark the bucket list as empty
  254. m_FirstElement( bucket ) = m_ElementsInBucket.InvalidIndex();
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Ensure capacity
  258. //-----------------------------------------------------------------------------
  259. template< class CBucketHandle, class CElementHandle, class S, class I >
  260. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::EnsureCapacity( int count )
  261. {
  262. m_ElementsInBucket.EnsureCapacity( count );
  263. m_BucketsUsedByElement.EnsureCapacity( count );
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Deallocate....
  267. //-----------------------------------------------------------------------------
  268. template< class CBucketHandle, class CElementHandle, class S, class I >
  269. void CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::Purge()
  270. {
  271. m_ElementsInBucket.Purge( );
  272. m_BucketsUsedByElement.Purge( );
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Number of elements allocated in each linked list (should be the same)
  276. //-----------------------------------------------------------------------------
  277. template< class CBucketHandle, class CElementHandle, class S, class I >
  278. int CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::NumAllocated( void ) const
  279. {
  280. Assert( m_ElementsInBucket.NumAllocated() == m_BucketsUsedByElement.NumAllocated() );
  281. return m_ElementsInBucket.NumAllocated();
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Invalid index for iteration..
  285. //-----------------------------------------------------------------------------
  286. template< class CBucketHandle, class CElementHandle, class S, class I >
  287. inline S CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::InvalidIndex()
  288. {
  289. return CUtlLinkedList< CElementHandle, I >::InvalidIndex();
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Used to iterate elements in a bucket; I is the iterator
  293. //-----------------------------------------------------------------------------
  294. template< class CBucketHandle, class CElementHandle, class S, class I >
  295. inline I CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::FirstElement( CBucketHandlePram bucket ) const
  296. {
  297. Assert( m_FirstElement );
  298. return m_FirstElement(bucket);
  299. }
  300. template< class CBucketHandle, class CElementHandle, class S, class I >
  301. inline I CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::NextElement( I idx ) const
  302. {
  303. return m_ElementsInBucket.Next(idx);
  304. }
  305. template< class CBucketHandle, class CElementHandle, class S, class I >
  306. inline CElementHandle CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::Element( I idx ) const
  307. {
  308. return m_ElementsInBucket[idx].m_Element;
  309. }
  310. //-----------------------------------------------------------------------------
  311. // Used to iterate buckets an element lies in; I is the iterator
  312. //-----------------------------------------------------------------------------
  313. template< class CBucketHandle, class CElementHandle, class S, class I >
  314. inline I CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::FirstBucket( CElementHandlePram element ) const
  315. {
  316. Assert( m_FirstBucket );
  317. return m_FirstBucket(element);
  318. }
  319. template< class CBucketHandle, class CElementHandle, class S, class I >
  320. inline I CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::NextBucket( I idx ) const
  321. {
  322. return m_BucketsUsedByElement.Next(idx);
  323. }
  324. template< class CBucketHandle, class CElementHandle, class S, class I >
  325. inline CBucketHandle CBidirectionalSet<CBucketHandle,CElementHandle,S,I>::Bucket( I idx ) const
  326. {
  327. return m_BucketsUsedByElement[idx].m_Bucket;
  328. }
  329. #endif // UTLBIDIRECTIONALSET_H