/****************************************************************************
*	SPHash.h
*       This is modified from sr/include/hash_n.h to minimize dependencies on
*       application specific headers.  
*
*	Owner: bohsu
*	Copyright ©2000 Microsoft Corporation All Rights Reserved.
*****************************************************************************/
#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#endif

//--- Includes --------------------------------------------------------------
#include <windows.h>
#include <math.h>
#include <crtdbg.h>
#ifdef _DEBUG
#include <stdio.h>
#endif _DEBUG

//--- Forward and External Declarations -------------------------------------

//--- TypeDef and Enumeration Declarations ----------------------------------

//--- Constants -------------------------------------------------------------

//--- Class, Struct and Union Definitions -----------------------------------

/***********************************************************************
* CSPHash Class
*   This is a templated hash table class.  Note that the base CSPHash class 
*   does not allocate or free the Keys and Values.  To define a hash class
*   that manages its Keys and Values, derive a subclass an overload Add() 
*   and ...
*****************************************************************bohsu*/
template<class KEY, class VALUE>
class CSPHash
{
public:
    // Constructor
    CSPHash(
        VALUE   ValueNIL = NULL,                // Value representing NIL
        UINT32  uInitialSize = 0);              // Initial hash table size

    // Destructor
    virtual ~CSPHash();

    // Returns number of (Key, Value) entries used in the hash table.
    inline UINT32 GetNumEntries(void) const { return m_uNumEntriesUsed; }

    // Returns the next entry starting at the given index.  Set puIndex = 0 for the first entry.
    VALUE GetNextEntry(
        UINT32 *puIndex,                        // Index to start looking for the next entry
        KEY    *pKey = NULL) const;             // [out] Key of the next entry found

    // Resets the content hash table.
    virtual void Reset(void);

    // Adds a (Key, Value) entry to the hash table.
    HRESULT Add(
        KEY     Key,                            // Key to add
        VALUE   Val);                           // Value associated with the Key

    // Lookup a Value based on the Key.  If not found, ValueNIL is returned.
    VALUE Lookup(
        KEY     Key) const;                     // Key to lookup

#ifdef _DEBUG
    // Dumps the hash table statistics to file handle.
    void    DumpStat(
        FILE       *hFile = NULL,               // Output file handle.  NULL -> DebugWindow
        const char *strHeader = NULL) const;    // Trace header
#endif _DEBUG

protected:
    // Data structure containing (Key, Value) pair
    struct ENTRY
    {
        KEY     Key;
        VALUE   Value;
    };

    // Find the index corresponding to the given Key.
    int FindIndex(
        KEY     Key) const;                     // Key to search for

    static UINT32 NextPrime(UINT32 Val);

protected:
    //---------------------------------------------------------------
    //--- The following functions can be overloaded by subclasses ---
    //---------------------------------------------------------------
    //  If Destroy*() is overloaded, you MUST overload the destructor with:
    //      virtual ~CSPDerivedHash() { Reset(); }
    //  Calling Reset() in the base class destructor has no effect because 
    //  the derived subclass will have been destroyed already by the time it
    //  gets to the base class destructor.  Thus, the correct DestroyKey() and
    //  DestroyValue() will never be called.

    // Hash function mapping the Key to a UINT32 index.
    virtual UINT32 HashKey(KEY Key) const          { return (UINT32)Key; }

    // Compare if two Keys are equal.
    virtual bool   AreKeysEqual(KEY Key1, KEY Key2) const { return Key1 == Key2; }

    // Hash function used to determine the skip count.
    virtual UINT32 HashKey2(KEY Key) const         { return 1; }

    // Overload if a deep copy of the Key needs to be made in Add().
    virtual KEY    CopyKey(KEY Key) const          { return Key; }

    // Overload if a deep copy of the Key needs to be made in Add().
    virtual VALUE  CopyValue(VALUE Value) const    { return Value;  }

    // Overload if the Key needs to be destroyed.
    virtual void   DestroyKey(KEY Key) const       { }

    // Overload if the Value needs to be destroyed.
    virtual void   DestroyValue(VALUE Value) const { }

