/** * Add a new name to the hashset and allocate it a unique id * @param hs the hashset in question * @param prop the property to add * @return 1 if successful, else 0 */ int hashset_put( hashset *hs, char *prop ) { unsigned slot; struct hs_bucket *b; if ( (float)hs->num_keys/(float)hs->num_buckets > MAX_RATIO ) { if ( !hashset_rehash(hs) ) return 0; } slot = hash(prop,strlen(prop))%hs->num_buckets; b = hs->buckets[slot]; if ( b == NULL ) { hs->buckets[slot] = hs_bucket_create(prop,hs->id++); if ( hs->buckets[slot] == NULL ) return 0; } else { do { // if key already present, just return if ( strcmp(prop,b->key)==0 ) return 0; else if ( b->next != NULL ) b = b->next; } while ( b->next != NULL ); // key not found b->next = hs_bucket_create(prop,hs->id++); if ( b->next == NULL ) return 0; } hs->num_keys++; return 1; }
/* Add k:d to ht. If k was already in ht, replace old entry by k:d. Rehash if necessary. Returns TRUE if k was not already in ht. */ HASH_FUNC_INLINE bool hashset_table_insert(hashset_table ht, hash_fn hash, keyeq_fn cmp, hash_key k) { assert(ht); if (ht->log2size == 0) { // array mode // replace? size_t i; for (i = 0; i < ht->used; ++i) { hash_key key = ht->table[i]; if (key == k || cmp(key, k)) { ht->table[i] = k; return FALSE; /* replace */ } } if (ht->used >= HASHSET_TABLE_ARRAY_THRESHOLD) { hashset_table_convert_to_hash_mode(ht, hash, cmp); // hash mode now assert(ht->log2size != 0); hashset_table_insert_internal(ht, hash, cmp, k); ht->used += 1; } else { // Resize. We depend on the system realloc being smart and only // re-allocating if necessary (i.e. between 4 and 8 bytes, or if nothing // after, don't need to reallocate). ht->table = realloc(ht->table, (sizeof (hash_key))*(ht->used + 1)); // append ht->table[ht->used] = k; ht->used += 1; } return TRUE; /* new key */ } else { // hash mode if (ht->used > 3 * hashset_table_capacity(ht) / 4) hashset_rehash(ht, hash, cmp); if (ht->used >= HASHSET_TABLE_MAXSIZE) { abort(); } size_t hashVal = hashset_find_bucket(ht, hash, k); size_t curIndex = hashVal; size_t tombstoneIndex = (size_t) -1; size_t i = 0; while (1) { hash_key key = ht->table[curIndex]; if (key == NULL) { if (tombstoneIndex != (size_t) -1) { assert(ht->table[tombstoneIndex] == HASHSET_TOMBSTONE); ht->table[tombstoneIndex] = k; // don't increment ht->used since it already counts tombstones. } else { ht->table[curIndex] = k; ht->used++; } return TRUE; /* new key */ } if (key == HASHSET_TOMBSTONE) { // quarl 2006-10-24 // We can replace this tombstone entry with the new key. BUT, we // still have to see if the key is currently in the table. So keep // looking, and if we don't find it, then replace the first tombstone // we found with the new key. Thanks to Simon Goldsmith & trendprof. if (tombstoneIndex == (size_t) -1) { tombstoneIndex = curIndex; } } if (hashset_do_cmp(key, k, cmp)) { // Update the data. // // quarl 2006-05-28: // We *must* update the key since keys are potentially mutable and // thus not the same even if they compare the same; not updating // will cause inconsistencies later. ht->table[curIndex] = k; return FALSE; /* replace */ } ++i; curIndex = hashset_probe(ht, hashVal, i); assert(i < hashset_table_capacity(ht)); // can't be 100% full - would have rehashed assert(curIndex != hashVal); } } }