136 lines
3.5 KiB
C
136 lines
3.5 KiB
C
/* A specialized unordered_set implementation for literals, where bucket_count
|
|
* is defined at initialization rather than increased automatically.
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#ifndef INTSET_NAME
|
|
#error "Please #define INTSET_NAME ... before including intset.h"
|
|
#endif
|
|
|
|
#ifndef INTSET_TYPE
|
|
#error "Please #define INTSET_TYPE ... before including intset.h"
|
|
#endif
|
|
|
|
/* macros to generate unique names from INTSET_NAME */
|
|
#ifndef INTSET_CONCAT
|
|
#define INTSET_CONCAT_(a, b) a ## b
|
|
#define INTSET_CONCAT(a, b) INTSET_CONCAT_(a, b)
|
|
#define INTSET_FUNC_(a, b) INTSET_CONCAT(a, _ ## b)
|
|
#endif
|
|
|
|
#define INTSET_FUNC(name) INTSET_FUNC_(INTSET_NAME, name)
|
|
#define INTSET_BUCKET INTSET_CONCAT(INTSET_NAME, Bucket)
|
|
#define INTSET_UNION INTSET_CONCAT(INTSET_NAME, Union)
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4200)
|
|
#endif
|
|
|
|
|
|
typedef struct {
|
|
size_t count;
|
|
union INTSET_UNION {
|
|
INTSET_TYPE val;
|
|
INTSET_TYPE *data;
|
|
} v;
|
|
} INTSET_BUCKET;
|
|
|
|
typedef struct {
|
|
size_t bucket_count;
|
|
INTSET_BUCKET buckets[];
|
|
} INTSET_NAME;
|
|
|
|
static INTSET_NAME *INTSET_FUNC(new)(size_t buckets)
|
|
{
|
|
size_t i, size;
|
|
INTSET_NAME *set;
|
|
|
|
if (buckets < 1)
|
|
buckets = 1;
|
|
if ((SIZE_MAX - sizeof(INTSET_NAME)) / sizeof(INTSET_BUCKET) < buckets)
|
|
return NULL;
|
|
size = sizeof(INTSET_NAME) + buckets * sizeof(INTSET_BUCKET);
|
|
set = (INTSET_NAME*)malloc(size);
|
|
if (!set)
|
|
return NULL;
|
|
memset(set, 0, size); /* gcc -fanalyzer does not understand this sets all buckets' count to 0 */
|
|
for (i = 0; i < buckets; i++) {
|
|
set->buckets[i].count = 0;
|
|
}
|
|
set->bucket_count = buckets;
|
|
return set;
|
|
}
|
|
|
|
static void INTSET_FUNC(free)(INTSET_NAME *set)
|
|
{
|
|
size_t i;
|
|
if (!set)
|
|
return;
|
|
for (i = 0; i < set->bucket_count; i++) {
|
|
if (set->buckets[i].count > 1)
|
|
free(set->buckets[i].v.data);
|
|
}
|
|
free(set);
|
|
}
|
|
|
|
static bool INTSET_FUNC(contains)(INTSET_NAME *set, INTSET_TYPE val)
|
|
{
|
|
size_t i;
|
|
INTSET_BUCKET* bucket = &set->buckets[(size_t)val % set->bucket_count];
|
|
if (bucket->count == 1)
|
|
return bucket->v.val == val;
|
|
for (i = 0; i < bucket->count; ++i) {
|
|
if (bucket->v.data[i] == val)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool INTSET_FUNC(add)(INTSET_NAME *set, INTSET_TYPE val)
|
|
{
|
|
INTSET_BUCKET* bucket;
|
|
|
|
if (INTSET_FUNC(contains)(set, val))
|
|
return true; /* ok */
|
|
|
|
bucket = &set->buckets[(size_t)val % set->bucket_count];
|
|
if (bucket->count == 0) {
|
|
bucket->v.val = val;
|
|
bucket->count = 1;
|
|
} else if (bucket->count == 1) {
|
|
INTSET_TYPE old = bucket->v.val;
|
|
bucket->v.data = (INTSET_TYPE*)malloc(2 * sizeof(INTSET_TYPE));
|
|
if (!bucket->v.data) {
|
|
bucket->v.val = old;
|
|
return false; /* error */
|
|
}
|
|
bucket->v.data[0] = old;
|
|
bucket->v.data[1] = val;
|
|
bucket->count = 2;
|
|
} else {
|
|
size_t new_bucket_size;
|
|
INTSET_TYPE* new_bucket_data;
|
|
|
|
new_bucket_size = (bucket->count + 1) * sizeof(INTSET_TYPE);
|
|
new_bucket_data = (INTSET_TYPE*)realloc(bucket->v.data, new_bucket_size);
|
|
if (!new_bucket_data)
|
|
return false; /* error */
|
|
bucket->v.data = new_bucket_data;
|
|
bucket->v.data[bucket->count++] = val;
|
|
}
|
|
return true; /* success */
|
|
}
|
|
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#undef INTSET_FUNC
|
|
#undef INTSET_BUCKET
|
|
#undef INTSET_UNION
|