202 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*++
 | 
						||
 | 
						||
   Copyright    (c) 2000    Microsoft Corporation
 | 
						||
 | 
						||
   Module  Name :
 | 
						||
       i-LKRhash.h
 | 
						||
 | 
						||
   Abstract:
 | 
						||
       Internal declarations for LKRhash: a fast, scalable,
 | 
						||
       cache- and MP-friendly hash table
 | 
						||
 | 
						||
   Author:
 | 
						||
       George V. Reilly      (GeorgeRe)     Sep-2000
 | 
						||
 | 
						||
   Environment:
 | 
						||
       Win32 - User Mode
 | 
						||
 | 
						||
   Project:
 | 
						||
       Internet Information Server RunTime Library
 | 
						||
 | 
						||
   Revision History:
 | 
						||
 | 
						||
--*/
 | 
						||
 | 
						||
#ifndef __I_LKRHASH_H__
 | 
						||
#define __I_LKRHASH_H__
 | 
						||
 | 
						||
// Should the table be allowed to contract after deletions?
 | 
						||
#define LKR_CONTRACT
 | 
						||
 | 
						||
#ifndef __LKRHASH_NO_NAMESPACE__
 | 
						||
namespace LKRhash {
 | 
						||
#endif // !__LKRHASH_NO_NAMESPACE__
 | 
						||
 | 
						||
 | 
						||
// Class for bucket chains of the hash table. Note that the first
 | 
						||
// nodeclump is actually included in the bucket and not dynamically
 | 
						||
// allocated, which increases space requirements slightly but does
 | 
						||
// improve performance.
 | 
						||
class CBucket
 | 
						||
{
 | 
						||
private:
 | 
						||
    typedef LKR_BUCKET_LOCK BucketLock;
 | 
						||
 | 
						||
    mutable BucketLock m_Lock;       // lock protecting this bucket
 | 
						||
 | 
						||
#ifdef LOCK_INSTRUMENTATION
 | 
						||
    static LONG sm_cBuckets;
 | 
						||
 | 
						||
    static const TCHAR*
 | 
						||
    _LockName()
 | 
						||
    {
 | 
						||
        LONG l = ++sm_cBuckets;
 | 
						||
        // possible race condition but we don't care, as this is never
 | 
						||
        // used in production code
 | 
						||
        static TCHAR s_tszName[CLockStatistics::L_NAMELEN];
 | 
						||
        wsprintf(s_tszName, _TEXT("B%06x"), 0xFFFFFF & l);
 | 
						||
        return s_tszName;
 | 
						||
    }
 | 
						||
#endif // LOCK_INSTRUMENTATION
 | 
						||
 | 
						||
public:
 | 
						||
    CNodeClump    m_ncFirst;    // first CNodeClump of this bucket
 | 
						||
 | 
						||
#if defined(LOCK_INSTRUMENTATION) || defined(IRTLDEBUG)
 | 
						||
    CBucket()
 | 
						||
#ifdef LOCK_INSTRUMENTATION
 | 
						||
        : m_Lock(_LockName())
 | 
						||
#endif // LOCK_INSTRUMENTATION
 | 
						||
    {
 | 
						||
#ifdef IRTLDEBUG
 | 
						||
        LOCK_LOCKTYPE lt = BucketLock::LockType();
 | 
						||
        if (lt == LOCK_SPINLOCK  ||  lt == LOCK_FAKELOCK)
 | 
						||
            IRTLASSERT(sizeof(*this) <= CNodeClump::BUCKET_BYTE_SIZE);
 | 
						||
#endif IRTLDEBUG
 | 
						||
    }
 | 
						||
#endif // LOCK_INSTRUMENTATION || IRTLDEBUG
 | 
						||
 | 
						||
    void  WriteLock()               { m_Lock.WriteLock(); }
 | 
						||
    void  ReadLock() const          { m_Lock.ReadLock(); }
 | 
						||
    void  WriteUnlock()             { m_Lock.WriteUnlock(); }
 | 
						||
    void  ReadUnlock() const        { m_Lock.ReadUnlock(); }
 | 
						||
    bool  IsWriteLocked() const     { return m_Lock.IsWriteLocked(); }
 | 
						||
    bool  IsReadLocked() const      { return m_Lock.IsReadLocked(); }
 | 
						||
    bool  IsWriteUnlocked() const   { return m_Lock.IsWriteUnlocked(); }
 | 
						||
    bool  IsReadUnlocked() const    { return m_Lock.IsReadUnlocked(); }
 | 
						||
#ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
 | 
						||
    void  SetSpinCount(WORD wSpins) { m_Lock.SetSpinCount(wSpins); }
 | 
						||
    WORD  GetSpinCount() const  { return m_Lock.GetSpinCount(); }
 | 
						||
#endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
 | 
						||
#ifdef LOCK_INSTRUMENTATION
 | 
						||
    CLockStatistics LockStats() const {return m_Lock.Statistics();}
 | 
						||
#endif // LOCK_INSTRUMENTATION
 | 
						||
}; // class CBucket
 | 
						||
 | 
						||
 | 
						||
 | 
						||
// The hash table space is divided into fixed-size segments (arrays of
 | 
						||
// CBuckets) and physically grows/shrinks one segment at a time. This is
 | 
						||
// a low-cost way of having a growable array of buckets.
 | 
						||
//
 | 
						||
// We provide small, medium, and large segments to better tune the
 | 
						||
// overall memory requirements of the hash table according to the
 | 
						||
// expected usage of an instance.
 | 
						||
//
 | 
						||
// We do not use virtual functions: partially because it's faster not to,
 | 
						||
// and partially so that the custom allocators can do a better job, as the
 | 
						||
// segment size is exactly 2^(6+_NBits) bytes long.
 | 
						||
 | 
						||
class CSegment
 | 
						||
{
 | 
						||
public:
 | 
						||
    CBucket m_bktSlots[1];
 | 
						||
 | 
						||
    // See note at m_bktSlots2 in CSizedSegment below
 | 
						||
    CBucket& Slot(DWORD i)
 | 
						||
    { return m_bktSlots[i]; }
 | 
						||
}; // class CSegment
 | 
						||
 | 
						||
 | 
						||
 | 
						||
// A segment directory keeps track of the segments comprising the hash table.
 | 
						||
// The directory is just a variable-sized array of pointers to
 | 
						||
// segments (CDirEntrys).
 | 
						||
class CDirEntry
 | 
						||
{
 | 
						||
public:
 | 
						||
    // MIN_DIRSIZE and MAX_DIRSIZE can be changed independently
 | 
						||
    // of anything else. Should be powers of two.
 | 
						||
    enum {
 | 
						||
        MIN_DIRSIZE =  (1<<3),   // minimum directory size
 | 
						||
        MAX_DIRSIZE = (1<<20),   // maximum directory size
 | 
						||
    };
 | 
						||
 | 
						||
    CSegment* m_pseg;
 | 
						||
 | 
						||
    CDirEntry()
 | 
						||
        : m_pseg(NULL)
 | 
						||
    {}
 | 
						||
 | 
						||
    ~CDirEntry()
 | 
						||
    { delete m_pseg; }
 | 
						||
}; // class CDirEntry
 | 
						||
 | 
						||
 | 
						||
 | 
						||
template <int _NBits, int _InitSizeMultiplier, LK_TABLESIZE _lkts>
 | 
						||
class CSizedSegment : public CSegment
 | 
						||
{
 | 
						||
public:
 | 
						||
    // Maximum table size equals MAX_DIRSIZE * SEGSIZE buckets.
 | 
						||
    enum {
 | 
						||
        SEGBITS  =       _NBits,// number of bits extracted from a hash
 | 
						||
                                // address for offset within a segment
 | 
						||
        SEGSIZE  = (1<<SEGBITS),// segment size
 | 
						||
        SEGMASK  = (SEGSIZE-1), // mask used for extracting offset bit
 | 
						||
        INITSIZE = _InitSizeMultiplier * SEGSIZE, // #segments to allocate
 | 
						||
                                // initially
 | 
						||
    };
 | 
						||
 | 
						||
    // Hack: assumes laid out immediately after CSegment::m_bktSlots,
 | 
						||
    // with no padding. The STATIC_ASSERTs in _AllocateSegment and in
 | 
						||
    // CompileTimeAssertions should cause a compile-time error if
 | 
						||
    // this assumption ever proves false.
 | 
						||
    CBucket m_bktSlots2[SEGSIZE-1];
 | 
						||
 | 
						||
public:
 | 
						||
    DWORD           Bits() const        { return SEGBITS; }
 | 
						||
    DWORD           Size() const        { return SEGSIZE; }
 | 
						||
    DWORD           Mask() const        { return SEGMASK; }
 | 
						||
    DWORD           InitSize() const    { return INITSIZE;}
 | 
						||
    LK_TABLESIZE    SegmentType() const { return _lkts; }
 | 
						||
 | 
						||
    static void CompileTimeAssertions()
 | 
						||
    {
 | 
						||
        STATIC_ASSERT(offsetof(CSizedSegment, m_bktSlots) + sizeof(CBucket)
 | 
						||
                      == offsetof(CSizedSegment, m_bktSlots2));
 | 
						||
    };
 | 
						||
 | 
						||
#ifdef IRTLDEBUG
 | 
						||
    CSizedSegment()
 | 
						||
    {
 | 
						||
        IRTLASSERT(&Slot(1) == m_bktSlots2);
 | 
						||
        IRTLASSERT(sizeof(*this) == SEGSIZE * sizeof(CBucket));
 | 
						||
    }
 | 
						||
#endif // IRTLDEBUG
 | 
						||
 | 
						||
    LKRHASH_ALLOCATOR_DEFINITIONS(CSizedSegment);
 | 
						||
}; // class CSizedSegment
 | 
						||
 | 
						||
 | 
						||
typedef CSizedSegment<3, 1, LK_SMALL_TABLESIZE>   CSmallSegment;
 | 
						||
typedef CSizedSegment<6, 1, LK_MEDIUM_TABLESIZE>  CMediumSegment;
 | 
						||
typedef CSizedSegment<9, 2, LK_LARGE_TABLESIZE>   CLargeSegment;
 | 
						||
 | 
						||
 | 
						||
#ifndef __LKRHASH_NO_NAMESPACE__
 | 
						||
};
 | 
						||
#endif // !__LKRHASH_NO_NAMESPACE__
 | 
						||
 | 
						||
#endif //__I_LKRHASH_H__
 |