3
\$\begingroup\$

The Hashset uses open addressing, linear probing and Robin Hood hashing for handling collisions. It comes with insert and remove operations, iteration (back and forward using a single iterator), min and max keys (this is to interface with a treeset that I've made, but not very useful) and set operations like union, intersection, difference and symmetric_difference.

Concerns

  • This is the first time I've used Robin Hood hashing. It seems way too simple so I'm not sure if it is implemented correctly!
  • Is it better to have an array of entries (buckets) or an array of pointers to entries (initialized with NULL)?
  • Are there any improvements that can be done in this hashtable?

This data structure is generated using macros. They follow the same idea as two of my previous questions (this one and this one).

How to

You can use HASHSET_GENERATE to generate a hashset of any type you want. It has 4 parameters:

  • PFX - Functions prefix, or namespace;
  • SNAME - Structure name (typedef SNAME##_s SNAME;);
  • FMOD - Functions modifier (static or empty, edit: not sure if works with inline);
  • V - Your data type to be worked with.

Or you can generate each part individually using HASHSET_GENERATE_STRUCT, HASHSET_GENERATE_HEADER and HASHSET_GENERATE_SOURCE.

hashset.h

#ifndef CMC_HASHSET_H
#define CMC_HASHSET_H

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#ifndef CMC_HASH_TABLE_SETUP
#define CMC_HASH_TABLE_SETUP

typedef enum EntryState_e
{
    ES_DELETED = -1,
    ES_EMPTY = 0,
    ES_FILLED = 1
} EntryState;

static const size_t cmc_hashtable_primes[] = {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};

#endif /* CMC_HASH_TABLE_SETUP */

#define HASHSET_GENERATE(PFX, SNAME, FMOD, V)    \
    HASHSET_GENERATE_STRUCT(PFX, SNAME, FMOD, V) \
    HASHSET_GENERATE_HEADER(PFX, SNAME, FMOD, V) \
    HASHSET_GENERATE_SOURCE(PFX, SNAME, FMOD, V)

/* PRIVATE *******************************************************************/
#define HASHSET_GENERATE_HEADER_PRIVATE(PFX, SNAME, FMOD, K, V) \
    HASHSET_GENERATE_HEADER(PFX, SNAME, FMOD, V)
#define HASHSET_GENERATE_SOURCE_PRIVATE(PFX, SNAME, FMOD, K, V) \
    HASHSET_GENERATE_STRUCT(PFX, SNAME, FMOD, V)                \
    HASHSET_GENERATE_SOURCE(PFX, SNAME, FMOD, V)
/* PUBLIC ********************************************************************/
#define HASHSET_GENERATE_HEADER_PUBLIC(PFX, SNAME, FMOD, K, V) \
    HASHSET_GENERATE_STRUCT(PFX, SNAME, FMOD, V)               \
    HASHSET_GENERATE_HEADER(PFX, SNAME, FMOD, V)
#define HASHSET_GENERATE_SOURCE_PUBLIC(PFX, SNAME, FMOD, K, V) \
    HASHSET_GENERATE_SOURCE(PFX, SNAME, FMOD, V)
/* STRUCT ********************************************************************/
#define HASHSET_GENERATE_STRUCT(PFX, SNAME, FMOD, V) \
                                                     \
    struct SNAME##_s                                 \
    {                                                \
        struct SNAME##_entry_s *buffer;              \
        size_t capacity;                             \
        size_t count;                                \
        double load;                                 \
        int (*cmp)(V, V);                            \
        size_t (*hash)(V);                           \
    };                                               \
                                                     \
    struct SNAME##_entry_s                           \
    {                                                \
        V value;                                     \
        size_t dist;                                 \
        enum EntryState_e state;                     \
    };                                               \
                                                     \
    struct SNAME##_iter_s                            \
    {                                                \
        struct SNAME##_s *target;                    \
        size_t cursor;                               \
        size_t index;                                \
        size_t first;                                \
        size_t last;                                 \
        bool start;                                  \
        bool end;                                    \
    };                                               \
                                                     \
/* HEADER ********************************************************************/
#define HASHSET_GENERATE_HEADER(PFX, SNAME, FMOD, V)                                          \
                                                                                              \
    typedef struct SNAME##_s SNAME;                                                           \
    typedef struct SNAME##_entry_s SNAME##_entry;                                             \
    typedef struct SNAME##_iter_s SNAME##_iter;                                               \
                                                                                              \
    FMOD SNAME *PFX##_new(size_t size, double load, int (*compare)(V, V), size_t (*hash)(V)); \
    FMOD void PFX##_clear(SNAME *_set_);                                                      \
    FMOD void PFX##_free(SNAME *_set_);                                                       \
    FMOD bool PFX##_insert(SNAME *_set_, V element);                                          \
    FMOD bool PFX##_remove(SNAME *_set_, V element);                                          \
    FMOD bool PFX##_insert_if(SNAME *_set_, V element, bool condition);                       \
    FMOD bool PFX##_remove_if(SNAME *_set_, V element, bool condition);                       \
    FMOD V PFX##_max(SNAME *_set_);                                                           \
    FMOD V PFX##_min(SNAME *_set_);                                                           \
    FMOD bool PFX##_empty(SNAME *_set_);                                                      \
    FMOD size_t PFX##_count(SNAME *_set_);                                                    \
                                                                                              \
    FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_);                                    \
    FMOD SNAME *PFX##_intersection(SNAME *_set1_, SNAME *_set2_);                             \
    FMOD SNAME *PFX##_difference(SNAME *_set1_, SNAME *_set2_);                               \
    FMOD SNAME *PFX##_symmetric_difference(SNAME *_set1_, SNAME *_set2_);                     \
                                                                                              \
    FMOD void PFX##_iter_new(SNAME##_iter *iter, SNAME *target);                              \
    FMOD bool PFX##_iter_start(SNAME##_iter *iter);                                           \
    FMOD bool PFX##_iter_end(SNAME##_iter *iter);                                             \
    FMOD void PFX##_iter_tostart(SNAME##_iter *iter);                                         \
    FMOD void PFX##_iter_toend(SNAME##_iter *iter);                                           \
    FMOD bool PFX##_iter_next(SNAME##_iter *iter, V *value, size_t *index);                   \
    FMOD bool PFX##_iter_prev(SNAME##_iter *iter, V *value, size_t *index);                   \
                                                                                              \
/* SOURCE ********************************************************************/
#define HASHSET_GENERATE_SOURCE(PFX, SNAME, FMOD, V)                                           \
                                                                                               \
    FMOD bool PFX##_grow(SNAME *_set_);                                                        \
    FMOD SNAME##_entry *PFX##_get_entry(SNAME *_set_, V element);                              \
    FMOD size_t PFX##_calculate_size(size_t required);                                         \
                                                                                               \
    FMOD SNAME *PFX##_new(size_t size, double load, int (*compare)(V, V), size_t (*hash)(V))   \
    {                                                                                          \
        if (size == 0 || load <= 0 || load >= 1)                                               \
            return NULL;                                                                       \
                                                                                               \
        size_t real_size = PFX##_calculate_size(size);                                         \
                                                                                               \
        SNAME *_set_ = malloc(sizeof(SNAME));                                                  \
                                                                                               \
        if (!_set_)                                                                            \
            return NULL;                                                                       \
                                                                                               \
        _set_->buffer = malloc(sizeof(SNAME##_entry) * real_size);                             \
                                                                                               \
        if (!_set_->buffer)                                                                    \
        {                                                                                      \
            free(_set_);                                                                       \
            return NULL;                                                                       \
        }                                                                                      \
                                                                                               \
        memset(_set_->buffer, 0, sizeof(SNAME##_entry) * real_size);                           \
                                                                                               \
        _set_->count = 0;                                                                      \
        _set_->capacity = real_size;                                                           \
        _set_->load = load;                                                                    \
        _set_->cmp = compare;                                                                  \
        _set_->hash = hash;                                                                    \
                                                                                               \
        return _set_;                                                                          \
    }                                                                                          \
                                                                                               \
    FMOD void PFX##_clear(SNAME *_set_)                                                        \
    {                                                                                          \
        memset(_set_->buffer, 0, sizeof(SNAME##_entry) * _set_->capacity);                     \
                                                                                               \
        _set_->count = 0;                                                                      \
    }                                                                                          \
                                                                                               \
    FMOD void PFX##_free(SNAME *_set_)                                                         \
    {                                                                                          \
        free(_set_->buffer);                                                                   \
        free(_set_);                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_insert(SNAME *_set_, V element)                                            \
    {                                                                                          \
        if ((double)_set_->capacity * _set_->load <= (double)_set_->count)                     \
        {                                                                                      \
            if (!PFX##_grow(_set_))                                                            \
                return false;                                                                  \
        }                                                                                      \
                                                                                               \
        size_t hash = _set_->hash(element);                                                    \
        size_t original_pos = hash % _set_->capacity;                                          \
        size_t pos = original_pos;                                                             \
                                                                                               \
        SNAME##_entry *target = &(_set_->buffer[pos]);                                         \
                                                                                               \
        if (PFX##_get_entry(_set_, element) != NULL)                                           \
            return false;                                                                      \
                                                                                               \
        if (target->state == ES_EMPTY || target->state == ES_DELETED)                          \
        {                                                                                      \
            target->value = element;                                                           \
            target->dist = pos - original_pos;                                                 \
            target->state = ES_FILLED;                                                         \
        }                                                                                      \
        else                                                                                   \
        {                                                                                      \
            while (true)                                                                       \
            {                                                                                  \
                pos++;                                                                         \
                target = &(_set_->buffer[pos % _set_->capacity]);                              \
                                                                                               \
                if (target->state == ES_EMPTY || target->state == ES_DELETED)                  \
                {                                                                              \
                    target->value = element;                                                   \
                    target->dist = pos - original_pos;                                         \
                    target->state = ES_FILLED;                                                 \
                                                                                               \
                    break;                                                                     \
                }                                                                              \
                else if (target->dist < pos - original_pos)                                    \
                {                                                                              \
                    V tmp = target->value;                                                     \
                    size_t tmp_dist = target->dist;                                            \
                                                                                               \
                    target->value = element;                                                   \
                    target->dist = pos - original_pos;                                         \
                                                                                               \
                    element = tmp;                                                             \
                    original_pos = pos - tmp_dist;                                             \
                }                                                                              \
            }                                                                                  \
        }                                                                                      \
                                                                                               \
        _set_->count++;                                                                        \
                                                                                               \
        return true;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_remove(SNAME *_set_, V element)                                            \
    {                                                                                          \
        SNAME##_entry *result = PFX##_get_entry(_set_, element);                               \
                                                                                               \
        if (result == NULL)                                                                    \
            return false;                                                                      \
                                                                                               \
        result->value = 0;                                                                     \
        result->dist = 0;                                                                      \
        result->state = ES_DELETED;                                                            \
                                                                                               \
        _set_->count--;                                                                        \
                                                                                               \
        return true;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_insert_if(SNAME *_set_, V element, bool condition)                         \
    {                                                                                          \
        if (condition)                                                                         \
            return PFX##_insert(_set_, element);                                               \
                                                                                               \
        return false;                                                                          \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_remove_if(SNAME *_set_, V element, bool condition)                         \
    {                                                                                          \
        if (condition)                                                                         \
            return PFX##_remove(_set_, element);                                               \
                                                                                               \
        return false;                                                                          \
    }                                                                                          \
                                                                                               \
    FMOD V PFX##_max(SNAME *_set_)                                                             \
    {                                                                                          \
        if (PFX##_empty(_set_))                                                                \
            return 0;                                                                          \
                                                                                               \
        V result, max;                                                                         \
        size_t index;                                                                          \
        SNAME##_iter iter;                                                                     \
                                                                                               \
        for (PFX##_iter_new(&iter, _set_); !PFX##_iter_end(&iter);)                            \
        {                                                                                      \
            PFX##_iter_next(&iter, &result, &index);                                           \
                                                                                               \
            if (index == 0)                                                                    \
                max = result;                                                                  \
            else if (_set_->cmp(result, max) > 0)                                              \
                max = result;                                                                  \
        }                                                                                      \
                                                                                               \
        return max;                                                                            \
    }                                                                                          \
                                                                                               \
    FMOD V PFX##_min(SNAME *_set_)                                                             \
    {                                                                                          \
        if (PFX##_empty(_set_))                                                                \
            return 0;                                                                          \
                                                                                               \
        V result, min;                                                                         \
        size_t index;                                                                          \
        SNAME##_iter iter;                                                                     \
                                                                                               \
        for (PFX##_iter_new(&iter, _set_); !PFX##_iter_end(&iter);)                            \
        {                                                                                      \
            PFX##_iter_next(&iter, &result, &index);                                           \
                                                                                               \
            if (index == 0)                                                                    \
                min = result;                                                                  \
            else if (_set_->cmp(result, min) < 0)                                              \
                min = result;                                                                  \
        }                                                                                      \
                                                                                               \
        return min;                                                                            \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_empty(SNAME *_set_)                                                        \
    {                                                                                          \
        return _set_->count == 0;                                                              \
    }                                                                                          \
                                                                                               \
    FMOD size_t PFX##_count(SNAME *_set_)                                                      \
    {                                                                                          \
        return _set_->count;                                                                   \
    }                                                                                          \
                                                                                               \
    FMOD SNAME *PFX##_union(SNAME *_set1_, SNAME *_set2_)                                      \
    {                                                                                          \
        V value;                                                                               \
        size_t index;                                                                          \
        SNAME##_iter iter1, iter2;                                                             \
                                                                                               \
        SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
                                                                                               \
        if (!_set_r_)                                                                          \
            return false;                                                                      \
                                                                                               \
        PFX##_iter_new(&iter1, _set1_);                                                        \
        PFX##_iter_new(&iter2, _set2_);                                                        \
                                                                                               \
        for (PFX##_iter_tostart(&iter1); !PFX##_iter_end(&iter1);)                             \
        {                                                                                      \
            PFX##_iter_next(&iter1, &value, &index);                                           \
            PFX##_insert(_set_r_, value);                                                      \
        }                                                                                      \
                                                                                               \
        for (PFX##_iter_tostart(&iter2); !PFX##_iter_end(&iter2);)                             \
        {                                                                                      \
            PFX##_iter_next(&iter2, &value, &index);                                           \
            PFX##_insert(_set_r_, value);                                                      \
        }                                                                                      \
                                                                                               \
        return _set_r_;                                                                        \
    }                                                                                          \
                                                                                               \
    FMOD SNAME *PFX##_intersection(SNAME *_set1_, SNAME *_set2_)                               \
    {                                                                                          \
        V value;                                                                               \
        size_t index;                                                                          \
        SNAME##_iter iter;                                                                     \
                                                                                               \
        SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
                                                                                               \
        if (!_set_r_)                                                                          \
            return false;                                                                      \
                                                                                               \
        SNAME *_set_A_ = _set1_->count < _set2_->count ? _set1_ : _set2_;                      \
        SNAME *_set_B_ = _set_A_ == _set1_ ? _set2_ : _set1_;                                  \
                                                                                               \
        PFX##_iter_new(&iter, _set_A_);                                                        \
                                                                                               \
        for (PFX##_iter_tostart(&iter); !PFX##_iter_end(&iter);)                               \
        {                                                                                      \
            PFX##_iter_next(&iter, &value, &index);                                            \
                                                                                               \
            if (PFX##_get_entry(_set_B_, value) != NULL)                                       \
                PFX##_insert(_set_r_, value);                                                  \
        }                                                                                      \
                                                                                               \
        return _set_r_;                                                                        \
    }                                                                                          \
                                                                                               \
    FMOD SNAME *PFX##_difference(SNAME *_set1_, SNAME *_set2_)                                 \
    {                                                                                          \
        V value;                                                                               \
        size_t index;                                                                          \
        SNAME##_iter iter;                                                                     \
                                                                                               \
        SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
                                                                                               \
        if (!_set_r_)                                                                          \
            return false;                                                                      \
                                                                                               \
        PFX##_iter_new(&iter, _set1_);                                                         \
                                                                                               \
        for (PFX##_iter_tostart(&iter); !PFX##_iter_end(&iter);)                               \
        {                                                                                      \
            PFX##_iter_next(&iter, &value, &index);                                            \
                                                                                               \
            if (PFX##_get_entry(_set2_, value) == NULL)                                        \
                PFX##_insert(_set_r_, value);                                                  \
        }                                                                                      \
                                                                                               \
        return _set_r_;                                                                        \
    }                                                                                          \
                                                                                               \
    FMOD SNAME *PFX##_symmetric_difference(SNAME *_set1_, SNAME *_set2_)                       \
    {                                                                                          \
        V value;                                                                               \
        size_t index;                                                                          \
        SNAME##_iter iter1, iter2;                                                             \
                                                                                               \
        SNAME *_set_r_ = PFX##_new(_set1_->capacity, _set1_->load, _set1_->cmp, _set1_->hash); \
                                                                                               \
        if (!_set_r_)                                                                          \
            return false;                                                                      \
                                                                                               \
        PFX##_iter_new(&iter1, _set1_);                                                        \
        PFX##_iter_new(&iter2, _set2_);                                                        \
                                                                                               \
        for (PFX##_iter_tostart(&iter1); !PFX##_iter_end(&iter1);)                             \
        {                                                                                      \
            PFX##_iter_next(&iter1, &value, &index);                                           \
                                                                                               \
            if (PFX##_get_entry(_set2_, value) == NULL)                                        \
                PFX##_insert(_set_r_, value);                                                  \
        }                                                                                      \
                                                                                               \
        for (PFX##_iter_tostart(&iter2); !PFX##_iter_end(&iter2);)                             \
        {                                                                                      \
            PFX##_iter_next(&iter2, &value, &index);                                           \
                                                                                               \
            if (PFX##_get_entry(_set1_, value) == NULL)                                        \
                PFX##_insert(_set_r_, value);                                                  \
        }                                                                                      \
                                                                                               \
        return _set_r_;                                                                        \
    }                                                                                          \
                                                                                               \
    FMOD void PFX##_iter_new(SNAME##_iter *iter, SNAME *target)                                \
    {                                                                                          \
        iter->target = target;                                                                 \
        iter->cursor = 0;                                                                      \
        iter->index = 0;                                                                       \
        iter->start = true;                                                                    \
        iter->end = PFX##_empty(target);                                                       \
                                                                                               \
        if (!PFX##_empty(target))                                                              \
        {                                                                                      \
            for (size_t i = 0; i < target->capacity; i++)                                      \
            {                                                                                  \
                if (target->buffer[i].state == ES_FILLED)                                      \
                {                                                                              \
                    iter->first = i;                                                           \
                    break;                                                                     \
                }                                                                              \
            }                                                                                  \
                                                                                               \
            iter->cursor = iter->first;                                                        \
                                                                                               \
            for (size_t i = target->capacity; i > 0; i--)                                      \
            {                                                                                  \
                if (target->buffer[i - 1].state == ES_FILLED)                                  \
                {                                                                              \
                    iter->last = i - 1;                                                        \
                    break;                                                                     \
                }                                                                              \
            }                                                                                  \
        }                                                                                      \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_iter_start(SNAME##_iter *iter)                                             \
    {                                                                                          \
        return PFX##_empty(iter->target) || iter->start;                                       \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_iter_end(SNAME##_iter *iter)                                               \
    {                                                                                          \
        return PFX##_empty(iter->target) || iter->end;                                         \
    }                                                                                          \
                                                                                               \
    FMOD void PFX##_iter_tostart(SNAME##_iter *iter)                                           \
    {                                                                                          \
        iter->cursor = iter->first;                                                            \
        iter->index = 0;                                                                       \
        iter->start = true;                                                                    \
        iter->end = PFX##_empty(iter->target);                                                 \
    }                                                                                          \
                                                                                               \
    FMOD void PFX##_iter_toend(SNAME##_iter *iter)                                             \
    {                                                                                          \
        iter->cursor = iter->last;                                                             \
        iter->index = iter->target->count - 1;                                                 \
        iter->start = PFX##_empty(iter->target);                                               \
        iter->end = true;                                                                      \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_iter_next(SNAME##_iter *iter, V *value, size_t *index)                     \
    {                                                                                          \
        if (iter->end)                                                                         \
            return false;                                                                      \
                                                                                               \
        SNAME##_entry *scan = &(iter->target->buffer[iter->cursor]);                           \
                                                                                               \
        *value = scan->value;                                                                  \
        *index = iter->index;                                                                  \
                                                                                               \
        if (iter->cursor == iter->last)                                                        \
            iter->end = true;                                                                  \
        else                                                                                   \
        {                                                                                      \
            iter->index++;                                                                     \
                                                                                               \
            while (1)                                                                          \
            {                                                                                  \
                iter->cursor++;                                                                \
                scan = &(iter->target->buffer[iter->cursor]);                                  \
                                                                                               \
                if (scan->state == ES_FILLED)                                                  \
                    break;                                                                     \
            }                                                                                  \
        }                                                                                      \
                                                                                               \
        iter->start = PFX##_empty(iter->target);                                               \
                                                                                               \
        return true;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_iter_prev(SNAME##_iter *iter, V *value, size_t *index)                     \
    {                                                                                          \
        if (iter->start)                                                                       \
            return false;                                                                      \
                                                                                               \
        SNAME##_entry *scan = &(iter->target->buffer[iter->cursor]);                           \
                                                                                               \
        *value = scan->value;                                                                  \
        *index = iter->index;                                                                  \
                                                                                               \
        if (iter->cursor == iter->first)                                                       \
            iter->start = true;                                                                \
        else                                                                                   \
        {                                                                                      \
            iter->index--;                                                                     \
                                                                                               \
            while (1)                                                                          \
            {                                                                                  \
                iter->cursor--;                                                                \
                scan = &(iter->target->buffer[iter->cursor]);                                  \
                                                                                               \
                if (scan->state == ES_FILLED)                                                  \
                    break;                                                                     \
            }                                                                                  \
        }                                                                                      \
                                                                                               \
        iter->end = PFX##_empty(iter->target);                                                 \
                                                                                               \
        return true;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD bool PFX##_grow(SNAME *_set_)                                                         \
    {                                                                                          \
        size_t new_size = PFX##_calculate_size((size_t)((double)_set_->capacity * 1.5));       \
                                                                                               \
        SNAME *_new_set_ = PFX##_new(new_size, _set_->load, _set_->cmp, _set_->hash);          \
                                                                                               \
        if (!_new_set_)                                                                        \
            return false;                                                                      \
                                                                                               \
        V value;                                                                               \
        size_t index;                                                                          \
        SNAME##_iter iter;                                                                     \
                                                                                               \
        for (PFX##_iter_new(&iter, _set_); !PFX##_iter_end(&iter);)                            \
        {                                                                                      \
            PFX##_iter_next(&iter, &value, &index);                                            \
            PFX##_insert(_new_set_, value);                                                    \
        }                                                                                      \
                                                                                               \
        if (_set_->count != _new_set_->count)                                                  \
        {                                                                                      \
            PFX##_free(_new_set_);                                                             \
                                                                                               \
            return false;                                                                      \
        }                                                                                      \
                                                                                               \
        SNAME##_entry *tmp = _set_->buffer;                                                    \
        _set_->buffer = _new_set_->buffer;                                                     \
        _new_set_->buffer = tmp;                                                               \
                                                                                               \
        _set_->capacity = _new_set_->capacity;                                                 \
                                                                                               \
        PFX##_free(_new_set_);                                                                 \
                                                                                               \
        return true;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD SNAME##_entry *PFX##_get_entry(SNAME *_set_, V element)                               \
    {                                                                                          \
        size_t hash = _set_->hash(element);                                                    \
        size_t pos = hash % _set_->capacity;                                                   \
                                                                                               \
        SNAME##_entry *target = &(_set_->buffer[pos % _set_->capacity]);                       \
                                                                                               \
        while (target->state == ES_FILLED || target->state == ES_DELETED)                      \
        {                                                                                      \
            if (_set_->cmp(target->value, element) == 0)                                       \
                return target;                                                                 \
                                                                                               \
            pos++;                                                                             \
            target = &(_set_->buffer[pos % _set_->capacity]);                                  \
        }                                                                                      \
                                                                                               \
        return NULL;                                                                           \
    }                                                                                          \
                                                                                               \
    FMOD size_t PFX##_calculate_size(size_t required)                                          \
    {                                                                                          \
        const size_t count = sizeof(cmc_hashtable_primes) / sizeof(cmc_hashtable_primes[0]);   \
                                                                                               \
        if (cmc_hashtable_primes[count - 1] < required)                                        \
            return required;                                                                   \
                                                                                               \
        size_t i = 0;                                                                          \
        while (cmc_hashtable_primes[i] < required)                                             \
            i++;                                                                               \
                                                                                               \
        return cmc_hashtable_primes[i];                                                        \
    }

#endif /* CMC_HASHSET_H */

EXAMPLE

#include <stdio.h>
#include <stdlib.h>
#include "hashset.h"

HASHSET_GENERATE(set, hash_set, /* static */, int)

void print_set(hash_set *s)
{
    size_t index;
    int result;
    hash_set_iter iter;
    set_iter_new(&iter, s);
    for (set_iter_tostart(&iter); !set_iter_end(&iter);)
    {
        set_iter_next(&iter, &result, &index);
        if (index == 0)
            printf("[ %2d, ", result);
        else if (index == s->count - 1)
            printf("%2d ]\n", result);
        else
            printf("%2d, ", result);
    }
}

int intcmp(int a, int b)
{
    return a - b;
}

size_t inthash(int t)
{
    size_t a = t;
    a += ~(a << 15);
    a ^= (a >> 10);
    a += (a << 3);
    a ^= (a >> 6);
    a += ~(a << 11);
    a ^= (a >> 16);
    return a;
}

int main(int argc, char const *argv[])
{
    hash_set *set1 = set_new(50, 0.9, intcmp, inthash);
    hash_set *set2 = set_new(50, 0.9, intcmp, inthash);

    for (int i = 1; i <= 20; i++)
        set_insert(set1, i);

    for (int i = 11; i <= 30; i++)
        set_insert(set2, i);

    hash_set *set3 = set_union(set1, set2);

    print_set(set1);
    printf("UNION\n");
    print_set(set2);
    printf("=\n");
    print_set(set3);

    printf("\n\n");

    hash_set *set4 = set_intersection(set1, set2);

    print_set(set1);
    printf("INTERSECTION\n");
    print_set(set2);
    printf("=\n");
    print_set(set4);

    printf("\n\n");

    hash_set *set5 = set_difference(set1, set2);

    print_set(set1);
    printf("DIFFERENCE\n");
    print_set(set2);
    printf("=\n");
    print_set(set5);

    printf("\n\n");

    hash_set *set6 = set_symmetric_difference(set1, set2);

    print_set(set1);
    printf("SYMMETRIC DIFFERENCE\n");
    print_set(set2);
    printf("=\n");
    print_set(set6);

    set_free(set1);
    set_free(set2);
    set_free(set3);
    set_free(set4);
    set_free(set5);
    set_free(set6);

    return 0;
}
\$\endgroup\$
6
  • 1
    \$\begingroup\$ An array of entries has the advantage of cache coherence, but it's bigger in memory and only supports a fixed size of entry. From Wikipedia, "If the table is expected to have a high load factor, the records are large, or the data is variable-sized, chained hash tables often perform as well or better." So, it depends? \$\endgroup\$
    – Neil
    Commented Apr 12, 2019 at 22:25
  • \$\begingroup\$ Is it possible to have an associative array with this? It seems to me that that's not enough parameters. \$\endgroup\$
    – Neil
    Commented Apr 13, 2019 at 0:58
  • 1
    \$\begingroup\$ @NeilEdelman To make a Hashmap is very very very close to the Hashset. In fact I have it implemented here. \$\endgroup\$
    – LeoVen
    Commented Apr 13, 2019 at 1:43
  • \$\begingroup\$ I still don't see how this is enough parameters. In your example, how do you link intcmp and inthash into your header? \$\endgroup\$
    – Neil
    Commented Apr 13, 2019 at 19:59
  • 1
    \$\begingroup\$ Ahh, I see how it's done now. If you coded it in HASHSET_GENERATE instead of the constructor, all the same hashset types would be compatible. And really, how much do you really have two or more equiality relations? I mean, I guess you could, but I would think it's rare. \$\endgroup\$
    – Neil
    Commented Apr 14, 2019 at 22:09

1 Answer 1

3
\$\begingroup\$

... used Robin Hood hashing. It seems way too simple so I'm not sure if it is implemented correctly!

No comment.

Is it better to have an array of entries (buckets) or an array of pointers to entries (initialized with NULL)?

6.01 or half-dozen of the other. Depends on use case.

I prefer a dynamic array of entries for less (perceived) fragmentation.

Are there any improvements that can be done in this hashtable?

Lack of comments.

I'd expect something to help indicate usage and limitation of the various generated functions in the .h files. Perhaps even a commented terse example?

Put that How to in the .h

Name space impact.

hashset.h creates

CMC_HASHSET_...
CMC_HASH_...
EntryState...
ES_...
cmc_hashtable...
HASHSET_...

I'd expect a more uniform name space usage. Example

cmcht.h
cmcht_...
CMCHT_...

Assumed range

size_t cmc_hashtable_primes[] = {53, 97, 193, 389, ... 402653189, 805306457, 1610612741 assumes size_t is 32 bit. For wider portability, conditionally handle 16 to 64 bit.

Unclear why table lacked entries near 2,000,000,000 and 4,000,000,000.

Why double math?

(size_t)((double)_set_->capacity * 1.5)
vs.
_set_->capacity + _set_->capacity/2

If concerned about overflow, a prior test is useful.

Linear search vs binary

With dozens of values to search, instead of a linear search while (cmc_hashtable_primes[i] < required) i++;, perhaps a binary one?

Allocate to sizeof de-referenced pointer

Original code was hard to review for correctnesses.

_set_->buffer = malloc(sizeof(SNAME##_entry) * real_size); 
// vs
_set_->buffer = malloc(sizeof *_set_->buffer * real_size); 

Good use of size_t/bool

Good use of prime numbers in hashing

size_t hash = _set_->hash(element);                                                    
size_t original_pos = hash % _set_->capacity; 

related

Main test

I'd re-order includes to insure "hashset.h" stands on its own

//#include <stdio.h>
//#include <stdlib.h>
#include "hashset.h"
#include <stdio.h>
#include <stdlib.h>

It appears the .h code is designed to handle multiple HASHSET_GENERATE(). If so, good to demo it.

A full range int compare for intcmp(int a, int b) without potential UB is return (a > b) - (a < b)


Bonus

OP had "only go to 32 bit integers is because I took them from here." so I extended to 64-bit.

0x               3                    3
0x               7                    7
0x               D                   13
0x              1D                   29
0x              35                   53
0x              61                   97
0x              C1                  193
0x             185                  389
0x             301                  769
0x             607                 1543
0x             C07                 3079
0x            1807                 6151
0x            3001                12289
0x            6011                24593
0x            C005                49157
0x           1800D                98317
0x           30005               196613
0x           60019               393241
0x           C0001               786433
0x          180005              1572869
0x          30000B              3145739
0x          60000D              6291469
0x          C00005             12582917
0x         1800013             25165843
0x         3000005             50331653
0x         6000017            100663319
0x         C000013            201326611
0x        18000005            402653189
0x        30000059            805306457
0x        60000005           1610612741
0x        C0000001           3221225473
0x       180000017           6442450967
0x       300000005          12884901893
0x       600000017          25769803799
0x       C0000002F          51539607599
0x      1800000007         103079215111
0x      3000000001         206158430209
0x      6000000019         412316860441
0x      C000000005         824633720837
0x     18000000011        1649267441681
0x     30000000059        3298534883417
0x     60000000001        6597069766657
0x     C0000000025       13194139533349
0x    18000000002F       26388279066671
0x    300000000037       52776558133303
0x    60000000000D      105553116266509
0x    C00000000037      211106232533047
0x   1800000000011      422212465066001
0x   3000000000059      844424930132057
0x   6000000000011     1688849860263953
0x   C000000000019     3377699720527897
0x  18000000000053     6755399441055827
0x  3000000000001F    13510798882111519
0x  6000000000005F    27021597764223071
0x  C0000000000005    54043195528445957
0x 180000000000025   108086391056891941
0x 300000000000023   216172782113783843
0x 600000000000005   432345564227567621
0x C00000000000031   864691128455135281
0x1800000000000011  1729382256910270481
0x3000000000000005  3458764513820540933
0x600000000000002F  6917529027641081903
0xC000000000000011 13835058055282163729
0xFFFFFFFFFFFFFFC5 18446744073709551557  extra
\$\endgroup\$
4
  • \$\begingroup\$ Your implementation of intcmp performs two comparisons while only 1.5 of them are actually required. \$\endgroup\$
    – bipll
    Commented Apr 13, 2019 at 13:34
  • 1
    \$\begingroup\$ @bipll Both (a > b) - (a < b) and the "1.5 compare" approaches are O(1) complexity. Compilers readily emit efficient code from (a > b) - (a < b) so to assess either is a micro-optimization. My goal is first correct functionality over range which a - b does not have. \$\endgroup\$
    – chux
    Commented Apr 13, 2019 at 13:39
  • \$\begingroup\$ Hi. First, thanks for the review! About the prime numbers and why they only go to 32 bit integers is because I took them from here. I still couldn't find prime numbers that go up to 64 bit values. \$\endgroup\$
    – LeoVen
    Commented Apr 13, 2019 at 20:38
  • \$\begingroup\$ @LeoVen Finding primes (each number is as far as possible from the nearest two powers of two) up to 2^64 looks like a fun programming task.... ;-) \$\endgroup\$
    – chux
    Commented Apr 14, 2019 at 2:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.