+/*
+ * A basic implementation of a hash-set. Unlike a hashtable, a hash
+ * set doesn't maintain key-value pairs. It simply maintains a key
+ * that can be set, removed, and checked for.
+ *
+ * See EXPOSED interface comment below
+ */
+#define GMQCC_HASHSET_PRIME0 0x0049
+#define GMQCC_HASHSET_PRIME1 0x1391
+
+static int util_hsput(hash_set_t *set, void *item) {
+ size_t hash = (size_t)item; /* shouldn't drop the bits */
+ size_t iter;
+
+ /* a == 0 || a == 1 */
+ if (hash >> 1)
+ return -1;
+
+ iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
+
+ /* while (set->items[iter] != 0 && set->items[iter] != 1) */
+ while (!(set->items[iter] >> 1)) {
+ if (set->items[iter] == hash)
+ return 0;
+
+ iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
+ }
+
+ set->total ++;
+ set->items[iter] = hash;
+
+ return 1;
+}
+
+static void util_hsupdate(hash_set_t *set) {
+ size_t *old;
+ size_t end;
+ size_t itr;
+
+ /* time to rehash? */
+ if ((float)set->total >= (size_t)((double)set->capacity * 0.85)) {
+ old = set->items;
+ end = set->capacity;
+
+ set->bits ++;
+ set->capacity = (size_t)(1 << set->bits);
+ set->mask = set->capacity - 1;
+ set->items = (size_t*)mem_a(set->capacity * sizeof(size_t));
+ set->total = 0;
+
+ /*assert(set->items);*/
+
+ /*
+ * this shouldn't be slow? if so unroll it a little perhaps
+ * (shouldn't be though)
+ */
+ for (itr = 0; itr < end; itr++)
+ util_hsput(set, (void*)old[itr]);
+
+ mem_d(old);
+ }
+}
+
+/*
+ * EXPOSED interface: all of these functions are exposed to the outside
+ * for use. The stuff above is static because it's the "internal" mechanics
+ * for syncronizing the set for updating, and putting data into the set.
+ */
+int util_hsadd(hash_set_t *set, void *item) {
+ int run = util_hsput(set, item); /* inlined */
+ util_hsupdate(set);
+
+ return run;
+}
+
+/* remove item in set */
+int util_hsrem(hash_set_t *set, void *item) {
+ size_t hash = (size_t)item;
+ size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
+
+ while (set->items[iter]) {
+ if (set->items[iter] == hash) {
+ set->items[iter] = 1;
+ set->total --;
+
+ return 1;
+ }
+ iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
+ }
+
+ return 0;
+}
+
+/* check if item is set */
+int util_hshas(hash_set_t *set, void *item) {
+ size_t hash = (size_t)item;
+ size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
+
+ while (set->items[iter]) {
+ if (set->items[iter] == hash)
+ return 1;
+
+ iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
+ }
+
+ return 0;
+}
+
+hash_set_t *util_hsnew(void) {
+ hash_set_t *set;
+
+ if (!(set = (hash_set_t*)mem_a(sizeof(hash_set_t))))
+ return NULL;
+
+ set->bits = 3;
+ set->total = 0;
+ set->capacity = (size_t)(1 << set->bits);
+ set->mask = set->capacity - 1;
+ set->items = (size_t*)mem_a(set->capacity * sizeof(size_t));
+
+ if (!set->items) {
+ util_hsdel(set);
+ return NULL;
+ }
+
+ return set;
+}
+
+void util_hsdel(hash_set_t *set) {
+ if (!set) return;
+
+ if (set->items)
+ mem_d(set->items);
+
+ mem_d(set);
+}
+#undef GMQCC_HASHSET_PRIME0
+#undef GMQCC_HASHSET_PRIME1
+
+
+/*
+ * Portable implementation of vasprintf/asprintf. Assumes vsnprintf
+ * exists, otherwise compiler error.
+ *
+ * TODO: fix for MSVC ....
+ */
+int util_vasprintf(char **dat, const char *fmt, va_list args) {
+ int ret;
+ int len;
+ char *tmp = NULL;
+
+ /*
+ * For visuals tido _vsnprintf doesn't tell you the length of a
+ * formatted string if it overflows. However there is a MSVC
+ * intrinsic (which is documented wrong) called _vcsprintf which
+ * will return the required amount to allocate.
+ */
+ #ifdef _MSC_VER
+ char *str;
+ if ((len = _vscprintf(fmt, args)) < 0) {
+ *dat = NULL;
+ return -1;
+ }
+
+ tmp = mem_a(len + 1);
+ if ((ret = _vsnprintf(tmp, len+1, fmt, args)) != len) {
+ mem_d(tmp);
+ *dat = NULL;
+ return -1;
+ }
+ *dat = tmp;
+ return len;
+ #else
+ /*
+ * For everything else we have a decent conformint vsnprintf that
+ * returns the number of bytes needed. We give it a try though on
+ * a short buffer, since efficently speaking, it could be nice to
+ * above a second vsnprintf call.
+ */
+ char buf[128];
+ va_list cpy;
+ va_copy(cpy, args);
+ len = vsnprintf(buf, sizeof(buf), fmt, cpy);
+ va_end (cpy);
+
+ if (len < (int)sizeof(buf)) {
+ *dat = util_strdup(buf);
+ return len;
+ }
+
+ /* not large enough ... */
+ tmp = (char*)mem_a(len + 1);
+ if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) {
+ mem_d(tmp);
+ *dat = NULL;
+ return -1;
+ }
+
+ *dat = tmp;
+ return len;
+ #endif
+}
+int util_asprintf(char **ret, const char *fmt, ...) {
+ va_list args;
+ int read;
+ va_start(args, fmt);
+ read = util_vasprintf(ret, fmt, args);
+ va_end (args);
+
+ return read;
+}
+