2024-04-22 00:09:51 +08:00

389 lines
10 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file dict.c
* \unit dict
* \brief This is a general-purpose C language dict module, with common data structure, realized by hash table.
* \author Lamdonn
* \version v1.0.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#include "dict.h"
#include <stdio.h>
#include <string.h>
#include <limits.h>
/**< Minimum capacity of hash table */
#define MIN_CAPACITY 4
/**< Address method of dict hash table */
#define TBALE ((groove_t *)(dict->base))
/* Hash table groove definition */
typedef struct
{
/**< Hash value */
unsigned int hash;
/**< Key address */
char *key;
/**< Value address */
void *value;
} GROOVE, *groove_t;
/* dict type define */
typedef struct DICT
{
/**< Base address for groove data, the data of the hash table exists in this address space */
void *base;
/**< Size of value, for example, sizeof(int), sizeof(char), ... */
unsigned int vsize;
/**< Size of dict, that is the count of dict members */
unsigned int size;
/**< Capacity of dict, actual hash table capacity */
unsigned int capacity;
/**< Iterator index, the static index required to record the iteration position during iteration traversal */
unsigned int it;
/**< Size of key, when it is a positive number, it is a fixed-length key, and 0 is a variable-length key. */
unsigned int ksize;
/**< Function to get indefinite key length */
int (*klength)(void *key);
#ifdef DICT_USE_ERROR
/**< Error space used due to misoperations in data reading and writing */
void *error;
#endif
} DICT;
static unsigned int hash_bkdr(void *data, unsigned int size)
{
int i = 0;
unsigned char *str = (unsigned char *)data;
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
for (i = 0; i < size; i++)
{
hash = hash * seed + (*str++);
}
return (hash & INT_MAX);
}
static int default_klength(void *key)
{
return strlen(key) + 1;
}
/* Default memory space comparison function */
static int kcompare(const void *s1, unsigned int n1, const void *s2, unsigned int n2)
{
const unsigned char *p1 = s1, *p2 = s2;
unsigned int i = 0;
for (i = 0; i < n1 && i < n2; i++)
{
if (p1[i] != p2[i])
{
return (p1[i] < p2[i]) ? -1 : 1;
}
}
if (n1 == n2) return 0;
return (n1 < n2) ? -1 : 1;
}
dict_t dict_create(unsigned int vsize)
{
dict_t dict;
/* Allocate space */
dict = (dict_t)malloc(sizeof(DICT));
if (!dict) return NULL;
#ifdef DICT_USE_ERROR
dict->error = malloc(vsize);
if (!dict->error) { free(dict); return NULL; }
#endif
/* Initialize structure members */
dict->base = NULL;
dict->ksize = 0;
dict->klength = default_klength;
dict->vsize = vsize;
dict->size = 0;
dict->capacity = 0;
dict->it = 0;
return dict;
}
void dict_delete(dict_t dict)
{
if (!dict) return;
dict_clear(dict);
#ifdef DICT_USE_ERROR
if (dict->error) free(dict->error);
#endif
free(dict);
}
/* But when the hash table stores a certain coefficient, the hash table needs to be readjusted to obtain more space. */
static int dict_resize(dict_t dict, unsigned int capacity)
{
unsigned int i = 0, index = 0, hash = 0;
int len = 0;
groove_t *base;
/* Newly allocate and initialize a space */
base = malloc(capacity * sizeof(groove_t));
if (!base) return 0;
memset(base, 0, capacity * sizeof(groove_t));
/* Copy old data to new hash table */
for (i = 0; i < dict->capacity; i++)
{
/* Data is stored in the groove */
if (TBALE[i])
{
/* The hash value of the current groove is marked as -1,
* indicating that this groove is still occupied,
* but no data is actually stored.
* The data originally stored will not be recorded in the new hash table, but will be released. */
if (TBALE[i]->hash == (unsigned int)-1)
{
if (TBALE[i]->key) free(TBALE[i]->key);
if (TBALE[i]->value) free(TBALE[i]->value);
free(TBALE[i]);
}
/* Add the new valid hash table data to the new hash table one by one. */
else
{
len = (dict->ksize > 0) ? dict->ksize : dict->klength(TBALE[i]->key);
hash = hash_bkdr((void *)TBALE[i]->key, len) % capacity;
index = hash;
while (base[index]) index = (index + 1) % capacity;
base[index] = TBALE[i];
base[index]->hash = hash;
}
}
}
/* Update hash table */
if (dict->base) free(dict->base);
dict->base = base;
dict->capacity = capacity;
return 1;
}
int dict_set_klength(dict_t dict, unsigned int ksize, int (*klength)(void *key))
{
if (!dict) return 0;
if (ksize == 0)
{
if (!klength) return 0; /* Functions that do not obtain indefinite length keys are not allowed */
dict->ksize = 0;
dict->klength = klength;
}
else
{
dict->ksize = ksize;
dict->klength = NULL;
}
return 1;
}
void* dict_insert(dict_t dict, void *key, void *value)
{
groove_t groove = NULL;
unsigned int hash = 0, index;
int len = 0;
if (!dict) return NULL;
if (!key) return NULL;
/* The current capacity affects the search rate and needs to be expanded */
if (dict->size >= ((dict->capacity >> 2) + (dict->capacity >> 1))) /* size exceeds 3/4 of capacity */
{
/* Allocate new hash table space */
if (!dict_resize(dict, dict->capacity < MIN_CAPACITY ? MIN_CAPACITY : dict->capacity << 1)) return NULL;
}
/* find a free groove */
len = (dict->ksize > 0) ? dict->ksize : dict->klength(key);
hash = hash_bkdr(key, len) % dict->capacity;
index = hash;
while (TBALE[index] && TBALE[index]->hash != -1)
{
index = (index + 1) % dict->capacity;
if (index == hash) return NULL;
}
/* space allocation */
groove = TBALE[index];
if (!groove) groove = (groove_t)malloc(sizeof(GROOVE));
if (!groove) return NULL;
groove->key = malloc(len);
if (!groove->key) { if (!TBALE[index]) free(groove); return NULL; }
groove->value = malloc(dict->vsize);
if (!groove->value) { free(groove->key), groove->key = NULL; if (!TBALE[index]) free(groove); return NULL; }
/* assign */
groove->hash = hash;
memcpy(groove->key, key, len);
if (value) memcpy(groove->value, value, dict->vsize);
/* insert */
TBALE[index] = groove;
dict->size++;
return groove->value;
}
static unsigned int find_index(dict_t dict, void *key)
{
unsigned int hash = 0, index;
int len = 0;
if (dict->capacity == 0) return -1;
len = (dict->ksize > 0) ? dict->ksize : dict->klength(key);
hash = hash_bkdr(key, len) % dict->capacity;
index = hash;
/* Traverse the entire hash table and find the groove where both hash value and key match */
while (TBALE[index])
{
if (TBALE[index]->hash == hash && !kcompare(key, len, TBALE[index]->key, (dict->ksize > 0) ? dict->ksize : dict->klength(TBALE[index]->key))) break;
index = (index + 1) % dict->capacity; /* linear search */
}
if (!TBALE[index]) return -1;
return index;
}
int dict_erase(dict_t dict, void *key)
{
groove_t groove;
unsigned int index, next;
if (!dict) return 0;
if (!key) return 0;
index = find_index(dict, key);
if (index == -1) return 0;
groove = TBALE[index];
/* Release the data stored in the groove and mark it as occupied */
if (groove->key) { free(groove->key); groove->key = NULL; }
if (groove->value) { free(groove->value); groove->value = NULL; }
groove->hash = -1;
dict->size--;
/* Size less than 1/4 of capacity */
if (dict->capacity > MIN_CAPACITY && dict->size <= (dict->capacity >> 2))
{
dict_resize(dict, dict->capacity >> 1);
}
return 1;
}
void dict_clear(dict_t dict)
{
int i = 0;
if (!dict) return;
for (i = 0; i < dict->capacity; i++)
{
if (TBALE[i])
{
if (TBALE[i]->key) free(TBALE[i]->key);
if (TBALE[i]->value) free(TBALE[i]->value);
free(TBALE[i]);
TBALE[i] = NULL;
}
}
if (dict->base) free(dict->base);
dict->base = NULL;
dict->size = 0;
dict->capacity = 0;
}
int dict_size(dict_t dict)
{
if (!dict) return 0;
return dict->size;
}
int dict_vsize(dict_t dict)
{
if (!dict) return 0;
return dict->vsize;
}
int dict_find(dict_t dict, void *key)
{
if (!dict) return 0;
if (!key) return 0;
return (find_index(dict, key) == -1) ? 0 : 1;
}
void* dict_value(dict_t dict, void *key)
{
unsigned int index;
groove_t groove;
if (!dict) return NULL;
if (!key) return dict_error(dict);
index = find_index(dict, key);
if (index == -1) return dict_error(dict);
return TBALE[index]->value;
}
#ifdef DICT_USE_ERROR
void* dict_error(dict_t dict)
{
if (!dict) return NULL;
return dict->error;
}
#endif
void dict_it_init(dict_t dict)
{
if (!dict) return;
dict->it = 0;
}
void* dict_it_get(dict_t dict, char **key)
{
groove_t groove = NULL;
if (!dict) return NULL;
if (dict->it >= dict->capacity) return dict_error(dict);
while (dict->it < dict->capacity)
{
if (TBALE[dict->it] && TBALE[dict->it]->hash != -1)
{
groove = TBALE[dict->it];
dict->it++;
break;
}
dict->it++;
}
if (!groove) return dict_error(dict);
if (key) *key = groove->key;
return groove->value;
}