    //------------------------
    //--- Member Variables ---
    //------------------------
protected:
    ENTRY  *m_aTable;                           // Hash table containing (Key, Value) pairs
    VALUE   m_ValueNIL;                         // Value representing NIL
    UINT32  m_uNumEntries;                      // Current size of hash table
    UINT32  m_uNumEntriesInit;                  // Initial size of hash table
    UINT32  m_uNumEntriesUsed;                  // Current number of entries used in hash table

#ifdef _DEBUG
    UINT32  m_uAccess;                          // Number of times a Key is looked up
    UINT32  m_uSearch;                          // Number of times a entry in the table is searched
    UINT32  m_uRegrow;                          // Number of times the hash table regrew
#endif _DEBUG
};


/***********************************************************************
* CSPStringHashW Class
*   CSPStringHashW is a hash of UNICODE strings to VALUEs.  The UNICODE string
*   is treated as a constant.  It is neither copied during Add() nor deleted
*   during destructor.  Likewise, VALUE is treated as a simple data type and
*   is neither copied nor destroyed.  If the application wants the class to 
*   manage its own copy of the string key or VALUE, derive a subclass and 
*   overload Copy*() and/or Destroy().
*****************************************************************bohsu*/
template<class VALUE> class CSPStringHashW : public CSPHash<const WCHAR *, VALUE> 
{ 
protected:
    UINT32 StringHashW(const WCHAR *wcsKey, UINT32 uPrime) const
    {
        UINT32  uHashIndex = 0;
	    for(const WCHAR *pwch = wcsKey; *pwch != NULL; pwch++)
            uHashIndex = uHashIndex * uPrime + *pwch;
        return uHashIndex;
    }

    //--- Overloaded functions ---
protected:
    virtual UINT32 HashKey(const WCHAR* wcsKey) const  { return StringHashW(wcsKey, 65599); }
    virtual UINT32 HashKey2(const WCHAR* wcsKey) const { return StringHashW(wcsKey, 257); }
    virtual bool AreKeysEqual(const WCHAR* wcsKey1, const WCHAR* wcsKey2) const
    { 
        return wcscmp(wcsKey1, wcsKey2) == 0; 
    }
};
 
/***********************************************************************
* CSPGUIDHash Class
*   CSPGUIDHash is a hash of GUIDs to VALUEs.  The GUID pointer is treated 
*   as a constant.  It is neither copied during Add() nor deleted
*   during destructor.  Likewise, VALUE is treated as a simple data type and
*   is neither copied nor destroyed.  If the application wants the class to 
*   manage its own copy of the GUID key or VALUE, derive a subclass and 
*   overload Copy*() and/or Destroy().
*****************************************************************bohsu*/
template<class VALUE> class CSPGUIDHash : public CSPHash<const GUID *, VALUE> 
{ 
    //--- Overloaded functions ---
protected:
    virtual UINT32 HashKey(const GUID *pguidKey) const  { return pguidKey->Data1; }
    virtual UINT32 HashKey2(const GUID *pguidKey) const { return pguidKey->Data2; }
    virtual bool AreKeysEqual(const GUID *pguidKey1, const GUID *pguidKey2) const
    { 
        // It is annoying that operator== for GUIDs return int (BOOL) instead of bool.
        return (*pguidKey1 == *pguidKey2) != 0; 
    }
};

//--- Function Declarations -------------------------------------------------

//--- Inline Function Definitions -------------------------------------------

/**********************************************************************
* CSPHash::CSPHash *
*------------------*
*	Description:  
*       Constructor.
****************************************************************bohsu*/
template<class KEY, class VALUE>
CSPHash<KEY, VALUE>::CSPHash(
    VALUE   ValueNIL,                       // Value representing NIL
    UINT32  uInitialSize)                   // Initial hash table size
{
    m_ValueNIL        = ValueNIL;
    m_aTable          = 0;
    m_uNumEntries     = 0;
    m_uNumEntriesInit = uInitialSize;       // Estimated final number of entries to be stored.
    m_uNumEntriesUsed = 0;

#ifdef _DEBUG
    m_uAccess = 0;
    m_uSearch = 0;
    m_uRegrow = 0;
#endif _DEBUG
}

/**********************************************************************
* CSPHash::~CSPHash *
*-------------------*
*	Description:  
*       Destructor.  This does not free KEY and VALUE.
*       If Destroy*() is overloaded, call Reset() in the subclass destructor.
****************************************************************bohsu*/
template<class KEY, class VALUE>
CSPHash<KEY, VALUE>::~CSPHash()
{
    delete [] m_aTable;
}

/**********************************************************************
* CSPHash::GetNextEntry *
*-----------------------*
*	Description:  
*       Returns the next entry starting at the given index.  Set puIndex = 0 for the first entry.
****************************************************************bohsu*/
template<class KEY, class VALUE>
VALUE CSPHash<KEY, VALUE>::GetNextEntry(
    UINT32 *puIndex,                        // Index to start looking for the next entry
    KEY    *pKey) const                     // [out] Key of the next entry found
{
    while (*puIndex < m_uNumEntries)
    {
        if (m_aTable[*puIndex].Value != m_ValueNIL)
        {
            if(pKey) *pKey = m_aTable[*puIndex].Key;
            return m_aTable[(*puIndex)++].Value;
        }
        ++*puIndex;
    }
    return m_ValueNIL;
}

/**********************************************************************
* CSPHash::Reset *
*----------------*
*	Description:  
*       Resets the content hash table.
****************************************************************bohsu*/
template<class KEY, class VALUE>
void CSPHash<KEY, VALUE>::Reset()
{
    for (UINT32 i=0; i < m_uNumEntries; i++)
    {
        if(m_aTable[i].Value != m_ValueNIL)
        {
            DestroyKey(m_aTable[i].Key);
            DestroyValue(m_aTable[i].Value);
            m_aTable[i].Value = m_ValueNIL;
        }
    }
    
    m_uNumEntriesUsed = 0;
#ifdef _DEBUG
    m_uAccess = m_uSearch = m_uRegrow = 0;    
#endif _DEBUG
}

/**********************************************************************
* CSPHash::Add *
*--------------*
*	Description:  
*       Adds a (Key, Value) entry to the hash table.
****************************************************************bohsu*/
template<class KEY, class VALUE>
HRESULT CSPHash<KEY, VALUE>::Add(
    KEY     Key,                            // Key to add
    VALUE   Val)                            // Value associated with the Key
{
    int ientry;

    // Implementation uses Val==m_ValueNIL to detect empty entries.
    _ASSERTE(Val != m_ValueNIL);

    // Grow if allowed and we're more than half full.
    // (Also handles initial alloc)
    if (m_uNumEntriesUsed * 2 >= m_uNumEntries)
    {
        /* half-full, too crowded ==> regrow */
        ENTRY * oldtable = m_aTable;
        UINT32 oldentry = m_uNumEntries;
        UINT32 prime = NextPrime(max(m_uNumEntriesUsed * 3 + 17, m_uNumEntriesInit));

#ifdef _DEBUG
        m_uRegrow++;
#endif _DEBUG

        // Alloc new table.
        m_aTable = new ENTRY[prime];
        if (m_aTable == NULL)
        {
            m_aTable = oldtable;
            return E_OUTOFMEMORY;
        }

        for (UINT32 i=0; i < prime; i++)
        {
            m_aTable[i].Value = m_ValueNIL;
        }

        m_uNumEntries = prime;

        for (i = 0; i < oldentry; i++)
        {
            if (oldtable[i].Value != m_ValueNIL)
            {
                ientry = FindIndex(oldtable[i].Key);
                _ASSERTE(ientry >= 0 && m_aTable[ientry].Value == m_ValueNIL);
                m_aTable[ientry] = oldtable[i];
            }
        }
        delete [] oldtable;
    }

    // Find out where this element should end up.
    ientry = FindIndex(Key);
    if (ientry < 0)
        return E_FAIL;  // Too full

    if (m_aTable[ientry].Value == m_ValueNIL)
    {
        // Not already there.  Add it.
        m_aTable[ientry].Key = CopyKey(Key);
        m_aTable[ientry].Value = CopyValue(Val);

        m_uNumEntriesUsed++;
    }
    else
    {
        return S_FALSE; // It was already there.
    }

    return S_OK;
}

/**********************************************************************
* CSPHash::Lookup *
*-----------------*
*	Description:  
*       Lookup a Value based on the Key.  If not found, ValueNIL is returned.
****************************************************************bohsu*/
template<class KEY, class VALUE>
VALUE CSPHash<KEY, VALUE>::Lookup(
    KEY     Key) const                      // Key to lookup
{
    int ientry = FindIndex(Key);
    if (ientry < 0)
        return m_ValueNIL;

    return m_aTable[ientry].Value;
}

