Blame modules/pam_console/hashtable.c

Packit 7e982e
/* Copyright (C) 2004 Christopher Clark <firstname.lastname@cl.cam.ac.uk> */
Packit 7e982e
Packit 7e982e
#include "hashtable.h"
Packit 7e982e
#include "hashtable_private.h"
Packit 7e982e
#include <stdlib.h>
Packit 7e982e
#include <stdio.h>
Packit 7e982e
#include <string.h>
Packit 7e982e
#include <math.h>
Packit 7e982e
Packit 7e982e
/*
Packit 7e982e
Credit for primes table: Aaron Krowne
Packit 7e982e
 http://br.endernet.org/~akrowne/
Packit 7e982e
 http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
Packit 7e982e
*/
Packit 7e982e
static const unsigned int primes[] = {
Packit 7e982e
53, 97, 193, 389,
Packit 7e982e
769, 1543, 3079, 6151,
Packit 7e982e
12289, 24593, 49157, 98317,
Packit 7e982e
196613, 393241, 786433, 1572869,
Packit 7e982e
3145739, 6291469, 12582917, 25165843,
Packit 7e982e
50331653, 100663319, 201326611, 402653189,
Packit 7e982e
805306457, 1610612741
Packit 7e982e
};
Packit 7e982e
const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]);
Packit 7e982e
const float max_load_factor = 0.65;
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
struct hashtable *
Packit 7e982e
create_hashtable(unsigned int minsize,
Packit 7e982e
                 unsigned int (*hashf) (void*),
Packit 7e982e
                 int (*eqf) (void*,void*))
Packit 7e982e
{
Packit 7e982e
    struct hashtable *h;
Packit 7e982e
    unsigned int pindex, size = primes[0];
Packit 7e982e
    /* Check requested hashtable isn't too large */
Packit 7e982e
    if (minsize > (1u << 30)) return NULL;
Packit 7e982e
    /* Enforce size as prime */
Packit 7e982e
    for (pindex=0; pindex < prime_table_length; pindex++) {
Packit 7e982e
        if (primes[pindex] > minsize) { size = primes[pindex]; break; }
Packit 7e982e
    }
Packit 7e982e
    h = (struct hashtable *)malloc(sizeof(struct hashtable));
Packit 7e982e
    if (NULL == h) return NULL; /*oom*/
Packit 7e982e
    h->table = (struct entry **)malloc(sizeof(struct entry*) * size);
Packit 7e982e
    if (NULL == h->table) { free(h); return NULL; } /*oom*/
Packit 7e982e
    memset(h->table, 0, size * sizeof(struct entry *));
Packit 7e982e
    h->tablelength  = size;
Packit 7e982e
    h->primeindex   = pindex;
Packit 7e982e
    h->entrycount   = 0;
Packit 7e982e
    h->hashfn       = hashf;
Packit 7e982e
    h->eqfn         = eqf;
Packit 7e982e
    h->loadlimit    = (unsigned int)(size * max_load_factor) + 1;
Packit 7e982e
    return h;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
Packit 7e982e
#define hash(h, k) h->hashfn(k)
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
static int
Packit 7e982e
hashtable_expand(struct hashtable *h)
Packit 7e982e
{
Packit 7e982e
    /* Double the size of the table to accomodate more entries */
Packit 7e982e
    struct entry **newtable;
Packit 7e982e
    struct entry *e;
Packit 7e982e
    struct entry **pE;
Packit 7e982e
    unsigned int newsize, i, idx;
Packit 7e982e
    /* Check we're not hitting max capacity */
Packit 7e982e
    if (h->primeindex == (prime_table_length - 1)) return 0;
Packit 7e982e
    newsize = primes[++(h->primeindex)];
Packit 7e982e
Packit 7e982e
    newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize);
Packit 7e982e
    if (NULL != newtable)
Packit 7e982e
    {
Packit 7e982e
        memset(newtable, 0, newsize * sizeof(struct entry *));
Packit 7e982e
        /* This algorithm is not 'stable'. ie. it reverses the list
Packit 7e982e
         * when it transfers entries between the tables */
Packit 7e982e
        for (i = 0; i < h->tablelength; i++) {
Packit 7e982e
            while (NULL != (e = h->table[i])) {
Packit 7e982e
                h->table[i] = e->next;
Packit 7e982e
                idx = indexFor(newsize,e->h);
Packit 7e982e
                e->next = newtable[idx];
Packit 7e982e
                newtable[idx] = e;
Packit 7e982e
            }
Packit 7e982e
        }
Packit 7e982e
        free(h->table);
Packit 7e982e
        h->table = newtable;
Packit 7e982e
    }
Packit 7e982e
    /* Plan B: realloc instead */
Packit 7e982e
    else 
Packit 7e982e
    {
Packit 7e982e
        newtable = (struct entry **)
Packit 7e982e
                   realloc(h->table, newsize * sizeof(struct entry *));
Packit 7e982e
        if (NULL == newtable) { (h->primeindex)--; return 0; }
Packit 7e982e
        h->table = newtable;
Packit 7e982e
        memset(newtable[h->tablelength], 0, newsize - h->tablelength);
Packit 7e982e
        for (i = 0; i < h->tablelength; i++) {
Packit 7e982e
            for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) {
Packit 7e982e
                idx = indexFor(newsize,e->h);
Packit 7e982e
                if (idx == i)
Packit 7e982e
                {
Packit 7e982e
                    pE = &(e->next);
Packit 7e982e
                }
Packit 7e982e
                else
Packit 7e982e
                {
Packit 7e982e
                    *pE = e->next;
Packit 7e982e
                    e->next = newtable[idx];
Packit 7e982e
                    newtable[idx] = e;
Packit 7e982e
                }
Packit 7e982e
            }
Packit 7e982e
        }
Packit 7e982e
    }
