|
|
1.1 ! root 1: /* Hash tables for Objective C internal structures ! 2: Copyright (C) 1993 Free Software Foundation, Inc. ! 3: ! 4: This file is part of GNU CC. ! 5: ! 6: GNU CC is free software; you can redistribute it and/or modify ! 7: it under the terms of the GNU General Public License as published by ! 8: the Free Software Foundation; either version 2, or (at your option) ! 9: any later version. ! 10: ! 11: GNU CC is distributed in the hope that it will be useful, ! 12: but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 14: GNU General Public License for more details. ! 15: ! 16: You should have received a copy of the GNU General Public License ! 17: along with GNU CC; see the file COPYING. If not, write to ! 18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ ! 19: ! 20: /* As a special exception, if you link this library with files ! 21: compiled with GCC to produce an executable, this does not cause ! 22: the resulting executable to be covered by the GNU General Public License. ! 23: This exception does not however invalidate any other reasons why ! 24: the executable file might be covered by the GNU General Public License. */ ! 25: ! 26: #include "assert.h" ! 27: ! 28: #include "objc/hash.h" ! 29: #include "objc/objc.h" ! 30: ! 31: #include "runtime.h" /* for DEBUG_PRINTF */ ! 32: ! 33: /* These two macros determine when a hash table is full and ! 34: by how much it should be expanded respectively. ! 35: ! 36: These equations are percentages. */ ! 37: #define FULLNESS(cache) \ ! 38: ((((cache)->size * 75) / 100) <= (cache)->used) ! 39: #define EXPANSION(cache) \ ! 40: ((cache)->size * 2) ! 41: ! 42: void *__objc_xcalloc (size_t, size_t); ! 43: ! 44: cache_ptr ! 45: hash_new (unsigned int size, hash_func_type hash_func, ! 46: compare_func_type compare_func) ! 47: { ! 48: cache_ptr cache; ! 49: ! 50: /* Pass me a value greater than 0 and a power of 2. */ ! 51: assert (size); ! 52: assert (!(size & (size - 1))); ! 53: ! 54: /* Allocate the cache structure. calloc insures ! 55: its initialization for default values. */ ! 56: cache = (cache_ptr) __objc_xcalloc (1, sizeof (struct cache)); ! 57: assert (cache); ! 58: ! 59: /* Allocate the array of buckets for the cache. ! 60: calloc initializes all of the pointers to NULL. */ ! 61: cache->node_table ! 62: = (node_ptr *) __objc_xcalloc (size, sizeof (node_ptr)); ! 63: assert (cache->node_table); ! 64: ! 65: cache->size = size; ! 66: ! 67: /* This should work for all processor architectures? */ ! 68: cache->mask = (size - 1); ! 69: ! 70: /* Store the hashing function so that codes can be computed. */ ! 71: cache->hash_func = hash_func; ! 72: ! 73: /* Store the function that compares hash keys to ! 74: determine if they are equal. */ ! 75: cache->compare_func = compare_func; ! 76: ! 77: return cache; ! 78: } ! 79: ! 80: ! 81: void ! 82: hash_delete (cache_ptr cache) ! 83: { ! 84: node_ptr node; ! 85: ! 86: ! 87: /* Purge all key/value pairs from the table. */ ! 88: while ((node = hash_next (cache, NULL))) ! 89: hash_remove (cache, node->key); ! 90: ! 91: /* Release the array of nodes and the cache itself. */ ! 92: free (cache->node_table); ! 93: free (cache); ! 94: } ! 95: ! 96: ! 97: void ! 98: hash_add (cache_ptr *cachep, const void *key, void *value) ! 99: { ! 100: size_t indx = (*(*cachep)->hash_func)(*cachep, key); ! 101: node_ptr node = (node_ptr) __objc_xcalloc (1, sizeof (struct cache_node)); ! 102: ! 103: ! 104: assert (node); ! 105: ! 106: /* Initialize the new node. */ ! 107: node->key = key; ! 108: node->value = value; ! 109: node->next = (*cachep)->node_table[indx]; ! 110: ! 111: /* Debugging. ! 112: Check the list for another key. */ ! 113: #ifdef DEBUG ! 114: { node_ptr node1 = (*cachep)->node_table[indx]; ! 115: ! 116: while (node1) { ! 117: ! 118: assert (node1->key != key); ! 119: node1 = node1->next; ! 120: } ! 121: } ! 122: #endif ! 123: ! 124: /* Install the node as the first element on the list. */ ! 125: (*cachep)->node_table[indx] = node; ! 126: ! 127: /* Bump the number of entries in the cache. */ ! 128: ++(*cachep)->used; ! 129: ! 130: /* Check the hash table's fullness. We're going ! 131: to expand if it is above the fullness level. */ ! 132: if (FULLNESS (*cachep)) { ! 133: ! 134: /* The hash table has reached its fullness level. Time to ! 135: expand it. ! 136: ! 137: I'm using a slow method here but is built on other ! 138: primitive functions thereby increasing its ! 139: correctness. */ ! 140: node_ptr node1 = NULL; ! 141: cache_ptr new = hash_new (EXPANSION (*cachep), ! 142: (*cachep)->hash_func, ! 143: (*cachep)->compare_func); ! 144: ! 145: DEBUG_PRINTF ("Expanding cache %#x from %d to %d\n", ! 146: *cachep, (*cachep)->size, new->size); ! 147: ! 148: /* Copy the nodes from the first hash table to the new one. */ ! 149: while ((node1 = hash_next (*cachep, node1))) ! 150: hash_add (&new, node1->key, node1->value); ! 151: ! 152: /* Trash the old cache. */ ! 153: hash_delete (*cachep); ! 154: ! 155: /* Return a pointer to the new hash table. */ ! 156: *cachep = new; ! 157: } ! 158: } ! 159: ! 160: ! 161: void ! 162: hash_remove (cache_ptr cache, const void *key) ! 163: { ! 164: size_t indx = (*cache->hash_func)(cache, key); ! 165: node_ptr node = cache->node_table[indx]; ! 166: ! 167: ! 168: /* We assume there is an entry in the table. Error if it is not. */ ! 169: assert (node); ! 170: ! 171: /* Special case. First element is the key/value pair to be removed. */ ! 172: if ((*cache->compare_func)(node->key, key)) { ! 173: cache->node_table[indx] = node->next; ! 174: free (node); ! 175: } else { ! 176: ! 177: /* Otherwise, find the hash entry. */ ! 178: node_ptr prev = node; ! 179: BOOL removed = NO; ! 180: ! 181: do { ! 182: ! 183: if ((*cache->compare_func)(node->key, key)) { ! 184: prev->next = node->next, removed = YES; ! 185: free (node); ! 186: } else ! 187: prev = node, node = node->next; ! 188: } while (!removed && node); ! 189: assert (removed); ! 190: } ! 191: ! 192: /* Decrement the number of entries in the hash table. */ ! 193: --cache->used; ! 194: } ! 195: ! 196: ! 197: node_ptr ! 198: hash_next (cache_ptr cache, node_ptr node) ! 199: { ! 200: /* If the scan is being started then reset the last node ! 201: visitied pointer and bucket index. */ ! 202: if (!node) ! 203: cache->last_bucket = 0; ! 204: ! 205: /* If there is a node visited last then check for another ! 206: entry in the same bucket; Otherwise step to the next bucket. */ ! 207: if (node) { ! 208: if (node->next) ! 209: /* There is a node which follows the last node ! 210: returned. Step to that node and retun it. */ ! 211: return node->next; ! 212: else ! 213: ++cache->last_bucket; ! 214: } ! 215: ! 216: /* If the list isn't exhausted then search the buckets for ! 217: other nodes. */ ! 218: if (cache->last_bucket < cache->size) { ! 219: /* Scan the remainder of the buckets looking for an entry ! 220: at the head of the list. Return the first item found. */ ! 221: while (cache->last_bucket < cache->size) ! 222: if (cache->node_table[cache->last_bucket]) ! 223: return cache->node_table[cache->last_bucket]; ! 224: else ! 225: ++cache->last_bucket; ! 226: ! 227: /* No further nodes were found in the hash table. */ ! 228: return NULL; ! 229: } else ! 230: return NULL; ! 231: } ! 232: ! 233: ! 234: /* Given KEY, return corresponding value for it in CACHE. ! 235: Return NULL if the KEY is not recorded. */ ! 236: ! 237: void * ! 238: hash_value_for_key (cache_ptr cache, const void *key) ! 239: { ! 240: node_ptr node = cache->node_table[(*cache->hash_func)(cache, key)]; ! 241: void *retval = NULL; ! 242: ! 243: if (node) ! 244: do { ! 245: if ((*cache->compare_func)(node->key, key)) ! 246: retval = node->value; ! 247: else ! 248: node = node->next; ! 249: } while (!retval && node); ! 250: ! 251: return retval; ! 252: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.