#include "CgeConfQuery.h" #include #include #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); }