Skip to content

TRIA/hashtab

Repository files navigation

This is a set of utility functions to create a hash table that works with either unsigned integer or string key, storing a void * , int, or unsigned int value.

The algorithm used is "hash buckets with chaining". A hash table is created with some number of buckets, each of which is a linked list head, and as new entries are added to the hashtable, the key is hashed and used to select one of the buckets. Entries are linked into the list corresponding to the bucket that their key hashes to. The hashing functions are fixed, but number of buckets and amount of space for the entries is set at hash table creation time, up to an optional maximum limit of entries.

The number of buckets is intended to reduce the length of list that has to be serially searched when an entry is added or looked up, so the number of buckets chosen is a tradeoff between space and CPU time. Some choices of number of buckets work much better than others with the supplied hashing functions and with the type of keys (e.g., random vs. sequential integers) being used. Some recommended bucket counts will be provided in documentation (for example, 47 and 49 work better than 51 for random integer keys).

There is only one kind of hash table, users can use a particular table with either unsigned int keys or string keys, but MUST NOT mix the two types of keys in a single hash table. The insertion and lookup functions specify the key type, not the hash table.

At present, memory used for the list entries and buckets is allocated using malloc() and is never freed. (This can be changed in the future if desired.)

Each hash table is managed as a dynamic rsrc resource, created dynamically. This allows hash table statistics to be printed using the rsrc PrintLong function, or by calling the corresponding hash table print function directly.

Entries can be added or modified:

   iHtIAddVal (table1, 25, (void *)value1);
   iHtISetVal (table1, 25, (void *)250);
   iHtSAddVal (table2, "key string", (void *)99);

Or looked up:

    printf ("value for key %d is %d\n", 25, (int) iHtIGetVal (table1, 25));

Or iterated through:

   htFOREACH(it,w,table1) {
        printf("key %d, value is: %d\n",  w-> ulKey, (int) w->pxValue);
   }

Or deleted:

   iHtSDelete (table2, "key string");
   iHtIDelete (table1, 25);

This is an adaptation of existing code, and was modified to use with FreeRTOS conventions. Here's the main part of the .h file:

#define htFORLOOP(walker,iterator) for(hashent_t *walker; (walker = pxHtIteratorNext (&iterator));)
#define htFOREACH(it,w,tab) htIterator_t it; vHtInitIterator(&it,tab); htFORLOOP(w,it)

typedef struct _htHashent {
	union  {
		Link_t xLinks;		// link to next/prev in this bucket
//		dlList_t xLinks;	// link to next/prev in this bucket
		struct _htHashent *pxFreelist;	// freelist link if entry is not in use
	};
	union {					// SI functions decide which to use, we don't care
		const char	*pcName;	// string key associated with this entry
		unsigned ulKey;		// integer key
	};
	union {
		void	*pxValue;	// value (being last forces struct alignment)
		unsigned ulValue;	// alternative access to reduce casting
		int 	lValue;
	};
} hashent_t;

typedef struct {
	Link_t *pxBuckets;		// The buckets -- an array of list heads
//	dlList_t *pxBuckets;		// The buckets -- an array of list heads
	hashent_t *pxFreelist; 	// freelist of allocated but not in use entries
	const char *pcTablename;	// name of this table, for logging/stats purposes
	unsigned ulBucketCount;	// size of buckets array at 'buckets'
							// if ==1, it's a serial search, hashing does nothing
	unsigned ulMaxEntries;	// max # entries allowed (absolute cap)
	unsigned ulCurEntries;	// count of current entries
	unsigned ulAllocSize:16;	// entries added if needed in blocks of this many
	unsigned xHasString:1;	// set if a string key has been added to the hash
	unsigned xHasInt:1;		// set if an integer key has been added to the hash
	unsigned _unused:14;	// RFU
} hashtab_t;

// allocate and initialize a new hash table, returns a pointer to it
hashtab_t *pxHtNewHashTable (const char *tablename, unsigned initentries,
						   unsigned maxentries, unsigned entryincrement,
						   unsigned numbuckets);

// Create an entry in the hash table.  It is an error to add an entry with
// an existing key, a zero will be returned.  Success is a non-zero return.
int iHtIAddVal (hashtab_t *table, unsigned key, void *value);
int iHtSAddVal (hashtab_t *table, const char *name, void *value);

// Add or modify an entry in the hash table.  The old value is replaced by the
// new one if the entry is found, and a new entry added to store the value if not.
// return value is the number of entries added, 0 or 1
int iHtISetVal (hashtab_t *table, unsigned key, void *value);
int iHtSSetVal (hashtab_t *table, const char *name, void *value);

// low-level routines that find list entries as hashent_t
hashent_t * pxHtIFindEntry (hashtab_t *table, unsigned key);
hashent_t * pxHtSFindEntry (hashtab_t *table, const char *name);

// Lookup routines to get just value, returns the value or NULL if not found
void *pvHtSGetVal (hashtab_t *table, const char *name);
void *pvHtIGetVal (hashtab_t *table, unsigned key);

// delete an entry from the hash table -- caller responsible for objects pointed to.
// I,S cases return number of deleted items, 0 or 1. EDelete assumes valid hashent_t.
int iHtSDelete (hashtab_t *table, const char *name);
int iHtIDelete (hashtab_t *table, unsigned key);
void vHtEDelete (hashent_t *entry);

// if compiled-in, print statistics of hash table
void vHtPrintStats (hashtab_t *table);

// {
//		htFOREACH(it,ht,hashtable) {
//			use ht->value etc.
//		}
		
void vHtInitIterator (htIterator_t *it, hashtab_t *table);
hashent_t *pxHtIteratorNext (htIterator_t *it);

#endif

About

General hash table routines for integer and string keys, storing a (void *) value

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published