#ifdef _DEBUG
/**********************************************************************
* CSPHash::DumpStat *
*-------------------*
*	Description:  
*       Dumps the hash table statistics to file handle.
****************************************************************bohsu*/
template<class KEY, class VALUE>
void CSPHash<KEY, VALUE>::DumpStat(
    FILE       *hFile,                      // Output file handle.
    const char *strHeader) const            // Trace header
{
    if(hFile == NULL)
    {
        char buf[100];

        sprintf(buf, "(%s) hash statistics:\n", strHeader ? strHeader : "");
        OutputDebugString(buf);
        sprintf(buf, "load=%d/%d = %.3g, regrow = %d\n", m_uNumEntriesUsed, m_uNumEntries,
               (m_uNumEntries == 0) ? 0 : (float)m_uNumEntriesUsed/(float)m_uNumEntries, m_uRegrow);
        OutputDebugString(buf);
        sprintf(buf, "access %d/%d = %g\n\n", m_uSearch, m_uAccess,
               (m_uAccess == 0) ? 0 :
               (float) m_uSearch / (float) m_uAccess);
        OutputDebugString(buf);
    }
    else
    {
        fprintf(hFile, "(%s) hash statistics:\n", strHeader ? strHeader : "");
        fprintf(hFile, "load=%d/%d = %.3g, regrow = %d\n", m_uNumEntriesUsed, m_uNumEntries,
               (m_uNumEntries == 0) ? 0 : (float)m_uNumEntriesUsed/(float)m_uNumEntries, m_uRegrow);
        fprintf(hFile, "access %d/%d = %g\n\n", m_uSearch, m_uAccess,
               (m_uAccess == 0) ? 0 :
               (float) m_uSearch / (float) m_uAccess);
    }
}
#endif _DEBUG

/**********************************************************************
* CSPHash::FindIndex *
*--------------------*
*	Description:  
*       Find the index corresponding to the given Key.
****************************************************************bohsu*/
template<class KEY, class VALUE>
int CSPHash<KEY, VALUE>::FindIndex(
    KEY     Key) const
{
#ifdef _DEBUG
    // Hack: Violate const declaration for statistics member variables
    const_cast<CSPHash *>(this)->m_uAccess++;
#endif _DEBUG

    if (m_uNumEntries == 0)
        return -1;

    UINT32 start = HashKey(Key) % m_uNumEntries;
    UINT32 index = start;

    UINT32 skip = 0;

    do
    {
#ifdef _DEBUG
        // Hack: Violate const declaration for statistics member variables
        const_cast<CSPHash *>(this)->m_uSearch++;
#endif _DEBUG

        // Not in table; return index where it should be placed.
        if (m_aTable[index].Value == m_ValueNIL)
            return index;

        if (AreKeysEqual(m_aTable[index].Key, Key))
            return index;

        if (skip == 0)
        {
            skip = HashKey2(Key);

            // Limit skip amount to non-zero and less than hash table size.
            // Since m_uNumEntries is prime, they are relatively prime and so we're guaranteed
            // to visit every bucket.
            if (m_uNumEntries > 1)
                skip = skip % (m_uNumEntries - 1) + 1;
        }

        index += skip;
        if (index >= m_uNumEntries)
            index -= m_uNumEntries;
    } while (index != start);

    _ASSERTE(m_uNumEntriesUsed == m_uNumEntries);
    return -1; /* all full and not found */
}

/**********************************************************************
* CSPHash::NextPrime *
*--------------------*
*	Description:  
*	    Return a prime number greater than or equal to Val.
*       If overflow occurs, return 0.
*
*   To Do: This function can be optimized significantly.
****************************************************************bohsu*/
template<class KEY, class VALUE>
UINT32 CSPHash<KEY, VALUE>::NextPrime(UINT32 Val) 
{
    UINT32      maxFactor;
    UINT32      i;
    
    if (Val < 2) return 2;                          // the smallest prime number    
    while(Val < 0xFFFFFFFF) 
    {        
        maxFactor = (UINT32) sqrt ((double) Val);   // Is Val a prime number?
        
        for (i = 2; i <= maxFactor; i++)            // Is i a factor of Val?
            if (Val % i == 0) break;
            
        if (i > maxFactor) return (Val);            
        Val++;
    };
    return 0;
}