mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-01 22:03:40 -06:00
455 lines
14 KiB
C++
455 lines
14 KiB
C++
/**********************************************************************
|
|
* $Id: cpl_hash_set.cpp 27044 2014-03-16 23:41:27Z rouault $
|
|
*
|
|
* Name: cpl_hash_set.cpp
|
|
* Project: CPL - Common Portability Library
|
|
* Purpose: Hash set functions.
|
|
* Author: Even Rouault, <even dot rouault at mines dash paris dot org>
|
|
*
|
|
**********************************************************************
|
|
* Copyright (c) 2008-2009, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_hash_set.h"
|
|
#include "cpl_list.h"
|
|
|
|
struct _CPLHashSet
|
|
{
|
|
CPLHashSetHashFunc fnHashFunc;
|
|
CPLHashSetEqualFunc fnEqualFunc;
|
|
CPLHashSetFreeEltFunc fnFreeEltFunc;
|
|
CPLList** tabList;
|
|
int nSize;
|
|
int nIndiceAllocatedSize;
|
|
int nAllocatedSize;
|
|
#ifdef HASH_DEBUG
|
|
int nCollisions;
|
|
#endif
|
|
};
|
|
|
|
static const int anPrimes[] =
|
|
{ 53, 97, 193, 389, 769, 1543, 3079, 6151,
|
|
12289, 24593, 49157, 98317, 196613, 393241,
|
|
786433, 1572869, 3145739, 6291469, 12582917,
|
|
25165843, 50331653, 100663319, 201326611,
|
|
402653189, 805306457, 1610612741 };
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetNew() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Creates a new hash set
|
|
*
|
|
* The hash function must return a hash value for the elements to insert.
|
|
* If fnHashFunc is NULL, CPLHashSetHashPointer will be used.
|
|
*
|
|
* The equal function must return if two elements are equal.
|
|
* If fnEqualFunc is NULL, CPLHashSetEqualPointer will be used.
|
|
*
|
|
* The free function is used to free elements inserted in the hash set,
|
|
* when the hash set is destroyed, when elements are removed or replaced.
|
|
* If fnFreeEltFunc is NULL, elements inserted into the hash set will not be freed.
|
|
*
|
|
* @param fnHashFunc hash function. May be NULL.
|
|
* @param fnEqualFunc equal function. May be NULL.
|
|
* @param fnFreeEltFunc element free function. May be NULL.
|
|
*
|
|
* @return a new hash set
|
|
*/
|
|
|
|
CPLHashSet* CPLHashSetNew(CPLHashSetHashFunc fnHashFunc,
|
|
CPLHashSetEqualFunc fnEqualFunc,
|
|
CPLHashSetFreeEltFunc fnFreeEltFunc)
|
|
{
|
|
CPLHashSet* set = (CPLHashSet*) CPLMalloc(sizeof(CPLHashSet));
|
|
set->fnHashFunc = (fnHashFunc) ? fnHashFunc : CPLHashSetHashPointer;
|
|
set->fnEqualFunc = (fnEqualFunc) ? fnEqualFunc : CPLHashSetEqualPointer;
|
|
set->fnFreeEltFunc = fnFreeEltFunc;
|
|
set->nSize = 0;
|
|
set->tabList = (CPLList**) CPLCalloc(sizeof(CPLList*), 53);
|
|
set->nIndiceAllocatedSize = 0;
|
|
set->nAllocatedSize = 53;
|
|
#ifdef HASH_DEBUG
|
|
set->nCollisions = 0;
|
|
#endif
|
|
return set;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetSize() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Returns the number of elements inserted in the hash set
|
|
*
|
|
* Note: this is not the internal size of the hash set
|
|
*
|
|
* @param set the hash set
|
|
*
|
|
* @return the number of elements in the hash set
|
|
*/
|
|
|
|
int CPLHashSetSize(const CPLHashSet* set)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
return set->nSize;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetDestroy() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Destroys an allocated hash set.
|
|
*
|
|
* This function also frees the elements if a free function was
|
|
* provided at the creation of the hash set.
|
|
*
|
|
* @param set the hash set
|
|
*/
|
|
|
|
void CPLHashSetDestroy(CPLHashSet* set)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
for(int i=0;i<set->nAllocatedSize;i++)
|
|
{
|
|
if (set->fnFreeEltFunc)
|
|
{
|
|
CPLList* cur = set->tabList[i];
|
|
while(cur)
|
|
{
|
|
set->fnFreeEltFunc(cur->pData);
|
|
cur = cur->psNext;
|
|
}
|
|
}
|
|
CPLListDestroy(set->tabList[i]);
|
|
}
|
|
CPLFree(set->tabList);
|
|
CPLFree(set);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetForeach() */
|
|
/************************************************************************/
|
|
|
|
|
|
/**
|
|
* Walk through the hash set and runs the provided function on all the
|
|
* elements
|
|
*
|
|
* This function is provided the user_data argument of CPLHashSetForeach.
|
|
* It must return TRUE to go on the walk through the hash set, or FALSE to
|
|
* make it stop.
|
|
*
|
|
* Note : the structure of the hash set must *NOT* be modified during the
|
|
* walk.
|
|
*
|
|
* @param set the hash set.
|
|
* @param fnIterFunc the function called on each element.
|
|
* @param user_data the user data provided to the function.
|
|
*/
|
|
|
|
void CPLHashSetForeach(CPLHashSet* set,
|
|
CPLHashSetIterEltFunc fnIterFunc,
|
|
void* user_data)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
if (!fnIterFunc) return;
|
|
|
|
for(int i=0;i<set->nAllocatedSize;i++)
|
|
{
|
|
CPLList* cur = set->tabList[i];
|
|
while(cur)
|
|
{
|
|
if (fnIterFunc(cur->pData, user_data) == FALSE)
|
|
return;
|
|
|
|
cur = cur->psNext;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetRehash() */
|
|
/************************************************************************/
|
|
|
|
static void CPLHashSetRehash(CPLHashSet* set)
|
|
{
|
|
int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize];
|
|
CPLList** newTabList = (CPLList**) CPLCalloc(sizeof(CPLList*), nNewAllocatedSize);
|
|
#ifdef HASH_DEBUG
|
|
CPLDebug("CPLHASH", "hashSet=%p, nSize=%d, nCollisions=%d, fCollisionRate=%.02f",
|
|
set, set->nSize, set->nCollisions, set->nCollisions * 100.0 / set->nSize);
|
|
set->nCollisions = 0;
|
|
#endif
|
|
for(int i=0;i<set->nAllocatedSize;i++)
|
|
{
|
|
CPLList* cur = set->tabList[i];
|
|
while(cur)
|
|
{
|
|
unsigned long nNewHashVal = set->fnHashFunc(cur->pData) % nNewAllocatedSize;
|
|
#ifdef HASH_DEBUG
|
|
if (newTabList[nNewHashVal])
|
|
set->nCollisions ++;
|
|
#endif
|
|
newTabList[nNewHashVal] = CPLListInsert(newTabList[nNewHashVal], cur->pData, 0);
|
|
cur = cur->psNext;
|
|
}
|
|
CPLListDestroy(set->tabList[i]);
|
|
}
|
|
CPLFree(set->tabList);
|
|
set->tabList = newTabList;
|
|
set->nAllocatedSize = nNewAllocatedSize;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetFindPtr() */
|
|
/************************************************************************/
|
|
|
|
static void** CPLHashSetFindPtr(CPLHashSet* set, const void* elt)
|
|
{
|
|
unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
|
|
CPLList* cur = set->tabList[nHashVal];
|
|
while(cur)
|
|
{
|
|
if (set->fnEqualFunc(cur->pData, elt))
|
|
return &cur->pData;
|
|
cur = cur->psNext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetInsert() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Inserts an element into a hash set.
|
|
*
|
|
* If the element was already inserted in the hash set, the previous
|
|
* element is replaced by the new element. If a free function was provided,
|
|
* it is used to free the previously inserted element
|
|
*
|
|
* @param set the hash set
|
|
* @param elt the new element to insert in the hash set
|
|
*
|
|
* @return TRUE if the element was not already in the hash set
|
|
*/
|
|
|
|
int CPLHashSetInsert(CPLHashSet* set, void* elt)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
void** pElt = CPLHashSetFindPtr(set, elt);
|
|
if (pElt)
|
|
{
|
|
if (set->fnFreeEltFunc)
|
|
set->fnFreeEltFunc(*pElt);
|
|
|
|
*pElt = elt;
|
|
return FALSE;
|
|
}
|
|
|
|
if (set->nSize >= 2 * set->nAllocatedSize / 3)
|
|
{
|
|
set->nIndiceAllocatedSize++;
|
|
CPLHashSetRehash(set);
|
|
}
|
|
|
|
unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
|
|
#ifdef HASH_DEBUG
|
|
if (set->tabList[nHashVal])
|
|
set->nCollisions ++;
|
|
#endif
|
|
set->tabList[nHashVal] = CPLListInsert(set->tabList[nHashVal], (void*) elt, 0);
|
|
set->nSize++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetLookup() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Returns the element found in the hash set corresponding to the element to look up
|
|
* The element must not be modified.
|
|
*
|
|
* @param set the hash set
|
|
* @param elt the element to look up in the hash set
|
|
*
|
|
* @return the element found in the hash set or NULL
|
|
*/
|
|
|
|
void* CPLHashSetLookup(CPLHashSet* set, const void* elt)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
void** pElt = CPLHashSetFindPtr(set, elt);
|
|
if (pElt)
|
|
return *pElt;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetRemove() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Removes an element from a hash set
|
|
*
|
|
* @param set the hash set
|
|
* @param elt the new element to remove from the hash set
|
|
*
|
|
* @return TRUE if the element was in the hash set
|
|
*/
|
|
|
|
int CPLHashSetRemove(CPLHashSet* set, const void* elt)
|
|
{
|
|
CPLAssert(set != NULL);
|
|
if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2)
|
|
{
|
|
set->nIndiceAllocatedSize--;
|
|
CPLHashSetRehash(set);
|
|
}
|
|
|
|
int nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
|
|
CPLList* cur = set->tabList[nHashVal];
|
|
CPLList* prev = NULL;
|
|
while(cur)
|
|
{
|
|
if (set->fnEqualFunc(cur->pData, elt))
|
|
{
|
|
if (prev)
|
|
prev->psNext = cur->psNext;
|
|
else
|
|
set->tabList[nHashVal] = cur->psNext;
|
|
|
|
if (set->fnFreeEltFunc)
|
|
set->fnFreeEltFunc(cur->pData);
|
|
|
|
CPLFree(cur);
|
|
#ifdef HASH_DEBUG
|
|
if (set->tabList[nHashVal])
|
|
set->nCollisions --;
|
|
#endif
|
|
|
|
set->nSize--;
|
|
return TRUE;
|
|
}
|
|
prev = cur;
|
|
cur = cur->psNext;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetHashPointer() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Hash function for an arbitrary pointer
|
|
*
|
|
* @param elt the arbitrary pointer to hash
|
|
*
|
|
* @return the hash value of the pointer
|
|
*/
|
|
|
|
unsigned long CPLHashSetHashPointer(const void* elt)
|
|
{
|
|
return (unsigned long)(GUIntBig) elt;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetEqualPointer() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Equality function for arbitrary pointers
|
|
*
|
|
* @param elt1 the first arbitrary pointer to compare
|
|
* @param elt2 the second arbitrary pointer to compare
|
|
*
|
|
* @return TRUE if the pointers are equal
|
|
*/
|
|
|
|
int CPLHashSetEqualPointer(const void* elt1, const void* elt2)
|
|
{
|
|
return elt1 == elt2;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetHashStr() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Hash function for a zero-terminated string
|
|
*
|
|
* @param elt the string to hash. May be NULL.
|
|
*
|
|
* @return the hash value of the string
|
|
*/
|
|
|
|
unsigned long CPLHashSetHashStr(const void *elt)
|
|
{
|
|
unsigned char* pszStr = (unsigned char*)elt;
|
|
unsigned long hash = 0;
|
|
int c;
|
|
|
|
if (pszStr == NULL)
|
|
return 0;
|
|
|
|
while ((c = *pszStr++) != '\0')
|
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
|
|
|
return hash;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CPLHashSetEqualStr() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Equality function for strings
|
|
*
|
|
* @param elt1 the first string to compare. May be NULL.
|
|
* @param elt2 the second string to compare. May be NULL.
|
|
*
|
|
* @return TRUE if the strings are equal
|
|
*/
|
|
|
|
int CPLHashSetEqualStr(const void* elt1, const void* elt2)
|
|
{
|
|
const char* pszStr1 = (const char*)elt1;
|
|
const char* pszStr2 = (const char*)elt2;
|
|
if (pszStr1 == NULL && pszStr2 != NULL)
|
|
return FALSE;
|
|
else if (pszStr1 != NULL && pszStr2 == NULL)
|
|
return FALSE;
|
|
else if (pszStr1 == NULL && pszStr2 == NULL)
|
|
return TRUE;
|
|
else
|
|
return strcmp(pszStr1, pszStr2) == 0;
|
|
}
|