See More

// This file is part of SmallBASIC // // Support for dictionary/associative array variables // // This program is distributed under the terms of the GPL v2.0 or later // Download the GNU Public License (GPL) from www.gnu.org // // Copyright(C) 2007-2016 Chris Warren-Smith. #include "config.h" #include #include #include #include #include #include "include/var.h" #include "include/hashmap.h" #define MAP_SIZE 32 /** * Our internal tree element node */ typedef struct Node { var_p_t key; var_p_t value; struct Node *left, *right; } Node; /** * Returns a new tree node */ Node *tree_create_node(var_p_t key) { Node *node = (Node *)malloc(sizeof(Node)); node->key = key; node->value = nullptr; node->left = nullptr; node->right = nullptr; return node; } int str_compare(const char *s1, int s1n, const char *s2, int s2n) { int result = 0; for (int i = 0;; i++) { if (i == s1n || i == s2n) { result = s1n < s2n ? -1 : s1n > s2n ? 1 : 0; break; } char c1 = s1[i]; char c2 = s2[i]; if (c1 != c2) { c1 = tolower(c1); c2 = tolower(c2); if (c1 != c2) { result = c1 < c2 ? -1 : 1; break; } } } return result; } static inline int tree_compare(const char *key, int length, var_p_t vkey) { int len1 = length; if (len1 && key[len1 - 1] == '\0') { len1--; } int len2 = vkey->v.p.length; if (len2 && vkey->v.p.ptr[len2 - 1] == '\0') { len2--; } return str_compare(key, len1, vkey->v.p.ptr, len2); } Node *tree_search(Node **rootp, const char *key, int length) { while (*rootp != nullptr) { int r = tree_compare(key, length, (*rootp)->key); if (r == 0) { return *rootp; } rootp = (r < 0) ? &(*rootp)->left : &(*rootp)->right; } Node *result = tree_create_node(nullptr); *rootp = result; return result; } Node *tree_find(Node **rootp, const char *key, int length) { while (*rootp != nullptr) { int r = tree_compare(key, length, (*rootp)->key); if (r == 0) { return *rootp; } rootp = (r < 0) ? &(*rootp)->left : &(*rootp)->right; } return nullptr; } int hashmap_get_hash(const char *key, int length) { int hash = 1, i; for (i = 0; i < length && key[i] != '\0'; i++) { hash += tolower(key[i]); hash <<= 3; hash ^= (hash >> 3); } return hash; } static inline Node *hashmap_search(var_p_t map, const char *key, int length) { int index = hashmap_get_hash(key, length) % map->v.m.size; Node **table = (Node **)map->v.m.map; Node *result = table[index]; if (result == nullptr) { // new entry result = table[index] = tree_create_node(nullptr); } else { int r = tree_compare(key, length, result->key); if (r < 0) { result = tree_search(&result->left, key, length); } else if (r > 0) { result = tree_search(&result->right, key, length); } } return result; } static inline Node *hashmap_find(var_p_t map, const char *key) { int length = strlen(key); int index = hashmap_get_hash(key, length) % map->v.m.size; Node **table = (Node **)map->v.m.map; Node *result = table[index]; if (result != nullptr) { int r = tree_compare(key, length, result->key); if (r < 0 && result->left != nullptr) { result = tree_find(&result->left, key, length); } else if (r > 0 && result->right != nullptr) { result = tree_find(&result->right, key, length); } else if (r != 0) { result = nullptr; } } return result; } void hashmap_create(var_p_t map, int size) { map->type = V_MAP; map->v.m.count = 0; map->v.m.id = -1; map->v.m.lib_id = -1; map->v.m.cls_id = -1; if (size == 0) { map->v.m.size = MAP_SIZE; } else { map->v.m.size = (size * 100) / 75; } map->v.m.map = calloc(map->v.m.size, sizeof(Node *)); } var_p_t hashmap_putv(var_p_t map, const var_p_t key) { assert(key->type == V_STR); Node *node = hashmap_search(map, key->v.p.ptr, key->v.p.length); assert(node->key == nullptr); node->key = key; node->value = v_new(); map->v.m.count++; return node->value; } var_p_t hashmap_get(var_p_t map, const char *key) { var_p_t result; Node *node = hashmap_find(map, key); if (node != nullptr) { result = node->value; } else { result = nullptr; } return result; }