|
|
//========= 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
|