Case is a fixed size, no runtime-allocation Hash Table written in C.
Case employs open-addressing hashing, a little tweaking for a long period use.
Complexity follows open-addressing except for insertion, every operations are on O(1).
Insertion(ctAdd), to make constant terms of other oprations, is O(n) for searching empty K-V data index. However, the searching is just a iteration over continuous bit flags(int[]), it works quite fast.
Thanks for "header", average counts of access packets for rehash-searching( increment hash ) are:
| search success | count |
|---|---|
| successed | < 1.08 |
| failed | < 1.16 |
Allocated heap size of each data, with denoting
- bytes of int :
I - a multiple of 32 larger than x =
M32(x) - a multiple of
Ilarger than x =MI[x] - required key length =
k - required value length =
v - required item counts =
n
| Data | size (bytes) | Eplanations |
|---|---|---|
| Item count capa | MI[n] |
The table capacity for item count is determined as MI[n] |
| Each K-V data | M32[k+v+2] |
+2 is for null terminated safety for max size key or value. |
| All K-Vs array | M32[k+v+2] * MI[n] |
|
| All headers array | I * MI[n] |
"Header" is one of tweaks of Case. It contains acutal "index" to K-V data |
| Bit flags of data | MI[n]/8 |
"Bit flags" is also a tweak. It make searching empty K-V data position faster. |
| Whole Heap | M32[k+v+2]*MI[n] + I*MI[n] + MI[n]/8 |
The table sacrifices a little memory to avoid hash-collision. Even if items count reach the max capacity, the level is as well as 12.5% for an ordinary hash table.
A hash(key) points to one of headers(int[]), then header indexes K-V data.
With allocating 8 times larger headers than actual table's capacity, tables occupancy ratio will be smaller than 12.5% at worst with little extra memory space.
CaseTable exposes you a method ctFet.
It just load the pointers for the key, and the value onto CaseTable.key, CaseTable.value.
It gives you much chance to avoid over-head.
Deletion(ctDel) does some cleaning (turn deleted into empty) if possible, the table performance will be quite stable for long term use.
Clone the repository into your directory, then include the header case/src/table.h.
CaseTable createCaseTable(int key_size_bytes,
int value_size_bytes,
int table_item_capacity);
CaseTable table, *ct;
// Create a table on your stack.
// Following code creates a table with,
// 100 KV pairs which is 8+64(+2) bytes long.
table = createCaseTable(8, 64, 100);
ct = &table;
printf("Total Volume of alloced heap:%d\n",
table.total_allocated_heap);char key[25] = "longer KEY than required.";
char value[5] = "value";
int signal;
// Over sized key, value works after get truncated.
signal = ctPut(ct, key, value);
// Return 0 when successed, -1 when failed.
printf("%d\n", signal);
// ctFet fetches the target pointer into
// table.key, table.value.
// So it does NOT copy string.
signal = ctFet(ct, key);
printf("%s\n", ct.key);
// => longer K
printf("%s\n", ct.value);int result;
result = ctHas(ct, "some key");
if (result == -1) {
printf("Case does NOT have the key!\n");
}
else {
printf("Case has the key!\n");
// result > -1.
}
result = ctDel(ct, "target key");// Do the same with Fet and Del, but a little bit faster.
ctPop(ct, "target key");
ct.count; // store the actual count of items stored.
ctFree(ct);// free all alloced heap of the table.