259 lines
6.4 KiB
C
259 lines
6.4 KiB
C
#include "CgeConfQuery.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define INIT_EXP 4
|
|
#define INIT_CAPACITY ((size_t)1 << INIT_EXP)
|
|
#define MAX_SECTION 4096
|
|
|
|
typedef struct {
|
|
char *section;
|
|
char *key;
|
|
char *value;
|
|
} Entry;
|
|
|
|
struct CgeConfQuery {
|
|
Entry *entryData;
|
|
size_t size;
|
|
size_t capacity;
|
|
Entry **slotData;
|
|
char currentSection[MAX_SECTION];
|
|
int exp;
|
|
};
|
|
|
|
static char *stringDup(const char *str) {
|
|
size_t length;
|
|
char *dest;
|
|
|
|
length = strlen(str);
|
|
if ((dest = malloc(length + 1)))
|
|
memcpy(dest, str, length + 1);
|
|
return dest;
|
|
}
|
|
|
|
static size_t hashKey(const char *section, const char *key) {
|
|
size_t h = 5381;
|
|
const char *p;
|
|
int c;
|
|
|
|
for (p = section; (c = *p); ++p)
|
|
h = ((h << 5) + h) + (unsigned char)c;
|
|
h = ((h << 5) + h);
|
|
for (p = key; (c = *p); ++p)
|
|
h = ((h << 5) + h) + (unsigned char)c;
|
|
|
|
return h;
|
|
}
|
|
|
|
static size_t lookup(size_t hash, int exp, size_t index) {
|
|
size_t mask = (1 << exp) - 1;
|
|
size_t step = (hash >> (sizeof(hash) * 8 - exp)) | 1;
|
|
return (index + step) & mask;
|
|
}
|
|
|
|
static int reserveSpace(CgeConfQuery *query, size_t need) {
|
|
CgeConfQuery tmp;
|
|
size_t i, hash, index;
|
|
|
|
if (need <= (query->capacity - (query->capacity >> 2)))
|
|
return 1;
|
|
|
|
tmp.exp = query->exp ? query->exp + 1 : INIT_EXP;
|
|
tmp.capacity = query->capacity ? query->capacity * 2 : INIT_CAPACITY;
|
|
tmp.size = 0;
|
|
tmp.entryData = malloc(tmp.capacity * sizeof(*tmp.entryData));
|
|
tmp.slotData = malloc(tmp.capacity * sizeof(*tmp.slotData));
|
|
if (!tmp.entryData || !tmp.slotData) {
|
|
free(tmp.entryData);
|
|
free(tmp.slotData);
|
|
return 0;
|
|
}
|
|
memset(tmp.slotData, 0, sizeof(*tmp.slotData) * tmp.capacity);
|
|
|
|
for (i = 0; i < query->size; ++i) {
|
|
Entry *entry = &query->entryData[i];
|
|
|
|
hash = hashKey(entry->section, entry->key);
|
|
index = lookup(hash, tmp.exp, 0);
|
|
while (tmp.slotData[index] != NULL)
|
|
index = lookup(hash, tmp.exp, index);
|
|
|
|
tmp.entryData[tmp.size] = *entry;
|
|
tmp.slotData[index] = tmp.entryData + tmp.size++;
|
|
}
|
|
|
|
free(query->entryData);
|
|
free(query->slotData);
|
|
query->entryData = tmp.entryData;
|
|
query->slotData = tmp.slotData;
|
|
query->capacity = tmp.capacity;
|
|
query->exp = tmp.exp;
|
|
return 1;
|
|
}
|
|
|
|
static int insert(CgeConfQuery *query, const char *section, const char *key,
|
|
const char *value) {
|
|
size_t hash, index;
|
|
char *newValue;
|
|
Entry *entry;
|
|
|
|
if (!reserveSpace(query, query->size + 1))
|
|
return 0;
|
|
|
|
hash = hashKey(section, key);
|
|
index = lookup(hash, query->exp, 0);
|
|
|
|
while ((entry = query->slotData[index])) {
|
|
if (strcmp(entry->section, section) == 0 &&
|
|
strcmp(entry->key, key) == 0) {
|
|
newValue = stringDup(value);
|
|
if (!newValue)
|
|
return 0;
|
|
free(entry->value);
|
|
entry->value = newValue;
|
|
return 1;
|
|
}
|
|
index = lookup(hash, query->exp, index);
|
|
}
|
|
|
|
entry = &query->entryData[query->size];
|
|
entry->section = stringDup(section);
|
|
entry->key = stringDup(key);
|
|
entry->value = stringDup(value);
|
|
if (!entry->section || !entry->key || !entry->value) {
|
|
free(entry->section);
|
|
free(entry->key);
|
|
free(entry->value);
|
|
return 0;
|
|
}
|
|
|
|
query->size++;
|
|
query->slotData[index] = entry;
|
|
return 1;
|
|
}
|
|
|
|
static void populateCb(int type, const char *key, const char *value,
|
|
void *user) {
|
|
CgeConfQuery *query = (CgeConfQuery *)user;
|
|
size_t len;
|
|
|
|
if (type == CGE_CONF_SECTION) {
|
|
len = strlen(value);
|
|
if (len >= sizeof(query->currentSection))
|
|
len = sizeof(query->currentSection) - 1;
|
|
memcpy(query->currentSection, value, len);
|
|
query->currentSection[len] = 0;
|
|
} else if (type == CGE_CONF_KEYVALUE) {
|
|
insert(query, query->currentSection, key, value);
|
|
}
|
|
}
|
|
|
|
static CgeConfQuery *queryNew(void) {
|
|
CgeConfQuery *query;
|
|
|
|
query = (CgeConfQuery *)malloc(sizeof(CgeConfQuery));
|
|
if (!query)
|
|
return NULL;
|
|
|
|
memset(query, 0, sizeof(*query));
|
|
return query;
|
|
}
|
|
|
|
CgeConfQuery *CgeConfQueryFromCb(CgeConfIO *io) {
|
|
CgeConfQuery *query;
|
|
|
|
query = queryNew();
|
|
if (!query)
|
|
return NULL;
|
|
|
|
if (!CgeConfFromCb(io, populateCb, query)) {
|
|
CgeConfQueryFree(query);
|
|
return NULL;
|
|
}
|
|
return query;
|
|
}
|
|
|
|
CgeConfQuery *CgeConfQueryFromFile(const char *file) {
|
|
CgeConfQuery *query;
|
|
|
|
query = queryNew();
|
|
if (!query)
|
|
return NULL;
|
|
|
|
if (!CgeConfFromFile(file, populateCb, query)) {
|
|
CgeConfQueryFree(query);
|
|
return NULL;
|
|
}
|
|
return query;
|
|
}
|
|
|
|
CgeConfQuery *CgeConfQueryFromMemory(const char *buffer, size_t size) {
|
|
CgeConfQuery *query;
|
|
|
|
query = queryNew();
|
|
if (!query)
|
|
return NULL;
|
|
|
|
if (!CgeConfFromMemory(buffer, size, populateCb, query)) {
|
|
CgeConfQueryFree(query);
|
|
return NULL;
|
|
}
|
|
return query;
|
|
}
|
|
|
|
CgeConfQuery *CgeConfQueryFromString(const char *str) {
|
|
return CgeConfQueryFromMemory(str, strlen(str));
|
|
}
|
|
|
|
const char *CgeConfQueryGet(CgeConfQuery *query, const char *section,
|
|
const char *key, const char *defaultValue) {
|
|
size_t hash, index, i;
|
|
Entry *entry;
|
|
|
|
if (!query || !key)
|
|
return defaultValue;
|
|
if (!section)
|
|
section = "";
|
|
|
|
hash = hashKey(section, key);
|
|
index = lookup(hash, query->exp, 0);
|
|
|
|
for (i = 0; i < query->capacity; ++i) {
|
|
entry = query->slotData[index];
|
|
if (!entry)
|
|
break;
|
|
if (strcmp(entry->section, section) == 0 &&
|
|
strcmp(entry->key, key) == 0)
|
|
return entry->value;
|
|
index = lookup(hash, query->exp, index);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
void CgeConfQueryFree(CgeConfQuery *query) {
|
|
size_t i;
|
|
|
|
if (!query)
|
|
return;
|
|
|
|
for (i = 0; i < query->size; ++i) {
|
|
free(query->entryData[i].section);
|
|
free(query->entryData[i].key);
|
|
free(query->entryData[i].value);
|
|
}
|
|
free(query->entryData);
|
|
free(query->slotData);
|
|
free(query);
|
|
}
|
|
|
|
void CgeConfQueryIter(CgeConfQuery *query, CgeConfQueryIterCb iter,
|
|
void *user) {
|
|
Entry *entry, *end;
|
|
|
|
entry = query->entryData;
|
|
end = query->entryData + query->size;
|
|
for (; entry != end; ++entry)
|
|
iter(entry->section, entry->key, entry->value, user);
|
|
}
|