|
@@ -66,35 +66,163 @@ struct hashtable_t *hash_new(int size);
|
|
|
* fiddles with hash-internals. */
|
|
|
void *hash_remove_bucket(struct hashtable_t *hash, struct hash_it_t *hash_it_t);
|
|
|
|
|
|
+/* free only the hashtable and the hash itself. */
|
|
|
+void hash_destroy(struct hashtable_t *hash);
|
|
|
+
|
|
|
/* remove the hash structure. if hashdata_free_cb != NULL, this function will be
|
|
|
* called to remove the elements inside of the hash. if you don't remove the
|
|
|
* elements, memory might be leaked. */
|
|
|
-void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb, void *arg);
|
|
|
+static inline void hash_delete(struct hashtable_t *hash,
|
|
|
+ hashdata_free_cb free_cb, void *arg)
|
|
|
+{
|
|
|
+ struct element_t *bucket, *last_bucket;
|
|
|
+ int i;
|
|
|
|
|
|
-/* free only the hashtable and the hash itself. */
|
|
|
-void hash_destroy(struct hashtable_t *hash);
|
|
|
+ for (i = 0; i < hash->size; i++) {
|
|
|
+ bucket = hash->table[i];
|
|
|
+
|
|
|
+ while (bucket != NULL) {
|
|
|
+ if (free_cb != NULL)
|
|
|
+ free_cb(bucket->data, arg);
|
|
|
+
|
|
|
+ last_bucket = bucket;
|
|
|
+ bucket = bucket->next;
|
|
|
+ kfree(last_bucket);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ hash_destroy(hash);
|
|
|
+}
|
|
|
|
|
|
/* adds data to the hashtable. returns 0 on success, -1 on error */
|
|
|
-int hash_add(struct hashtable_t *hash, hashdata_compare_cb compare,
|
|
|
- hashdata_choose_cb choose, void *data);
|
|
|
+static inline int hash_add(struct hashtable_t *hash,
|
|
|
+ hashdata_compare_cb compare,
|
|
|
+ hashdata_choose_cb choose, void *data)
|
|
|
+{
|
|
|
+ int index;
|
|
|
+ struct element_t *bucket, *prev_bucket = NULL;
|
|
|
+
|
|
|
+ if (!hash)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ index = choose(data, hash->size);
|
|
|
+ bucket = hash->table[index];
|
|
|
+
|
|
|
+ while (bucket != NULL) {
|
|
|
+ if (compare(bucket->data, data))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ prev_bucket = bucket;
|
|
|
+ bucket = bucket->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* found the tail of the list, add new element */
|
|
|
+ bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC);
|
|
|
+
|
|
|
+ if (bucket == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ bucket->data = data;
|
|
|
+ bucket->next = NULL;
|
|
|
+
|
|
|
+ /* and link it */
|
|
|
+ if (prev_bucket == NULL)
|
|
|
+ hash->table[index] = bucket;
|
|
|
+ else
|
|
|
+ prev_bucket->next = bucket;
|
|
|
+
|
|
|
+ hash->elements++;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
/* removes data from hash, if found. returns pointer do data on success, so you
|
|
|
* can remove the used structure yourself, or NULL on error . data could be the
|
|
|
* structure you use with just the key filled, we just need the key for
|
|
|
* comparing. */
|
|
|
-void *hash_remove(struct hashtable_t *hash, hashdata_compare_cb compare,
|
|
|
- hashdata_choose_cb choose, void *data);
|
|
|
+static inline void *hash_remove(struct hashtable_t *hash,
|
|
|
+ hashdata_compare_cb compare,
|
|
|
+ hashdata_choose_cb choose, void *data)
|
|
|
+{
|
|
|
+ struct hash_it_t hash_it_t;
|
|
|
+
|
|
|
+ hash_it_t.index = choose(data, hash->size);
|
|
|
+ hash_it_t.bucket = hash->table[hash_it_t.index];
|
|
|
+ hash_it_t.prev_bucket = NULL;
|
|
|
+
|
|
|
+ while (hash_it_t.bucket != NULL) {
|
|
|
+ if (compare(hash_it_t.bucket->data, data)) {
|
|
|
+ hash_it_t.first_bucket =
|
|
|
+ (hash_it_t.bucket ==
|
|
|
+ hash->table[hash_it_t.index] ?
|
|
|
+ &hash->table[hash_it_t.index] : NULL);
|
|
|
+ return hash_remove_bucket(hash, &hash_it_t);
|
|
|
+ }
|
|
|
+
|
|
|
+ hash_it_t.prev_bucket = hash_it_t.bucket;
|
|
|
+ hash_it_t.bucket = hash_it_t.bucket->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
|
|
|
/* finds data, based on the key in keydata. returns the found data on success,
|
|
|
* or NULL on error */
|
|
|
-void *hash_find(struct hashtable_t *hash, hashdata_compare_cb compare,
|
|
|
- hashdata_choose_cb choose, void *keydata);
|
|
|
+static inline void *hash_find(struct hashtable_t *hash,
|
|
|
+ hashdata_compare_cb compare,
|
|
|
+ hashdata_choose_cb choose, void *keydata)
|
|
|
+{
|
|
|
+ int index;
|
|
|
+ struct element_t *bucket;
|
|
|
+
|
|
|
+ if (!hash)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ index = choose(keydata , hash->size);
|
|
|
+ bucket = hash->table[index];
|
|
|
+
|
|
|
+ while (bucket != NULL) {
|
|
|
+ if (compare(bucket->data, keydata))
|
|
|
+ return bucket->data;
|
|
|
+
|
|
|
+ bucket = bucket->next;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
|
|
|
/* resize the hash, returns the pointer to the new hash or NULL on
|
|
|
* error. removes the old hash on success */
|
|
|
-struct hashtable_t *hash_resize(struct hashtable_t *hash,
|
|
|
- hashdata_compare_cb compare,
|
|
|
- hashdata_choose_cb choose, int size);
|
|
|
+static inline struct hashtable_t *hash_resize(struct hashtable_t *hash,
|
|
|
+ hashdata_compare_cb compare,
|
|
|
+ hashdata_choose_cb choose,
|
|
|
+ int size)
|
|
|
+{
|
|
|
+ struct hashtable_t *new_hash;
|
|
|
+ struct element_t *bucket;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* initialize a new hash with the new size */
|
|
|
+ new_hash = hash_new(size);
|
|
|
+
|
|
|
+ if (new_hash == NULL)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* copy the elements */
|
|
|
+ for (i = 0; i < hash->size; i++) {
|
|
|
+ bucket = hash->table[i];
|
|
|
+
|
|
|
+ while (bucket != NULL) {
|
|
|
+ hash_add(new_hash, compare, choose, bucket->data);
|
|
|
+ bucket = bucket->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* remove hash and eventual overflow buckets but not the content
|
|
|
+ * itself. */
|
|
|
+ hash_delete(hash, NULL, NULL);
|
|
|
+
|
|
|
+ return new_hash;
|
|
|
+}
|
|
|
|
|
|
/* iterate though the hash. first element is selected with iter_in NULL. use
|
|
|
* the returned iterator to access the elements until hash_it_t returns NULL. */
|