HOME

#include <stdbool.h>

struct StringSetEntry
{
    struct StringSetEntry* pNext;
    unsigned int HashValue;
    const char Key[];
};

struct StringSet
{
    struct StringSetEntry** pHashTable;
    unsigned int nHashTableSize;
    int Count;
};

#define STRING_SET_INIT \
  { NULL, 100, 0 }


bool StringSet_Find(struct StringSet* pMap, const char* Key);
bool StringSet_Remove(struct StringSet* pMap, const char* Key);
void StringSet_RemoveAll(struct StringSet* pMap);
int StringSet_Add(struct StringSet* pMap, const char* Key);
void StringSet_Destroy(struct StringSet* pMap);
#include "StringSet.h"
#include <stdlib.h>
#include <string.h>

static unsigned int String2_HashKey(const char* Key)
{
    // hash key to unsigned int value by pseudorandomizing transform
    // (algorithm copied from STL string hash in xfunctional)
    unsigned int uHashVal = 2166136261U;
    unsigned int uFirst = 0;
    unsigned int uLast = (unsigned int) strlen(Key);
    unsigned int uStride = 1 + uLast / 10;

    for (; uFirst < uLast; uFirst += uStride)
    {
        uHashVal = 16777619U * uHashVal ^ (unsigned int) Key[uFirst];
    }

    return (uHashVal);
}

void StringSet_RemoveAll(struct StringSet* pMap)
{
    if (pMap->pHashTable != NULL)
    {
        for (unsigned int nHash = 0; nHash < pMap->nHashTableSize; nHash++)
        {
            struct StringSetEntry* pKeyValue = pMap->pHashTable[nHash];

            while (pKeyValue != NULL)
            {
                struct StringSetEntry* pKeyValueCurrent = pKeyValue;
                pKeyValue = pKeyValue->pNext;
                free(pKeyValueCurrent);
            }
            pMap->pHashTable[nHash] = NULL;
        }
        
        pMap->Count = 0;
    }
}

void StringSet_Destroy(struct StringSet* pMap)
{
    StringSet_RemoveAll(pMap);
    free(pMap->pHashTable);
    pMap->pHashTable = NULL;
}

static struct StringSetEntry* StringSet_GetAssocAt(struct StringSet* pMap,
    const char* Key,
    unsigned int* nHashBucket,
    unsigned int* HashValue)
{
    if (pMap->pHashTable == NULL)
    {
        *HashValue = 0;
        *nHashBucket = 0;
        return NULL;
    }

    *HashValue = String2_HashKey(Key);
    *nHashBucket = *HashValue % pMap->nHashTableSize;

    struct StringSetEntry* pResult = NULL;

    struct StringSetEntry* pKeyValue = pMap->pHashTable[*nHashBucket];

    for (; pKeyValue != NULL; pKeyValue = pKeyValue->pNext)
    {
        if (pKeyValue->HashValue == *HashValue &&
            strcmp(pKeyValue->Key, Key) == 0)
        {
            pResult = pKeyValue;
            break;
        }
    }

    return pResult;
}

bool StringSet_Find(struct StringSet* pMap, const char* Key)
{
    bool bResult = false;

    unsigned int nHashBucket, HashValue;
    struct StringSetEntry* pKeyValue = StringSet_GetAssocAt(pMap, Key, &nHashBucket, &HashValue);

    return pKeyValue != NULL;
}


bool StringSet_Remove(struct StringSet* pMap, const char* Key)
{
    bool bResult = false;

    if (pMap->pHashTable != NULL)
    {
        unsigned int HashValue = String2_HashKey(Key);

        struct StringSetEntry** ppKeyValuePrev =
            &pMap->pHashTable[HashValue % pMap->nHashTableSize];

        struct StringSetEntry* pKeyValue = *ppKeyValuePrev;

        for (; pKeyValue != NULL; pKeyValue = pKeyValue->pNext)
        {
            if ((pKeyValue->HashValue == HashValue) &&
                (strcmp(pKeyValue->Key, Key) == 0))
            {
                // remove from list
                *ppKeyValuePrev = pKeyValue->pNext;
                free(pKeyValue);
                bResult = true;
                pMap->Count--;
                break;
            }

            ppKeyValuePrev = &pKeyValue->pNext;
        }
    }

    return bResult;
}

int StringSet_Add(struct StringSet* pMap, const char* Key)
{
    int result = 0;

    if (pMap->pHashTable == NULL)
    {
        if (pMap->nHashTableSize < 1)
        {
            pMap->nHashTableSize = 1000;
        }

        struct StringSetEntry** pHashTable =
            (struct StringSetEntry**) malloc(sizeof(struct StringSetEntry*) * pMap->nHashTableSize);

        if (pHashTable != NULL)
        {
            memset(pHashTable, 0, sizeof(struct StringSetEntry*) * pMap->nHashTableSize);
            pMap->pHashTable = pHashTable;
        }
    }

    if (pMap->pHashTable != NULL)
    {
        unsigned int nHashBucket, HashValue;
        struct StringSetEntry* pKeyValue =
            StringSet_GetAssocAt(pMap, Key, &nHashBucket, &HashValue);

        if (pKeyValue == NULL)
        {
            pKeyValue = (struct StringSetEntry*)malloc(sizeof(struct StringSetEntry) + strlen(Key) * sizeof(char) + 1);
            pKeyValue->HashValue = HashValue;
            strcpy((char*) pKeyValue->Key, Key);

            pKeyValue->pNext = pMap->pHashTable[nHashBucket];
            pMap->pHashTable[nHashBucket] = pKeyValue;
            pMap->Count++;
            result = 1; //inserted
        }
        else
        {
            //already exist
        }
    }
    else
    {
        result = -1;//out of memory
    }

    return result;
}