NetBSD/external/bsd/top/dist/hash.m4c

667 lines
14 KiB
Plaintext
Executable File

/*
* Copyright (c) 1984 through 2008, William LeFebvre
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of William LeFebvre nor the names of other
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* hash.m4c */
/* The file hash.c is generated from hash.m4c via the preprocessor M4 */
/*
* Hash table functions: init, add, lookup, first, next, sizeinfo.
* This is a conventional "bucket hash". The key is hashed in to a number
* less than or equal to the number of buckets and the result is used
* to index in to the array of buckets. Each bucket is a linked list
* that contains all the key/value pairs which hashed to that index.
*/
#include "config.h"
#if STDC_HEADERS
#include <string.h>
#include <stdlib.h>
#define memzero(a, b) memset((a), 0, (b))
#else /* !STDC_HEADERS */
#ifdef HAVE_MEMCPY
#define memzero(a, b) memset((a), 0, (b))
#else
#define memcpy(a, b, c) bcopy((b), (a), (c))
#define memzero(a, b) bzero((a), (b))
#define memcmp(a, b, c) bcmp((a), (b), (c))
#endif /* HAVE_MEMCPY */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#endif
void *malloc();
void free();
char *strdup();
#endif /* !STDC_HEADERS */
/* After all that there are still some systems that don't have NULL defined */
#ifndef NULL
#define NULL 0
#endif
#ifdef HAVE_MATH_H
#include <math.h>
#endif
#if !HAVE_PID_T
typedef long pid_t;
#endif
#if !HAVE_ID_T
typedef long id_t;
#endif
#include "hash.h"
dnl # The m4 code uses MALLOC, FREE, and STRDUP for dynamic allocation.
dnl # You can change what these get mapped to here:
define(`MALLOC', `malloc($1)')
define(`FREE', `free($1)')
define(`STRDUP', `strdup($1)')
static int
next_prime(int x)
{
double i, j;
int f;
i = x;
while (i++)
{
f=1;
for (j=2; j<i; j++)
{
if ((i/j)==floor(i/j))
{
f=0;
break;
}
}
if (f)
{
return (int)i;
}
}
return 1;
}
/* string hashes */
static int
string_hash(hash_table *ht, char *key)
{
unsigned long s = 0;
unsigned char ch;
int shifting = 24;
while ((ch = (unsigned char)*key++) != '\0')
{
if (shifting == 0)
{
s = s + ch;
shifting = 24;
}
else
{
s = s + (ch << shifting);
shifting -= 8;
}
}
return (s % ht->num_buckets);
}
void ll_init(llist *q)
{
q->head = NULL;
q->count = 0;
}
llistitem *ll_newitem(int size)
{
llistitem *qi;
qi = (llistitem *)MALLOC(sizeof(llistitem) + size);
qi->datum = ((void *)qi + sizeof(llistitem));
return qi;
}
void ll_freeitem(llistitem *li)
{
FREE(li);
}
void ll_add(llist *q, llistitem *new)
{
new->next = q->head;
q->head = new;
q->count++;
}
void ll_extract(llist *q, llistitem *qi, llistitem *last)
{
if (last == NULL)
{
q->head = qi->next;
}
else
{
last->next = qi->next;
}
qi->next = NULL;
q->count--;
}
#define LL_FIRST(q) ((q)->head)
llistitem *
ll_first(llist *q)
{
return q->head;
}
#define LL_NEXT(q, qi) ((qi) != NULL ? (qi)->next : NULL)
llistitem *
ll_next(llist *q, llistitem *qi)
{
return (qi != NULL ? qi->next : NULL);
}
#define LL_ISEMPTY(ll) ((ll)->count == 0)
int
ll_isempty(llist *ll)
{
return (ll->count == 0);
}
/*
* hash_table *hash_create(int num)
*
* Creates a hash table structure with at least "num" buckets.
*/
hash_table *
hash_create(int num)
{
hash_table *result;
bucket_t *b;
int bytes;
int i;
/* create the resultant structure */
result = (hash_table *)MALLOC(sizeof(hash_table));
/* adjust bucket count to be prime */
num = next_prime(num);
/* create the buckets */
bytes = sizeof(bucket_t) * num;
result->buckets = b = (bucket_t *)MALLOC(bytes);
result->num_buckets = num;
/* create each bucket as a linked list */
i = num;
while (--i >= 0)
{
ll_init(&(b->list));
b++;
}
return result;
}
/*
* unsigned int hash_count(hash_table *ht)
*
* Return total number of elements contained in hash table.
*/
unsigned int
hash_count(hash_table *ht)
{
register int i = 0;
register int cnt = 0;
register bucket_t *bucket;
bucket = ht->buckets;
while (i++ < ht->num_buckets)
{
cnt += bucket->list.count;
bucket++;
}
return cnt;
}
/*
* void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
*
* Fill in "sizes" with information about bucket lengths in hash
* table "max".
*/
void
hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
{
register int i;
register int idx;
register bucket_t *bucket;
memzero(sizes, max * sizeof(unsigned int));
bucket = ht->buckets;
i = 0;
while (i++ < ht->num_buckets)
{
idx = bucket->list.count;
sizes[idx >= max ? max-1 : idx]++;
bucket++;
}
}
dnl # HASH_TABLE_TMPL(suffix, keytype, to_hash, to_cmp, to_alloc, to_free)
dnl #
dnl # This generates hash table functions suitable for keys
dnl # of type "keytype". The function names all end with "suffix".
dnl # "to_hash" is an expression that generates a hash index (this
dnl # expression can include key and ht). "to_cmp" is an expression
dnl # that compares "key" to "k1". "to_alloc" is an expression that
dnl # allocates space for "key", or empty if no allocation is needed.
dnl # "to_free" is an expression that frees "key", or empty if no
dnl # allocation is needed.
define(`HASH_TABLE_TMPL', `
/*
* void hash_add_$1(hash_table *ht, $2 key, void *value)
*
* Add an element to table "ht". The element is described by
* "key" and "value". Return NULL on success. If the key
* already exists in the table, then no action is taken and
* the data for the existing element is returned.
* Key type is $2
*/
void *
hash_add_$1(hash_table *ht, $2 key, void *value)
{
bucket_t *bucket;
hash_item_$1 *hi;
hash_item_$1 *h;
llist *ll;
llistitem *li;
llistitem *newli;
$2 k1;
/* allocate the space we will need */
newli = ll_newitem(sizeof(hash_item_$1));
hi = (hash_item_$1 *)newli->datum;
/* fill in the values */
hi->key = ifelse($5, , key, $5);
hi->value = value;
/* hash to the bucket */
bucket = &(ht->buckets[$3]);
/* walk the list to make sure we do not have a duplicate */
ll = &(bucket->list);
li = LL_FIRST(ll);
while (li != NULL)
{
h = (hash_item_$1 *)li->datum;
k1 = h->key;
if ($4)
{
/* found one */
break;
}
li = LL_NEXT(ll, li);
}
li = NULL;
/* is there already one there? */
if (li == NULL)
{
/* add the unique element to the buckets list */
ll_add(&(bucket->list), newli);
return NULL;
}
else
{
/* free the stuff we just allocated */
ll_freeitem(newli);
return ((hash_item_$1 *)(li->datum))->value;
}
}
/*
* void *hash_replace_$1(hash_table *ht, $2 key, void *value)
*
* Replace an existing value in the hash table with a new one and
* return the old value. If the key does not already exist in
* the hash table, add a new element and return NULL.
* Key type is $2
*/
void *
hash_replace_$1(hash_table *ht, $2 key, void *value)
{
bucket_t *bucket;
hash_item_$1 *hi;
llist *ll;
llistitem *li;
void *result = NULL;
$2 k1;
/* find the bucket */
bucket = &(ht->buckets[$3]);
/* walk the list until we find the existing item */
ll = &(bucket->list);
li = LL_FIRST(ll);
while (li != NULL)
{
hi = (hash_item_$1 *)li->datum;
k1 = hi->key;
if ($4)
{
/* found it: now replace the value with the new one */
result = hi->value;
hi->value = value;
break;
}
li = LL_NEXT(ll, li);
}
/* if the element wasnt found, add it */
if (result == NULL)
{
li = ll_newitem(sizeof(hash_item_$1));
hi = (hash_item_$1 *)li->datum;
hi->key = ifelse($5, , key, $5);
hi->value = value;
ll_add(&(bucket->list), li);
}
/* return the old value (so it can be freed) */
return result;
}
/*
* void *hash_lookup_$1(hash_table *ht, $2 key)
*
* Look up "key" in "ht" and return the associated value. If "key"
* is not found, return NULL. Key type is $2
*/
void *
hash_lookup_$1(hash_table *ht, $2 key)
{
bucket_t *bucket;
llist *ll;
llistitem *li;
hash_item_$1 *h;
void *result;
$2 k1;
result = NULL;
if ((bucket = &(ht->buckets[$3])) != NULL)
{
ll = &(bucket->list);
li = LL_FIRST(ll);
while (li != NULL)
{
h = (hash_item_$1 *)li->datum;
k1 = h->key;
if ($4)
{
result = h->value;
break;
}
li = LL_NEXT(ll, li);
}
}
return result;
}
/*
* void *hash_remove_$1(hash_table *ht, $2 key)
*
* Remove the element associated with "key" from the hash table
* "ht". Return the value or NULL if the key was not found.
*/
void *
hash_remove_$1(hash_table *ht, $2 key)
{
bucket_t *bucket;
llist *ll;
llistitem *li;
llistitem *lilast;
hash_item_$1 *hi;
void *result;
$2 k1;
result = NULL;
if ((bucket = &(ht->buckets[$3])) != NULL)
{
ll = &(bucket->list);
li = LL_FIRST(ll);
lilast = NULL;
while (li != NULL)
{
hi = (hash_item_$1 *)li->datum;
k1 = hi->key;
if ($4)
{
ll_extract(ll, li, lilast);
result = hi->value;
key = hi->key;
$6;
ll_freeitem(li);
break;
}
lilast = li;
li = LL_NEXT(ll, li);
}
}
return result;
}
/*
* hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos)
*
* First function to call when iterating through all items in the hash
* table. Returns the first item in "ht" and initializes "*pos" to track
* the current position.
*/
hash_item_$1 *
hash_first_$1(hash_table *ht, hash_pos *pos)
{
/* initialize pos for first item in first bucket */
pos->num_buckets = ht->num_buckets;
pos->hash_bucket = ht->buckets;
pos->curr = 0;
pos->ll_last = NULL;
/* find the first non-empty bucket */
while(pos->hash_bucket->list.count == 0)
{
pos->hash_bucket++;
if (++pos->curr >= pos->num_buckets)
{
return NULL;
}
}
/* set and return the first item */
pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
return (hash_item_$1 *)pos->ll_item->datum;
}
/*
* hash_item_$1 *hash_next_$1(hash_pos *pos)
*
* Return the next item in the hash table, using "pos" as a description
* of the present position in the hash table. "pos" also identifies the
* specific hash table. Return NULL if there are no more elements.
*/
hash_item_$1 *
hash_next_$1(hash_pos *pos)
{
llistitem *li;
/* move item to last and check for NULL */
if ((pos->ll_last = pos->ll_item) == NULL)
{
/* are we really at the end of the hash table? */
if (pos->curr >= pos->num_buckets)
{
/* yes: return NULL */
return NULL;
}
/* no: regrab first item in current bucket list (might be NULL) */
li = LL_FIRST(&(pos->hash_bucket->list));
}
else
{
/* get the next item in the llist */
li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
}
/* if its NULL we have to find another bucket */
while (li == NULL)
{
/* locate another bucket */
pos->ll_last = NULL;
/* move to the next one */
pos->hash_bucket++;
if (++pos->curr >= pos->num_buckets)
{
/* at the end of the hash table */
pos->ll_item = NULL;
return NULL;
}
/* get the first element (might be NULL) */
li = LL_FIRST(&(pos->hash_bucket->list));
}
/* li is the next element to dish out */
pos->ll_item = li;
return (hash_item_$1 *)li->datum;
}
/*
* void *hash_remove_pos_$1(hash_pos *pos)
*
* Remove the hash table entry pointed to by position marker "pos".
* The data from the entry is returned upon success, otherwise NULL.
*/
void *
hash_remove_pos_$1(hash_pos *pos)
{
llistitem *li;
void *ans;
hash_item_$1 *hi;
$2 key;
/* sanity checks */
if (pos == NULL || pos->ll_last == pos->ll_item)
{
return NULL;
}
/* at this point pos contains the item to remove (ll_item)
and its predecesor (ll_last) */
/* extract the item from the llist */
li = pos->ll_item;
ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
/* retain the data */
hi = (hash_item_$1 *)li->datum;
ans = hi->value;
/* free up the space */
key = hi->key;
$6;
ll_freeitem(li);
/* back up to previous item */
/* its okay for ll_item to be null: hash_next will detect it */
pos->ll_item = pos->ll_last;
return ans;
}
')
dnl # create hash talbe functions for unsigned int and for strings */
HASH_TABLE_TMPL(`uint', `unsigned int', `(key % ht->num_buckets)', `key == k1', ,)
HASH_TABLE_TMPL(`pid', `pid_t', `(key % ht->num_buckets)', `key == k1', ,)
HASH_TABLE_TMPL(`string', `char *', `string_hash(ht, key)', `strcmp(key, k1) == 0', `STRDUP(key)', `FREE(key)')
HASH_TABLE_TMPL(`pidthr', `pidthr_t', `((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)', `(key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)', ,)
#if HAVE_LWPID_T
HASH_TABLE_TMPL(`lwpid', `lwpid_t', `(key % ht->num_buckets)', `key == k1', ,)
#endif