Blame common/dict.c

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
}