Packit 7e982e
    h->tablelength = newsize;
Packit 7e982e
    h->loadlimit   = (unsigned int)(newsize * max_load_factor) + 1;
Packit 7e982e
    return -1;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
unsigned int
Packit 7e982e
hashtable_count(struct hashtable *h)
Packit 7e982e
{
Packit 7e982e
    return h->entrycount;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
int
Packit 7e982e
hashtable_insert(struct hashtable *h, void *k, void *v)
Packit 7e982e
{
Packit 7e982e
    /* This method allows duplicate keys - but they shouldn't be used */
Packit 7e982e
    unsigned int idx;
Packit 7e982e
    struct entry *e;
Packit 7e982e
    if (++(h->entrycount) > h->loadlimit)
Packit 7e982e
    {
Packit 7e982e
        /* Ignore the return value. If expand fails, we should
Packit 7e982e
         * still try cramming just this value into the existing table
Packit 7e982e
         * -- we may not have memory for a larger table, but one more
Packit 7e982e
         * element may be ok. Next time we insert, we'll try expanding again.*/
Packit 7e982e
        hashtable_expand(h);
Packit 7e982e
    }
Packit 7e982e
    e = (struct entry *)malloc(sizeof(struct entry));
Packit 7e982e
    if (NULL == e) { --(h->entrycount); return 0; } /*oom*/
Packit 7e982e
    e->h = hash(h,k);
Packit 7e982e
    idx = indexFor(h->tablelength,e->h);
Packit 7e982e
    e->k = k;
Packit 7e982e
    e->v = v;
Packit 7e982e
    e->next = h->table[idx];
Packit 7e982e
    h->table[idx] = e;
Packit 7e982e
    return -1;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
void * /* returns value associated with key */
Packit 7e982e
hashtable_search(struct hashtable *h, void *k)
Packit 7e982e
{
Packit 7e982e
    struct entry *e;
Packit 7e982e
    unsigned int hashvalue, idx;
Packit 7e982e
    hashvalue = hash(h,k);
Packit 7e982e
    idx = indexFor(h->tablelength,hashvalue);
Packit 7e982e
    e = h->table[idx];
Packit 7e982e
    while (NULL != e)
Packit 7e982e
    {
Packit 7e982e
        /* Check hash value to short circuit heavier comparison */
Packit 7e982e
        if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v;
Packit 7e982e
        e = e->next;
Packit 7e982e
    }
Packit 7e982e
    return NULL;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
void * /* returns value associated with key */
Packit 7e982e
hashtable_remove(struct hashtable *h, void *k, int free_key)
Packit 7e982e
{
Packit 7e982e
    /* TODO: consider compacting the table when the load factor drops enough,
Packit 7e982e
     *       or provide a 'compact' method. */
Packit 7e982e
Packit 7e982e
    struct entry *e;
Packit 7e982e
    struct entry **pE;
Packit 7e982e
    void *v;
Packit 7e982e
    unsigned int hashvalue, idx;
Packit 7e982e
Packit 7e982e
    hashvalue = hash(h,k);
Packit 7e982e
    idx = indexFor(h->tablelength,hash(h,k));
Packit 7e982e
    pE = &(h->table[idx]);
Packit 7e982e
    e = *pE;
Packit 7e982e
    while (NULL != e)
Packit 7e982e
    {
Packit 7e982e
        /* Check hash value to short circuit heavier comparison */
Packit 7e982e
        if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
Packit 7e982e
        {
Packit 7e982e
            *pE = e->next;
Packit 7e982e
            h->entrycount--;
Packit 7e982e
            v = e->v;
Packit 7e982e
            if (free_key) freekey(e->k);
Packit 7e982e
            free(e);
Packit 7e982e
            return v;
Packit 7e982e
        }
Packit 7e982e
        pE = &(e->next);
Packit 7e982e
        e = e->next;
Packit 7e982e
    }
Packit 7e982e
    return NULL;
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*****************************************************************************/
Packit 7e982e
/* destroy */
Packit 7e982e
void
Packit 7e982e
hashtable_destroy(struct hashtable *h, int free_kv)
Packit 7e982e
{
Packit 7e982e
    unsigned int i;
Packit 7e982e
    struct entry *e, *f;
Packit 7e982e
    struct entry **table = h->table;
Packit 7e982e
    if (free_kv)
Packit 7e982e
    {
Packit 7e982e
        for (i = 0; i < h->tablelength; i++)
Packit 7e982e
        {
Packit 7e982e
            e = table[i];
Packit 7e982e
            while (NULL != e)
Packit 7e982e
            { f = e; e = e->next; freekey(f->k); free(f->v); free(f); }
Packit 7e982e
        }
Packit 7e982e
    }
Packit 7e982e
    free(h->table);
Packit 7e982e
    free(h);
Packit 7e982e
}
Packit 7e982e
Packit 7e982e
/*
Packit 7e982e
 * Copyright (c) 2002, Christopher Clark
Packit 7e982e
 * All rights reserved.
Packit 7e982e
 * 
Packit 7e982e
 * Redistribution and use in source and binary forms, with or without
Packit 7e982e
 * modification, are permitted provided that the following conditions
Packit 7e982e
 * are met:
Packit 7e982e
 * 
Packit 7e982e
 * * Redistributions of source code must retain the above copyright
Packit 7e982e
 * notice, this list of conditions and the following disclaimer.
Packit 7e982e
 * 
Packit 7e982e
 * * Redistributions in binary form must reproduce the above copyright
Packit 7e982e
 * notice, this list of conditions and the following disclaimer in the
Packit 7e982e
 * documentation and/or other materials provided with the distribution.
Packit 7e982e
 * 
Packit 7e982e
 * * Neither the name of the original author; nor the names of any contributors
Packit 7e982e
 * may be used to endorse or promote products derived from this software
Packit 7e982e
 * without specific prior written permission.
Packit 7e982e
 * 
Packit 7e982e
 * 
Packit 7e982e
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit 7e982e
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit 7e982e
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Packit 7e982e
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
Packit 7e982e
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit 7e982e
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit 7e982e
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit 7e982e
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit 7e982e
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit 7e982e
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit 7e982e
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 7e982e
*/