// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* A hash table. Example, hashing nul-terminated char*s: hash_hash_t str_hash (void *v) { char *s; hash_hash_t hash = 0; for (s = *(char **)v; *s != 0; s++) { hash = (hash ^ *s) * 2654435769U; } return (hash); } int str_eq (void *a, void *b) { return (strcmp (*(char **)a, *(char **)b) == 0); } void str_del (void *arg, void *data) { *(char **)arg = *(char **)data; } struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); ... 3=> 2**3 entries initial size ... 12=> 2**12 entries before sprouting sub-tables ... 15=> number of adjacent probes to attempt before growing Example lookup: char *key = "foobar"; char **result_ptr; if (hash_lookup (h, &key, (void **) &result_ptr)) { printf ("found in table: %s\n", *result_ptr); } else { printf ("not found in table\n"); } Example insertion: char *key = strdup ("foobar"); char **result_ptr; if (hash_lookup (h, &key, (void **) &result_ptr)) { printf ("found in table: %s\n", *result_ptr); printf ("to overwrite, do *result_ptr = key\n"); } else { printf ("not found in table; inserted as %s\n", *result_ptr); assert (*result_ptr == key); } Example deletion: char *key = "foobar"; char *result; if (hash_remove (h, &key, &result)) { printf ("key found and deleted from table\n"); printf ("called str_del (&result, data) to copy data to result: %s\n", result); } else { printf ("not found in table\n"); } Example iteration over the elements of *h: char **data; struct hash_iter it; hash_iter_init (h, &it); for (data = hash_next (&it); data != 0; data = hash_next (&it)) { printf ("%s\n", *data); } */ #define malloc runtime·mal #define memset(a,b,c) runtime·memclr((byte*)(a), (uint32)(c)) #define memcpy(a,b,c) runtime·memmove((byte*)(a),(byte*)(b),(uint32)(c)) #define assert(a) if(!(a)) runtime·throw("hashmap assert") #define free(x) runtime·free(x) #define memmove(a,b,c) runtime·memmove(a, b, c) struct Hmap; /* opaque */ struct hash_subtable; /* opaque */ struct hash_entry; /* opaque */ typedef uintptr uintptr_t; typedef uintptr_t hash_hash_t; struct hash_iter { uint8* data; /* returned from next */ int32 elemsize; /* size of elements in table */ int32 changes; /* number of changes observed last time */ int32 i; /* stack pointer in subtable_state */ bool cycled; /* have reached the end and wrapped to 0 */ hash_hash_t last_hash; /* last hash value returned */ hash_hash_t cycle; /* hash value where we started */ struct Hmap *h; /* the hash table */ MapType *t; /* the map type */ struct hash_iter_sub { struct hash_entry *e; /* pointer into subtable */ struct hash_entry *start; /* start of subtable */ struct hash_entry *last; /* last entry in subtable */ } subtable_state[4]; /* Should be large enough unless the hashing is so bad that many distinct data values hash to the same hash value. */ }; /* Return a hashtable h 2**init_power empty entries, each with "datasize" data bytes. (*data_hash)(a) should return the hash value of data element *a. (*data_eq)(a,b) should return whether the data at "a" and the data at "b" are equal. (*data_del)(arg, a) will be invoked when data element *a is about to be removed from the table. "arg" is the argument passed to "hash_remove()". Growing is accomplished by resizing if the current tables size is less than a threshold, and by adding subtables otherwise. hint should be set the expected maximum size of the table. "datasize" should be in [sizeof (void*), ..., 255]. If you need a bigger "datasize", store a pointer to another piece of memory. */ //struct hash *hash_new (int32 datasize, // hash_hash_t (*data_hash) (void *), // int32 (*data_eq) (void *, void *), // void (*data_del) (void *, void *), // int64 hint); /* Lookup *data in *h. If the data is found, return 1 and place a pointer to the found element in *pres. Otherwise return 0 and place 0 in *pres. */ // int32 hash_lookup (struct hash *h, void *data, void **pres); /* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) where p points to the data in the table, then remove it from *h and return 1. Otherwise return 0. */ // int32 hash_remove (struct hash *h, void *data, void *arg); /* Lookup *data in *h. If the data is found, return 1, and place a pointer to the found element in *pres. Otherwise, return 0, allocate a region for the data to be inserted, and place a pointer to the inserted element in *pres; it is the caller's responsibility to copy the data to be inserted to the pointer returned in *pres in this case. If using garbage collection, it is the caller's responsibility to add references for **pres if HASH_ADDED is returned. */ // int32 hash_insert (struct hash *h, void *data, void **pres); /* Return the number of elements in the table. */ // uint32 hash_count (struct hash *h); /* The following call is useful only if not using garbage collection on the table. Remove all sub-tables associated with *h. This undoes the effects of hash_init(). If other memory pointed to by user data must be freed, the caller is responsible for doiing do by iterating over *h first; see hash_iter_init()/hash_next(). */ // void hash_destroy (struct hash *h); /*----- iteration -----*/ /* Initialize *it from *h. */ // void hash_iter_init (struct hash *h, struct hash_iter *it); /* Return the next used entry in the table which which *it was initialized. */ // void *hash_next (struct hash_iter *it); /*---- test interface ----*/ /* Call (*data_visit) (arg, level, data) for every data entry in the table, whether used or not. "level" is the subtable level, 0 means first level. */ /* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ // void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg);