|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
dict.c - dictionary functions
|
|
Packit |
6bd9ab |
This file is part of the nss-pam-ldapd library.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
Copyright (C) 2007, 2008, 2009, 2010, 2012, 2013 Arthur de Jong
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
02110-1301 USA
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "config.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include <stdlib.h>
|
|
Packit |
6bd9ab |
#include <string.h>
|
|
Packit |
6bd9ab |
#include <strings.h>
|
|
Packit |
6bd9ab |
#include <ctype.h>
|
|
Packit |
6bd9ab |
#ifdef HAVE_STDINT_H
|
|
Packit |
6bd9ab |
#include <stdint.h>
|
|
Packit |
6bd9ab |
#endif /* HAVE_STDINT_H */
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "dict.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
This module uses a hashtable to store its key to value mappings. The
|
|
Packit |
6bd9ab |
structure is basically as follows:
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
[struct dictionary]
|
|
Packit |
6bd9ab |
\- holds an array of pointers to a linked list of [struct dict_entry]
|
|
Packit |
6bd9ab |
\- each entry has a key/value mapping
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
The hashmap can be resized when the total number of elements in the hashmap
|
|
Packit |
6bd9ab |
exceeds a certain load factor.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
All the keys are copied in a separate linked list of buffers where each new
|
|
Packit |
6bd9ab |
buffer that is allocated is larger than the previous one. The first buffer
|
|
Packit |
6bd9ab |
in the linked list is always the current one.
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* an entry stores one key/value pair */
|
|
Packit |
6bd9ab |
struct dict_entry {
|
|
Packit |
6bd9ab |
uint32_t hash; /* used for quick matching and rehashing */
|
|
Packit |
6bd9ab |
const char *key; /* a reference to a copy of the key */
|
|
Packit |
6bd9ab |
void *value; /* the stored value */
|
|
Packit |
6bd9ab |
struct dict_entry *next;
|
|
Packit |
6bd9ab |
};
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* the initial size of the hashtable */
|
|
Packit |
6bd9ab |
#define DICT_INITSIZE 7
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* load factor at which point to grow hashtable */
|
|
Packit |
6bd9ab |
#define DICT_LOADPERCENTAGE 400
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* the dictionary is a hashtable */
|
|
Packit |
6bd9ab |
struct dictionary {
|
|
Packit |
6bd9ab |
int size; /* size of the hashtable */
|
|
Packit |
6bd9ab |
int num; /* total number of keys stored */
|
|
Packit |
6bd9ab |
struct dict_entry **table; /* the hashtable */
|
|
Packit |
6bd9ab |
};
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* Simple hash function that computes the hash value of a string. */
|
|
Packit |
6bd9ab |
static uint32_t stringhash(const char *str)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
uint32_t hash = 5381;
|
|
Packit |
6bd9ab |
uint32_t c;
|
|
Packit |
6bd9ab |
while ((c = *str++) != '\0')
|
|
Packit |
6bd9ab |
hash = 33 * hash + c;
|
|
Packit |
6bd9ab |
return hash;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* Grow the hashtable. */
|
|
Packit |
6bd9ab |
static void growhashtable(DICT *dict)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
int newsize;
|
|
Packit |
6bd9ab |
struct dict_entry **newtable;
|
|
Packit |
6bd9ab |
struct dict_entry *entry, *tmp;
|
|
Packit |
6bd9ab |
newsize = dict->size * 3 + 1;
|
|
Packit |
6bd9ab |
/* allocate room for new hashtable */
|
|
Packit |
6bd9ab |
newtable = (struct dict_entry **)malloc(newsize * sizeof(struct dict_entry *));
|
|
Packit |
6bd9ab |
if (newtable == NULL)
|
|
Packit |
6bd9ab |
return; /* allocating memory failed continue to fill the existing table */
|
|
Packit |
6bd9ab |
/* clear new table */
|
|
Packit |
6bd9ab |
for (i = 0; i < newsize; i++)
|
|
Packit |
6bd9ab |
newtable[i] = NULL;
|
|
Packit |
6bd9ab |
/* copy old hashtable into new table */
|
|
Packit |
6bd9ab |
for (i = 0; i < dict->size; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* go over elements in linked list */
|
|
Packit |
6bd9ab |
entry = dict->table[i];
|
|
Packit |
6bd9ab |
while (entry != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
tmp = entry;
|
|
Packit |
6bd9ab |
entry = entry->next;
|
|
Packit |
6bd9ab |
/* put in new position */
|
|
Packit |
6bd9ab |
tmp->next = newtable[tmp->hash % newsize];
|
|
Packit |
6bd9ab |
newtable[tmp->hash % newsize] = tmp;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* free the old hashtable */
|
|
Packit |
6bd9ab |
free(dict->table);
|
|
Packit |
6bd9ab |
/* put new hashtable in place */
|
|
Packit |
6bd9ab |
dict->size = newsize;
|
|
Packit |
6bd9ab |
dict->table = newtable;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
DICT *dict_new(void)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct dictionary *dict;
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
/* allocate room for dictionary information */
|
|
Packit |
6bd9ab |
dict = (struct dictionary *)malloc(sizeof(struct dictionary));
|
|
Packit |
6bd9ab |
if (dict == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
dict->size = DICT_INITSIZE;
|
|
Packit |
6bd9ab |
dict->num = 0;
|
|
Packit |
6bd9ab |
/* allocate initial hashtable */
|
|
Packit |
6bd9ab |
dict->table = (struct dict_entry **)malloc(DICT_INITSIZE * sizeof(struct dict_entry *));
|
|
Packit |
6bd9ab |
if (dict->table == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
free(dict);
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* clear the hashtable */
|
|
Packit |
6bd9ab |
for (i = 0; i < DICT_INITSIZE; i++)
|
|
Packit |
6bd9ab |
dict->table[i] = NULL;
|
|
Packit |
6bd9ab |
/* we're done */
|
|
Packit |
6bd9ab |
return dict;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
void dict_free(DICT *dict)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
struct dict_entry *entry, *etmp;
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
/* free hashtable entries */
|
|
Packit |
6bd9ab |
for (i = 0; i < dict->size; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
entry = dict->table[i];
|
|
Packit |
6bd9ab |
while (entry != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
etmp = entry;
|
|
Packit |
6bd9ab |
entry = entry->next;
|
|
Packit |
6bd9ab |
free(etmp);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* free the hashtable */
|
|
Packit |
6bd9ab |
free(dict->table);
|
|
Packit |
6bd9ab |
/* free dictionary struct itself */
|
|
Packit |
6bd9ab |
free(dict);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
void *dict_get(DICT *dict, const char *key)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
uint32_t hash;
|
|
Packit |
6bd9ab |
struct dict_entry *entry;
|
|
Packit |
6bd9ab |
/* calculate the hash */
|
|
Packit |
6bd9ab |
hash = stringhash(key);
|
|
Packit |
6bd9ab |
/* loop over the linked list in the hashtable */
|
|
Packit |
6bd9ab |
for (entry = dict->table[hash % dict->size]; entry != NULL; entry = entry->next)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((entry->hash == hash) && (strcmp(entry->key, key) == 0))
|
|
Packit |
6bd9ab |
return entry->value;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* no matches found */
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
const char *dict_getany(DICT *dict)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
/* loop over the linked list in the hashtable */
|
|
Packit |
6bd9ab |
for (i = 0; i < dict->size; i++)
|
|
Packit |
6bd9ab |
if (dict->table[i])
|
|
Packit |
6bd9ab |
return dict->table[i]->key;
|
|
Packit |
6bd9ab |
/* no matches found */
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int dict_put(DICT *dict, const char *key, void *value)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
uint32_t hash;
|
|
Packit |
6bd9ab |
int l;
|
|
Packit |
6bd9ab |
char *buf;
|
|
Packit |
6bd9ab |
int idx;
|
|
Packit |
6bd9ab |
struct dict_entry *entry, *prev;
|
|
Packit |
6bd9ab |
/* check if we should grow the hashtable */
|
|
Packit |
6bd9ab |
if (dict->num >= ((dict->size * DICT_LOADPERCENTAGE) / 100))
|
|
Packit |
6bd9ab |
growhashtable(dict);
|
|
Packit |
6bd9ab |
/* calculate the hash and position in the hashtable */
|
|
Packit |
6bd9ab |
hash = stringhash(key);
|
|
Packit |
6bd9ab |
idx = hash % dict->size;
|
|
Packit |
6bd9ab |
/* check if the entry is already present */
|
|
Packit |
6bd9ab |
for (entry = dict->table[idx], prev = NULL; entry != NULL; prev = entry, entry = entry->next)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if ((entry->hash == hash) && (strcmp(entry->key, key) == 0))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* check if we should unset the entry */
|
|
Packit |
6bd9ab |
if (value == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* remove from linked list */
|
|
Packit |
6bd9ab |
if (prev == NULL)
|
|
Packit |
6bd9ab |
dict->table[idx] = entry->next;
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
prev->next = entry->next;
|
|
Packit |
6bd9ab |
/* free entry memory and register removal */
|
|
Packit |
6bd9ab |
free(entry);
|
|
Packit |
6bd9ab |
dict->num--;
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* just set the new value */
|
|
Packit |
6bd9ab |
entry->value = value;
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* if entry should be unset we're done */
|
|
Packit |
6bd9ab |
if (value == NULL)
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
/* entry is not present, make new entry */
|
|
Packit |
6bd9ab |
l = strlen(key) + 1;
|
|
Packit |
6bd9ab |
buf = (char *)malloc(sizeof(struct dict_entry) + l);
|
|
Packit |
6bd9ab |
if (buf == NULL)
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
entry = (struct dict_entry *)(void *)buf;
|
|
Packit |
6bd9ab |
buf += sizeof(struct dict_entry);
|
|
Packit |
6bd9ab |
strcpy(buf, key);
|
|
Packit |
6bd9ab |
entry->hash = hash;
|
|
Packit |
6bd9ab |
entry->key = buf;
|
|
Packit |
6bd9ab |
entry->value = value;
|
|
Packit |
6bd9ab |
/* insert into hashtable/linked list */
|
|
Packit |
6bd9ab |
entry->next = dict->table[idx];
|
|
Packit |
6bd9ab |
dict->table[idx] = entry;
|
|
Packit |
6bd9ab |
/* increment number of stored items */
|
|
Packit |
6bd9ab |
dict->num++;
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
const char **dict_keys(DICT *dict)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
struct dict_entry *entry;
|
|
Packit |
6bd9ab |
char *buf;
|
|
Packit |
6bd9ab |
const char **values;
|
|
Packit |
6bd9ab |
size_t sz;
|
|
Packit |
6bd9ab |
int num;
|
|
Packit |
6bd9ab |
/* figure out how much memory to allocate */
|
|
Packit |
6bd9ab |
num = 0;
|
|
Packit |
6bd9ab |
sz = 0;
|
|
Packit |
6bd9ab |
for (i = 0; i < dict->size; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
entry = dict->table[i];
|
|
Packit |
6bd9ab |
while (entry != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
num++;
|
|
Packit |
6bd9ab |
sz += strlen(entry->key) + 1;
|
|
Packit |
6bd9ab |
entry = entry->next;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* allocate the needed memory */
|
|
Packit |
6bd9ab |
buf = (char *)malloc((num + 1) * sizeof(char *) + sz);
|
|
Packit |
6bd9ab |
if (buf == NULL)
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
values = (const char **)(void *)buf;
|
|
Packit |
6bd9ab |
buf += (num + 1) * sizeof(char *);
|
|
Packit |
6bd9ab |
/* fill the array with the keys */
|
|
Packit |
6bd9ab |
num = 0;
|
|
Packit |
6bd9ab |
for (i = 0; i < dict->size; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
entry = dict->table[i];
|
|
Packit |
6bd9ab |
while (entry != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
strcpy(buf, entry->key);
|
|
Packit |
6bd9ab |
values[num++] = buf;
|
|
Packit |
6bd9ab |
buf += strlen(buf) + 1;
|
|
Packit |
6bd9ab |
entry = entry->next;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
values[num] = NULL;
|
|
Packit |
6bd9ab |
/* done */
|
|
Packit |
6bd9ab |
return values;
|
|
Packit |
6bd9ab |
}
|