169 lines
5.1 KiB
C
169 lines
5.1 KiB
C
#include <c_BinarySearchST.h>
|
|
#include <c_Memory.h>
|
|
|
|
/**
|
|
* Core Rank/Binary Search operation.
|
|
* Returns the exact index if the key is found, or the insertion slot index if not found.
|
|
*/
|
|
static inline c_size_t c_BSST_Rank(const c_BinarySearchST_t* st, const void* key, c_bool_t* out_found) {
|
|
c_size_t left = 0;
|
|
c_size_t right = st->size;
|
|
char* keys_base = (char*)st->keys;
|
|
c_size_t ks = st->key_size;
|
|
|
|
while (left < right) {
|
|
c_size_t mid = left + (right - left) / 2;
|
|
int cmp = st->compar(key, keys_base + (mid * ks));
|
|
|
|
if (cmp == 0) {
|
|
if (out_found) *out_found = C_TRUE;
|
|
return mid;
|
|
} else if (cmp > 0) {
|
|
left = mid + 1;
|
|
} else {
|
|
right = mid;
|
|
}
|
|
}
|
|
|
|
if (out_found) *out_found = C_FALSE;
|
|
return left; // 'left' represents the precise index where the key *should* go
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------------------------ */
|
|
/* */
|
|
|
|
c_err_t c_BinarySearchST_Init(c_BinarySearchST_t* st, c_size_t initial_capacity,
|
|
c_size_t key_size, c_size_t val_size,
|
|
int (*compar)(const void*, const void*)) {
|
|
if (st == NULL || key_size == 0 || val_size == 0 || compar == NULL) return C_ERR_PARAM;
|
|
|
|
st->capacity = (initial_capacity > 0) ? initial_capacity : 4;
|
|
st->key_size = key_size;
|
|
st->val_size = val_size;
|
|
st->size = 0;
|
|
st->compar = compar;
|
|
|
|
st->keys = C_ALLOC(st->capacity * key_size);
|
|
st->vals = C_ALLOC(st->capacity * val_size);
|
|
|
|
if (st->keys == NULL || st->vals == NULL) {
|
|
C_FREE(st->keys);
|
|
C_FREE(st->vals);
|
|
return C_ERR_NOMEM;
|
|
}
|
|
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
void c_BinarySearchST_Destroy(c_BinarySearchST_t* st) {
|
|
if (st) {
|
|
C_FREE(st->keys);
|
|
C_FREE(st->vals);
|
|
st->size = 0;
|
|
st->capacity = 0;
|
|
}
|
|
}
|
|
|
|
c_err_t c_BinarySearchST_Clear(c_BinarySearchST_t* st) {
|
|
if (st == NULL) return C_ERR_PARAM;
|
|
st->size = 0; // Soft reset clears tracking variables but keeps allocated memory blocks
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
c_bool_t c_BinarySearchST_Contains(const c_BinarySearchST_t* st, const void* key) {
|
|
if (st == NULL || key == NULL) return C_FALSE;
|
|
c_bool_t found = C_FALSE;
|
|
c_BSST_Rank(st, key, &found);
|
|
return found;
|
|
}
|
|
|
|
c_err_t c_BinarySearchST_Put(c_BinarySearchST_t* st, const void* key, const void* val) {
|
|
if (st == NULL || key == NULL || val == NULL) return C_ERR_PARAM;
|
|
|
|
c_bool_t found = C_FALSE;
|
|
c_size_t idx = c_BSST_Rank(st, key, &found);
|
|
|
|
char* keys_base = (char*)st->keys;
|
|
char* vals_base = (char*)st->vals;
|
|
c_size_t ks = st->key_size;
|
|
c_size_t vs = st->val_size;
|
|
|
|
// Symbol Table Behavior: If the key already exists, overwrite the value
|
|
if (found) {
|
|
memcpy(vals_base + (idx * vs), val, vs);
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
// Dynamic parallel array capacity expansion
|
|
if (st->size >= st->capacity) {
|
|
c_size_t new_capacity = st->capacity * 2;
|
|
void* new_keys = C_ALLOC(new_capacity * ks);
|
|
void* new_vals = C_ALLOC(new_capacity * vs);
|
|
|
|
if (new_keys == NULL || new_vals == NULL) {
|
|
C_FREE(new_keys);
|
|
C_FREE(new_vals);
|
|
return C_ERR_NOMEM;
|
|
}
|
|
|
|
if (st->size > 0) {
|
|
memcpy(new_keys, st->keys, st->size * ks);
|
|
memcpy(new_vals, st->vals, st->size * vs);
|
|
}
|
|
|
|
C_FREE(st->keys); C_FREE(st->vals);
|
|
st->keys = new_keys; st->vals = new_vals;
|
|
st->capacity = new_capacity;
|
|
keys_base = (char*)st->keys;
|
|
vals_base = (char*)st->vals;
|
|
}
|
|
|
|
// Shift memory components to create a gap for insertion
|
|
if (idx < st->size) {
|
|
memmove(keys_base + ((idx + 1) * ks), keys_base + (idx * ks), (st->size - idx) * ks);
|
|
memmove(vals_base + ((idx + 1) * vs), vals_base + (idx * vs), (st->size - idx) * vs);
|
|
}
|
|
|
|
// Drop elements directly into parallel array channels
|
|
memcpy(keys_base + (idx * ks), key, ks);
|
|
memcpy(vals_base + (idx * vs), val, vs);
|
|
st->size++;
|
|
|
|
return C_ERR_OK;
|
|
}
|
|
|
|
void* c_BinarySearchST_Get(const c_BinarySearchST_t* st, const void* key) {
|
|
if (st == NULL || key == NULL) return NULL;
|
|
|
|
c_bool_t found = C_FALSE;
|
|
c_size_t idx = c_BSST_Rank(st, key, &found);
|
|
|
|
if (found) {
|
|
return (char*)st->vals + (idx * st->val_size);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
c_err_t c_BinarySearchST_Delete(c_BinarySearchST_t* st, const void* key) {
|
|
if (st == NULL || key == NULL) return C_ERR_PARAM;
|
|
|
|
c_bool_t found = C_FALSE;
|
|
c_size_t idx = c_BSST_Rank(st, key, &found);
|
|
|
|
if (!found) return C_ERR_NOTFOUND;
|
|
|
|
char* keys_base = (char*)st->keys;
|
|
char* vals_base = (char*)st->vals;
|
|
c_size_t ks = st->key_size;
|
|
c_size_t vs = st->val_size;
|
|
|
|
// Compress parallel entries down over the deleted item slot
|
|
if (idx < st->size - 1) {
|
|
memmove(keys_base + (idx * ks), keys_base + ((idx + 1) * ks), (st->size - 1 - idx) * ks);
|
|
memmove(vals_base + (idx * vs), vals_base + ((idx + 1) * vs), (st->size - 1 - idx) * vs);
|
|
}
|
|
|
|
st->size--;
|
|
return C_ERR_OK;
|
|
}
|