1

I want to create a hashmap with uthash.

I want the key and the value to be a struct, containing a String and a size_t like this:

typedef struct hash_ptr {
    char* string;
    size_t len;
}hash_ptr;

The hash table itself looks like this:

typedef struct hash_map_entry {
    struct hash_ptr *key;
    struct hash_ptr *value;
    UT_hash_handle hh;
}hash_map_entry;

In order to add new entries to the map I wrote a new function called add_entry():

void add_entry(hash_map_entry *map, hash_ptr *key, hash_ptr *value) {
    hash_map_entry *entry;
    HASH_FIND(hh, map, key, sizeof *key, entry);
    if (entry == NULL) {
        entry = (hash_map_entry*) malloc(sizeof *entry);
        memset(entry, 0, sizeof *entry);
        entry->value = value;
        entry->key = key;
        HASH_ADD(hh, map, key, sizeof *key, entry);
    }
}

However, after initializing and calling add_entry()...

hash_map_entry *map = NULL;

hash_ptr *key = (hash_ptr*) malloc(sizeof *key);
memset(key, 0, sizeof *key);
key->string = "Is this the Krusty Krab?";
key->len = strlen(key->string);

hash_ptr *value = (hash_ptr*) malloc(sizeof *value);
memset(value, 0, sizeof *value);
value->string = "No, this is Patrick!";
value->len = strlen(value->string);

add_entry(map, key, value);

...HASH_FIND doesn't find the added entry:

hash_map_entry *find_me;
HASH_FIND(hh, map, key, sizeof *key, find_me);

and find_me is NULL.

I followed the Instructions for using a struct as a key from the official user guide.

Where am I wrong?

rn42v
  • 97
  • 6
  • Does this work: `HASH_ADD_KEYPTR(hh, map, key->string, key->len, entry);` and `HASH_FIND(hh, map, key->string, key->len, find_me);`? – Ian Abbott Nov 07 '19 at 17:17
  • @IanAbbott Unfortunately not, same result. – rn42v Nov 07 '19 at 17:23
  • I think you'll need to resort to defining your own `HASH_FUNCTION` and `HASH_KEYCMP` macros as described in [Specifying an alternate key comparison function](http://troydhanson.github.io/uthash/userguide.html#hash_keycompare). Or change the `key` to a `char *` and either store the key length separately, or assume the key is a null terminated string pointer and use the "String _pointer_ in structure" example in [String keys](http://troydhanson.github.io/uthash/userguide.html#_string_keys). – Ian Abbott Nov 07 '19 at 17:44
  • @IanAbbott According [this](http://troydhanson.github.io/uthash/userguide.html#_structure_keys) using a struct as a key should work. – rn42v Nov 07 '19 at 17:50
  • @m42v1r Yes, as long as struct padding is cleared and the pointers inside the structs being compared match. The question is, are you comparing pointers or the things the pointers point to? – Ian Abbott Nov 07 '19 at 17:55
  • @IanAbbott the things pointers point to – rn42v Nov 07 '19 at 17:58
  • It looks like you need to change the first parameter of `add_entry` to `hash_map_entry **map`, replace `map` with `*map` inside the function, and call `add_entry` with `&map` instead of `map`. See "Passing the hash pointer into functions" in the uthash user guide. I think your current version of `add_entry` makes the `map` parameter point to a new hash table, but the caller's `map` variable is unchanged and still NULL. – Ian Abbott Nov 07 '19 at 18:24
  • @IanAbbott Yes, now after add_entry() is called the map variable has changed. However HASH_FIND() still return NULL – rn42v Nov 07 '19 at 18:40

1 Answers1

1

This is the simplest modification I can come up with. Changes from the original are:

  1. Change the first parameter of add_entry from hash_map_entry *map to hash_map_entry **map and adjust the code accordingly.

  2. Use HASH_ADD_KEYPTR to hash the contents of the string inside struct hash_ptr instead of hashing the struct hash_ptr itself. NOTE: the code assumes that the len member is the length of the the object pointed to by the string member, in bytes.

  3. Related to 2, change the usage of HASH_FIND to hash the contents of the string inside struct hash_ptr.

Here is the result:

#include <stdio.h>
#include <stdlib.h>
#include "uthash.h"

typedef struct hash_ptr {
    char* string;
    size_t len;
}hash_ptr;

typedef struct hash_map_entry {
    struct hash_ptr *key;
    struct hash_ptr *value;
    UT_hash_handle hh;
}hash_map_entry;

void add_entry(hash_map_entry **map, hash_ptr *key, hash_ptr *value) {
    hash_map_entry *entry;
    HASH_FIND(hh, *map, key->string, key->len, entry);
    if (entry == NULL) {
        entry = (hash_map_entry*) malloc(sizeof *entry);
        memset(entry, 0, sizeof *entry);
        entry->value = value;
        entry->key = key;
        HASH_ADD_KEYPTR(hh, *map, key->string, key->len, entry);
    }
}

int main(void)
{
    hash_map_entry *map = NULL;

    hash_ptr *key = (hash_ptr*) malloc(sizeof *key);
    memset(key, 0, sizeof *key);
    key->string = "Is this the Krusty Krab?";
    key->len = strlen(key->string);

    hash_ptr *value = (hash_ptr*) malloc(sizeof *value);
    memset(value, 0, sizeof *value);
    value->string = "No, this is Patrick!";
    value->len = strlen(value->string);

    add_entry(&map, key, value);

    hash_map_entry *find_me;
    HASH_FIND(hh, map, key->string, key->len, find_me);
    if (find_me)
    {
        printf("found key=\"%s\", val=\"%s\"\n", find_me->key->string, find_me->value->string);
    }
    else
    {
        printf("not found\n");
    }
    return 0;
}
Ian Abbott
  • 15,083
  • 19
  • 33