[Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
[Knuth] The Art of Computer Programming, part 3 (6.4) */
-#define HTAB_SIZE 1021
+#define HTAB_SIZE 1021UL // *MUST* be a prime number!!
typedef struct htab_entry {
unsigned long used;
} htab_entry;
static htab_entry *htab_table = NULL;
-static usbi_mutex_t htab_write_mutex = NULL;
-static unsigned long htab_size, htab_filled;
-
-/* For the used double hash method the table size has to be a prime. To
- correct the user given table size we need a prime test. This trivial
- algorithm is adequate because the code is called only during init and
- the number is likely to be small */
-static int isprime(unsigned long number)
-{
- // no even number will be passed
- unsigned int divider = 3;
-
- while((divider * divider < number) && (number % divider != 0))
- divider += 2;
-
- return (number % divider != 0);
-}
+static usbi_mutex_t htab_mutex = NULL;
+static unsigned long htab_filled;
/* Before using the hash table we must allocate memory for it.
We allocate one element more as the found prime number says.
This is done for more effective indexing as explained in the
comment for the hash function. */
-static bool htab_create(struct libusb_context *ctx, unsigned long nel)
+static bool htab_create(struct libusb_context *ctx)
{
if (htab_table != NULL) {
usbi_err(ctx, "hash table already allocated");
}
// Create a mutex
- usbi_mutex_init(&htab_write_mutex);
+ usbi_mutex_init(&htab_mutex);
- // Change nel to the first prime number not smaller as nel.
- nel |= 1;
- while (!isprime(nel))
- nel += 2;
-
- htab_size = nel;
- usbi_dbg("using %lu entries hash table", nel);
+ usbi_dbg("using %lu entries hash table", HTAB_SIZE);
htab_filled = 0;
// allocate memory and zero out.
- htab_table = calloc(htab_size + 1, sizeof(htab_entry));
+ htab_table = calloc(HTAB_SIZE + 1, sizeof(htab_entry));
if (htab_table == NULL) {
usbi_err(ctx, "could not allocate space for hash table");
return false;
if (htab_table == NULL)
return;
- for (i = 0; i < htab_size; i++) {
- if (htab_table[i].used)
- safe_free(htab_table[i].str);
- }
+ for (i = 0; i < HTAB_SIZE; i++)
+ free(htab_table[i].str);
- usbi_mutex_destroy(&htab_write_mutex);
safe_free(htab_table);
+
+ usbi_mutex_destroy(&htab_mutex);
}
/* This is the search function. It uses double hashing with open addressing.
++r;
// compute table hash: simply take the modulus
- hval = r % htab_size;
+ hval = r % HTAB_SIZE;
if (hval == 0)
++hval;
// Try the first index
idx = hval;
+ // Mutually exclusive access (R/W lock would be better)
+ usbi_mutex_lock(&htab_mutex);
+
if (htab_table[idx].used) {
- if ((htab_table[idx].used == hval) && (safe_strcmp(str, htab_table[idx].str) == 0))
- return idx; // existing hash
+ if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
+ goto out_unlock; // existing hash
usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str);
// Second hash function, as suggested in [Knuth]
- hval2 = 1 + hval % (htab_size - 2);
+ hval2 = 1 + hval % (HTAB_SIZE - 2);
do {
// Because size is prime this guarantees to step through all available indexes
if (idx <= hval2)
- idx = htab_size + idx - hval2;
+ idx = HTAB_SIZE + idx - hval2;
else
idx -= hval2;
break;
// If entry is found use it.
- if ((htab_table[idx].used == hval) && (safe_strcmp(str, htab_table[idx].str) == 0))
- return idx;
+ if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
+ goto out_unlock;
} while (htab_table[idx].used);
}
// Not found => New entry
// If the table is full return an error
- if (htab_filled >= htab_size) {
- usbi_err(NULL, "hash table is full (%d entries)", htab_size);
- return 0;
+ if (htab_filled >= HTAB_SIZE) {
+ usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE);
+ idx = 0;
+ goto out_unlock;
}
- // Concurrent threads might be storing the same entry at the same time
- // (eg. "simultaneous" enums from different threads) => use a mutex
- usbi_mutex_lock(&htab_write_mutex);
- // Just free any previously allocated string (which should be the same as
- // new one). The possibility of concurrent threads storing a collision
- // string (same hash, different string) at the same time is extremely low
- safe_free(htab_table[idx].str);
- htab_table[idx].used = hval;
htab_table[idx].str = _strdup(str);
if (htab_table[idx].str == NULL) {
usbi_err(NULL, "could not duplicate string for hash table");
- usbi_mutex_unlock(&htab_write_mutex);
- return 0;
+ idx = 0;
+ goto out_unlock;
}
+
+ htab_table[idx].used = hval;
++htab_filled;
- usbi_mutex_unlock(&htab_write_mutex);
+
+out_unlock:
+ usbi_mutex_unlock(&htab_mutex);
return idx;
}
if (!windows_init_clock(ctx))
goto error_roll_back;
- if (!htab_create(ctx, HTAB_SIZE))
+ if (!htab_create(ctx))
goto error_roll_back;
return LIBUSB_SUCCESS;