146#include "qinternal.h"
147#include "utilities/qhash.h"
148#include "containers/qhasharr.h"
150#define COLLISION_MARK (-1)
151#define EXTBLOCK_MARK (-2)
155static qhasharr_slot_t* get_slots(qhasharr_t *tbl);
156static int find_avail(qhasharr_t *tbl,
int startidx);
157static int get_idx(qhasharr_t *tbl,
const void *name,
size_t namesize,
159static void *get_data(qhasharr_t *tbl,
int idx,
size_t *size);
160static bool put_data(qhasharr_t *tbl,
int idx, uint32_t hash,
const void *name,
161 size_t namesize,
const void *data,
size_t datasize,
163static bool copy_slot(qhasharr_t *tbl,
int idx1,
int idx2);
164static bool remove_slot(qhasharr_t *tbl,
int idx);
165static bool remove_data(qhasharr_t *tbl,
int idx);
180 size_t memsize =
sizeof(qhasharr_data_t)
181 + (
sizeof(qhasharr_slot_t) * (max));
208qhasharr_t *
qhasharr(
void *memory,
size_t memsize) {
210 qhasharr_data_t *tbldata = (qhasharr_data_t *) memory;
215 int maxslots = (memsize -
sizeof(qhasharr_data_t))
216 /
sizeof(qhasharr_slot_t);
217 if (maxslots < 1 || memsize <=
sizeof(qhasharr_t)) {
223 memset((
void *) tbldata, 0, memsize);
224 tbldata->maxslots = maxslots;
225 tbldata->usedslots = 0;
230 qhasharr_t *tbl = (qhasharr_t *) malloc(
sizeof(qhasharr_t));
235 memset((
void *) tbl, 0,
sizeof(qhasharr_t));
299 data, (data) ? strlen(data) + 1 : 0);
318 DYNAMIC_VSPRINTF(str, format);
345 const void *data,
size_t datasize) {
346 if (tbl == NULL || name == NULL || namesize == 0 || data == NULL
352 qhasharr_data_t *tbldata = tbl->data;
353 qhasharr_slot_t *tblslots = get_slots(tbl);
356 if (tbldata->usedslots >= tbldata->maxslots) {
365 if (tblslots[hash].count == 0) {
367 if (put_data(tbl, hash, hash, name, namesize, data, datasize,
371 }
else if (tblslots[hash].count > 0) {
373 int idx = get_idx(tbl, name, namesize, hash);
380 int idx = find_avail(tbl, hash);
387 if (put_data(tbl, idx, hash, name, namesize, data, datasize,
388 COLLISION_MARK) ==
false) {
393 tblslots[hash].count++;
399 int idx = find_avail(tbl, hash + 1);
406 copy_slot(tbl, idx, hash);
407 remove_slot(tbl, hash);
410 if (tblslots[idx].link != -1) {
411 tblslots[tblslots[idx].link].hash = idx;
413 if (tblslots[idx].count == EXTBLOCK_MARK) {
414 tblslots[tblslots[idx].hash].link = idx;
418 if (put_data(tbl, hash, hash, name, namesize, data, datasize,
443void *
qhasharr_get(qhasharr_t *tbl,
const char *name,
size_t *datasize) {
486 if (tbl == NULL || name == NULL || namesize == 0) {
491 qhasharr_data_t *tbldata = tbl->data;
495 int idx = get_idx(tbl, name, namesize, hash);
501 return get_data(tbl, idx, datasize);
534 if (tbl == NULL || name == NULL || namesize == 0) {
539 qhasharr_data_t *tbldata = tbl->data;
543 int idx = get_idx(tbl, name, namesize, hash);
595 qhasharr_data_t *tbldata = tbl->data;
596 qhasharr_slot_t *tblslots = get_slots(tbl);
598 if (tblslots[idx].count == 1) {
600 remove_data(tbl, idx);
601 }
else if (tblslots[idx].count > 1) {
604 for (idx2 = idx + 1;; idx2++) {
605 if (idx2 >= tbldata->maxslots)
611 if (tblslots[idx2].count == COLLISION_MARK
612 && tblslots[idx2].hash == tblslots[idx].hash) {
618 int backupcount = tblslots[idx].count;
619 remove_data(tbl, idx);
620 copy_slot(tbl, idx, idx2);
621 remove_slot(tbl, idx2);
623 tblslots[idx].count = backupcount - 1;
624 if (tblslots[idx].link != -1) {
625 tblslots[tblslots[idx].link].hash = idx;
628 }
else if (tblslots[idx].count == COLLISION_MARK) {
630 if (tblslots[tblslots[idx].hash].count <= 1) {
634 tblslots[tblslots[idx].hash].count--;
637 remove_data(tbl, idx);
675 if (tbl == NULL || obj == NULL || idx == NULL) {
680 qhasharr_data_t *tbldata = tbl->data;
681 qhasharr_slot_t *tblslots = get_slots(tbl);
683 for (; *idx < tbldata->maxslots; (*idx)++) {
684 if (tblslots[*idx].count == 0 || tblslots[*idx].count == EXTBLOCK_MARK) {
688 size_t namesize = tblslots[*idx].data.pair.namesize;
689 if (namesize > Q_HASHARR_NAMESIZE)
690 namesize = Q_HASHARR_NAMESIZE;
692 obj->name = malloc(namesize + 1);
693 if (obj->name == NULL) {
697 memcpy(obj->name, tblslots[*idx].data.pair.name, namesize);
698 memcpy(obj->name + namesize,
"", 1);
699 obj->namesize = namesize;
701 obj->data = get_data(tbl, *idx, &obj->datasize);
702 if (obj->data == NULL) {
731 qhasharr_data_t *tbldata = tbl->data;
733 if (maxslots != NULL)
734 *maxslots = tbldata->maxslots;
735 if (usedslots != NULL)
736 *usedslots = tbldata->usedslots;
754 qhasharr_data_t *tbldata = tbl->data;
755 qhasharr_slot_t *tblslots = get_slots(tbl);
757 if (tbldata->usedslots == 0)
760 tbldata->usedslots = 0;
764 memset((
void *) tblslots,
'\0',
765 (tbldata->maxslots *
sizeof(qhasharr_slot_t)));
789 qhasharr_slot_t *tblslots = get_slots(tbl);
793 while (tbl->getnext(tbl, &obj, &idx) ==
true) {
794 uint16_t namesize = tblslots[idx - 1].data.pair.namesize;
795 _q_textout(out, obj.name, obj.namesize, MAX_HUMANOUT);
796 fprintf(out,
"%s(%d)=", (namesize > Q_HASHARR_NAMESIZE) ?
"..." :
"",
798 _q_textout(out, obj.data, obj.datasize, MAX_HUMANOUT);
799 fprintf(out,
" (%zu)\n", obj.datasize);
806 qhasharr_data_t *tbldata = tbl->data;
807 fprintf(out,
"%d elements (slot %d used/%d total)\n",
808 tbldata->num, tbldata->usedslots, tbldata->maxslots);
809 for (idx = 0; idx < tbldata->maxslots; idx++) {
810 if (tblslots[idx].count == 0)
continue;
812 fprintf(out,
"slot=%d,type=", idx);
813 if (tblslots[idx].count == EXTBLOCK_MARK) {
814 fprintf(out,
"EXTEND");
815 fprintf(out,
",prev=%d", tblslots[idx].hash);
816 fprintf(out,
",next=%d", tblslots[idx].link);
817 fprintf(out,
",datasize=%d", tblslots[idx].datasize);
818 fprintf(out,
",data=");
820 tblslots[idx].data.ext.data,
821 tblslots[idx].datasize,
824 fprintf(out,
"%s", (tblslots[idx].count == COLLISION_MARK)?
"COLISN":
"NORMAL");
825 fprintf(out,
",next=%d", tblslots[idx].link);
826 fprintf(out,
",count=%d", tblslots[idx].count);
827 fprintf(out,
",hash=%u", tblslots[idx].hash);
828 fprintf(out,
",namesize=%d", tblslots[idx].data.pair.namesize);
829 fprintf(out,
",datasize=%d", tblslots[idx].datasize);
830 fprintf(out,
",name=");
832 tblslots[idx].data.pair.name,
833 (tblslots[idx].data.pair.namesize>Q_HASHARR_NAMESIZE)
835 : tblslots[idx].data.pair.namesize,
837 fprintf(out,
",data=");
839 tblslots[idx].data.pair.data,
840 tblslots[idx].datasize,
866static qhasharr_slot_t* get_slots(qhasharr_t *tbl) {
867 return (qhasharr_slot_t*) ((
char*) (tbl->data) +
sizeof(qhasharr_data_t));
871static int find_avail(qhasharr_t *tbl,
int startidx) {
872 qhasharr_data_t *tbldata = tbl->data;
873 qhasharr_slot_t *tblslots = get_slots(tbl);
875 if (startidx >= tbldata->maxslots)
880 if (tblslots[idx].count == 0)
884 if (idx >= tbldata->maxslots)
893static int get_idx(qhasharr_t *tbl,
const void *name,
size_t namesize,
895 qhasharr_data_t *tbldata = tbl->data;
896 qhasharr_slot_t *tblslots = get_slots(tbl);
898 if (tblslots[hash].count > 0) {
900 for (count = 0, idx = hash; count < tblslots[hash].count;) {
901 if (tblslots[idx].hash == hash
902 && (tblslots[idx].count > 0 || tblslots[idx].count == COLLISION_MARK)) {
908 if (namesize == tblslots[idx].data.pair.namesize) {
909 if (namesize <= Q_HASHARR_NAMESIZE) {
911 if (!memcmp(name, tblslots[idx].data.pair.name,
917 unsigned char namemd5[16];
919 if (!memcmp(name, tblslots[idx].data.pair.name,
922 tblslots[idx].data.pair.namemd5,
932 if (idx >= tbldata->maxslots)
946static void *get_data(qhasharr_t *tbl,
int idx,
size_t *size) {
952 qhasharr_slot_t *tblslots = get_slots(tbl);
956 for (newidx = idx, datasize = 0;; newidx = tblslots[newidx].link) {
957 datasize += tblslots[newidx].datasize;
958 if (tblslots[newidx].link == -1)
963 if ((data = malloc(datasize)) == NULL) {
968 for (newidx = idx, dp = data;; newidx = tblslots[newidx].link) {
969 if (tblslots[newidx].count == EXTBLOCK_MARK) {
971 memcpy(dp, (
void *) tblslots[newidx].data.ext.data,
972 tblslots[newidx].datasize);
975 memcpy(dp, (
void *) tblslots[newidx].data.pair.data,
976 tblslots[newidx].datasize);
979 dp += tblslots[newidx].datasize;
980 if (tblslots[newidx].link == -1)
989static bool put_data(qhasharr_t *tbl,
int idx, uint32_t hash,
const void *name,
990 size_t namesize,
const void *data,
size_t datasize,
992 qhasharr_data_t *tbldata = tbl->data;
993 qhasharr_slot_t *tblslots = get_slots(tbl);
995 assert(tblslots[idx].count == 0);
997 unsigned char namemd5[16];
1001 tblslots[idx].count = count;
1002 tblslots[idx].hash = hash;
1003 memcpy(tblslots[idx].data.pair.name, name,
1004 (namesize < Q_HASHARR_NAMESIZE) ? namesize : Q_HASHARR_NAMESIZE);
1005 memcpy((
char *) tblslots[idx].data.pair.namemd5, (
char *) namemd5, 16);
1006 tblslots[idx].data.pair.namesize = namesize;
1007 tblslots[idx].link = -1;
1012 for (newidx = idx, savesize = 0; savesize < datasize;) {
1014 int tmpidx = find_avail(tbl, newidx + 1);
1016 remove_data(tbl, idx);
1022 memset((
void *) (&tblslots[tmpidx]),
'\0',
sizeof(qhasharr_slot_t));
1024 tblslots[tmpidx].count = EXTBLOCK_MARK;
1025 tblslots[tmpidx].hash = newidx;
1026 tblslots[tmpidx].link = -1;
1027 tblslots[tmpidx].datasize = 0;
1028 tblslots[newidx].link = tmpidx;
1034 size_t copysize = datasize - savesize;
1035 if (tblslots[newidx].count == EXTBLOCK_MARK) {
1037 if (copysize >
sizeof(
struct Q_HASHARR_SLOT_EXT)) {
1038 copysize =
sizeof(
struct Q_HASHARR_SLOT_EXT);
1040 memcpy(tblslots[newidx].data.ext.data, data + savesize, copysize);
1043 if (copysize > Q_HASHARR_DATASIZE) {
1044 copysize = Q_HASHARR_DATASIZE;
1046 memcpy(tblslots[newidx].data.pair.data, data + savesize, copysize);
1051 tblslots[newidx].datasize = copysize;
1052 savesize += copysize;
1055 tbldata->usedslots++;
1061static bool copy_slot(qhasharr_t *tbl,
int idx1,
int idx2) {
1062 qhasharr_slot_t *tblslots = get_slots(tbl);
1064 if (tblslots[idx1].count != 0 || tblslots[idx2].count == 0) {
1069 memcpy((
void *) (&tblslots[idx1]), (
void *) (&tblslots[idx2]),
1070 sizeof(qhasharr_slot_t));
1075static bool remove_slot(qhasharr_t *tbl,
int idx) {
1076 qhasharr_slot_t *tblslots = get_slots(tbl);
1077 assert(tblslots[idx].count != 0);
1079 tblslots[idx].count = 0;
1083static bool remove_data(qhasharr_t *tbl,
int idx) {
1084 qhasharr_data_t *tbldata = tbl->data;
1085 qhasharr_slot_t *tblslots = get_slots(tbl);
1086 assert(tblslots[idx].count != 0);
1089 int link = tblslots[idx].link;
1090 remove_slot(tbl, idx);
1091 tbldata->usedslots--;
bool qhashmd5(const void *data, size_t nbytes, void *retbuf)
Calculate 128-bit(16-bytes) MD5 hash.
uint32_t qhashmurmur3_32(const void *data, size_t nbytes)
Get 32-bit Murmur3 hash.
void qhasharr_clear(qhasharr_t *tbl)
qhasharr->clear(): Clears this table so that it contains no keys.
int qhasharr_size(qhasharr_t *tbl, int *maxslots, int *usedslots)
qhasharr->size(): Returns the number of objects in this table.
bool qhasharr_put_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, const void *data, size_t datasize)
qhasharr->put_by_obj(): Put an object into this table by key object.
bool qhasharr_remove_by_idx(qhasharr_t *tbl, int idx)
qhasharr->remove_by_idx(): Remove an object from this table by index number.
void * qhasharr_get(qhasharr_t *tbl, const char *name, size_t *datasize)
qhasharr->get(): Get an object from this table
bool qhasharr_debug(qhasharr_t *tbl, FILE *out)
qhasharr->debug(): Print the hash table for debugging purposes.
bool qhasharr_put(qhasharr_t *tbl, const char *name, const void *data, size_t datasize)
qhasharr->put(): Put an object into this table.
qhasharr_t * qhasharr(void *memory, size_t memsize)
Initialize a static hash table.
size_t qhasharr_calculate_memsize(int max)
Get how much memory is needed for N slots.
char * qhasharr_getstr(qhasharr_t *tbl, const char *name)
qhasharr->getstr(): Finds an object with the given name and returns it as a string.
bool qhasharr_getnext(qhasharr_t *tbl, qhasharr_obj_t *obj, int *idx)
qhasharr->getnext(): Get next element.
void * qhasharr_get_by_obj(qhasharr_t *tbl, const void *name, size_t namesize, size_t *datasize)
qhasharr->get_by_obj(): Get an object from this table by key object.
bool qhasharr_putstrf(qhasharr_t *tbl, const char *name, const char *format,...)
qhasharr->putstrf(): Put a formatted string into this table.
void qhasharr_free(qhasharr_t *tbl)
qhasharr->free(): Free table reference object.
bool qhasharr_remove(qhasharr_t *tbl, const char *name)
qhasharr->remove(): Remove an object from this table.
bool qhasharr_putstr(qhasharr_t *tbl, const char *name, const char *data)
qhasharr->putstr(): Put a string into this table
bool qhasharr_remove_by_obj(qhasharr_t *tbl, const char *name, size_t namesize)
qhasharr->remove_by_obj(): Remove an object from this table by key object