#include #include #include #include /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* Internal constructor helper to build an isolated TST node capsule */ static c_TSTNode_t* c_TSTNode_Create(char c) { c_TSTNode_t* node = (c_TSTNode_t*)C_CALLOC(1, sizeof(c_TSTNode_t)); if (node) { node->c = c; } return node; } /* Internal destructor helper to clear TST nodes non-recursively using an explicit heap stack */ static void c_TSTNode_DestroyRecursive(c_TSTNode_t* root) { if (!root) return; c_ArrayStack_t node_stack; c_ArrayStack_Init(&node_stack, sizeof(c_TSTNode_t*), 256); c_ArrayStack_Push(&node_stack, &root); while (!c_ArrayStack_IsEmpty(&node_stack)) { c_TSTNode_t* curr = 0; c_ArrayStack_Pop(&node_stack, &curr); c_bool_t advanced = C_FALSE; // Push children onto the cleanup stack frame and clear links to avoid cycles if (curr->left) { c_ArrayStack_Push(&node_stack, &curr->left); curr->left = NULL; advanced = C_TRUE; } else if (curr->mid) { c_ArrayStack_Push(&node_stack, &curr->mid); curr->mid = NULL; advanced = C_TRUE; } else if (curr->right) { c_ArrayStack_Push(&node_stack, &curr->right); curr->right = NULL; advanced = C_TRUE; } if (advanced == C_FALSE) { C_FREE(curr); } } c_ArrayStack_Destroy(&node_stack); } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ c_err_t c_TST_Init(c_TST_t* self) { if (!self) return C_ERR_PARAM; self->root = NULL; self->size = 0; return C_ERR_OK; } void c_TST_Destroy(c_TST_t* self) { if (!self) return; c_TSTNode_DestroyRecursive(self->root); self->root = NULL; self->size = 0; } /* Internal recursive worker to support clean TST node creation and value insertions */ static c_TSTNode_t* c_TST_PutWorker(c_TSTNode_t* x, const char* key, c_size_t d, void* value, c_bool_t* is_new, c_err_t* err) { char c = key[d]; if (!x) { x = c_TSTNode_Create(c); if (!x) { *err = C_ERR_NOMEM; return NULL; } } if (c < x->c) { x->left = c_TST_PutWorker(x->left, key, d, value, is_new, err); } else if (c > x->c) { x->right = c_TST_PutWorker(x->right, key, d, value, is_new, err); } else if (d < strlen(key) - 1) { x->mid = c_TST_PutWorker(x->mid, key, d + 1, value, is_new, err); } else { if (x->value == NULL) { *is_new = C_TRUE; } x->value = value; } return x; } c_err_t c_TST_Put(c_TST_t* self, const char* key, void* value) { if (!self || !key || strlen(key) == 0 || !value) return C_ERR_PARAM; c_bool_t is_new = C_FALSE; c_err_t err = C_ERR_OK; self->root = c_TST_PutWorker(self->root, key, 0, value, &is_new, &err); if (err == C_ERR_OK && is_new == C_TRUE) { self->size++; } return err; } void* c_TST_Get(c_TST_t* self, const char* key) { if (!self || !key || strlen(key) == 0 || !self->root) return NULL; c_TSTNode_t* curr = self->root; c_size_t d = 0; c_size_t len = strlen(key); while (curr) { char c = key[d]; if (c < curr->c) { curr = curr->left; } else if (c > curr->c) { curr = curr->right; } else if (d < len - 1) { curr = curr->mid; d++; } else { return curr->value; } } return NULL; } c_bool_t c_TST_Contains(c_TST_t* self, const char* key) { return (c_TST_Get(self, key) != NULL) ? C_TRUE : C_FALSE; } /* Internal prefix traversal worker */ static void c_TST_CollectWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, c_size_t depth, c_StringList* result) { if (!x) return; // Explore smaller alphabetical character trees leftward (keeps current string prefix length unchanged) c_TST_CollectWorker(x->left, sb, depth, result); // Append the matching node character token directly to the string builder c_StringBuffer_Append(sb, &(x->c), 1); if (x->value != NULL) { c_StringList_Append(result, sb->buffer); } // Continue crawling down matching children on the middle branch c_TST_CollectWorker(x->mid, sb, depth + 1, result); // Backtrack step: restore parent character layout configuration length boundaries c_StringBuffer_SetLength(sb, depth); // Explore larger alphabetical character trees rightward c_TST_CollectWorker(x->right, sb, depth, result); } c_err_t c_TST_KeysWithPrefix(c_TST_t* self, const char* prefix, c_StringList* result) { if (!self || !prefix || !result || !self->root) return C_ERR_PARAM; c_TSTNode_t* curr = self->root; c_size_t d = 0; c_size_t len = strlen(prefix); // Navigate to the end node matching the prefix string character rules while (curr) { char c = prefix[d]; if (c < curr->c) { curr = curr->left; } else if (c > curr->c) { curr = curr->right; } else if (d < len - 1) { curr = curr->mid; d++; } else { break; // Prefix matched up to curr node boundaries } } if (!curr) return C_ERR_OK; // Prefix not found safely yields 0 matches c_StringBuffer_t sb; if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) return C_ERR_NOMEM; // Seed our builder with the matching prefix handle string tokens c_StringBuffer_Append(&sb, prefix, len); // If the prefix node boundary itself holds an active value, record it first if (curr->value != NULL) { c_StringList_Append(result, prefix); } // Crawl down the middle branch to extract all matching multi-character children variations c_TST_CollectWorker(curr->mid, &sb, len, result); c_StringBuffer_Destroy(&sb); return C_ERR_OK; } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ /* Internal recursive wildcard collector worker */ static void c_TST_MatchWorker(c_TSTNode_t* x, c_StringBuffer_t* sb, const char* pattern, c_size_t d, c_StringList* result) { if (!x) return; char c = pattern[d]; c_size_t len = strlen(pattern); // Explore smaller characters leftward if the pattern permits or if it's a wildcard if (c == '.' || c < x->c) { c_TST_MatchWorker(x->left, sb, pattern, d, result); } // Process current node character matching boundaries if (c == '.' || c == x->c) { // Append the current split character token onto your string buffer builder stack c_StringBuffer_Append(sb, &(x->c), 1); // Terminal case: If we have reached the final character index of the pattern layout string if (d == len - 1) { if (x->value != NULL) { c_StringList_Append(result, sb->buffer); } } else { // Advance deeper down the middle branch to explore matching multi-character continuations c_TST_MatchWorker(x->mid, sb, pattern, d + 1, result); } // Backtracking unwinding step: reset logical buffer length configuration framework c_StringBuffer_SetLength(sb, d); } // Explore larger characters rightward if the pattern permits or if it's a wildcard if (c == '.' || c > x->c) { c_TST_MatchWorker(x->right, sb, pattern, d, result); } } /** * Gather all keys currently matching a specific wildcard pattern string inside the TST */ c_err_t c_TST_KeysThatMatch(c_TST_t* self, const char* pattern, c_StringList* result) { if (!self || !pattern || strlen(pattern) == 0 || !result || !self->root) { return C_ERR_PARAM; } c_StringBuffer_t sb; if (c_StringBuffer_Init(&sb, 256) != C_ERR_OK) { return C_ERR_NOMEM; } // Start crawling the ternary search tree using our shared string buffer accumulator c_TST_MatchWorker(self->root, &sb, pattern, 0, result); c_StringBuffer_Destroy(&sb); return C_ERR_OK; } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ char* c_TST_LongestPrefixOf(c_TST_t* self, const char* query) { if (!self || !query || !self->root) { return NULL; } c_TSTNode_t* curr = self->root; c_size_t query_len = strlen(query); c_size_t longest_match_len = 0; c_bool_t match_found = C_FALSE; c_size_t d = 0; // Run an iterative O(L) scan across TST split character tokens while (curr && d < query_len) { char c = query[d]; if (c < curr->c) { curr = curr->left; // Step left: current character is smaller } else if (c > curr->c) { curr = curr->right; // Step right: current character is larger } else { // Character matches curr->c exactly! Check if this marks a complete key if (curr->value != NULL) { longest_match_len = d + 1; match_found = C_TRUE; } curr = curr->mid; // Advance down the middle branch d++; // Advance to next character in query string } } // Allocate an isolated heap buffer to hold the output string copy c_size_t output_bytes = match_found ? (longest_match_len + 1) : 1; char* result_str = (char*)C_ALLOC(output_bytes); if (!result_str) { return NULL; } if (match_found == C_TRUE) { memcpy(result_str, query, longest_match_len); result_str[longest_match_len] = '\0'; } else { result_str[0] = '\0'; // Return a clean empty string if no prefix matches } return result_str; }