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