1 /****************************************************************************** 2 * 3 * Copyright (C) 2014 Google, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19 #include <assert.h> 20 21 #include "osi/include/allocator.h" 22 #include "osi/include/hash_map.h" 23 #include "osi/include/list.h" 24 #include "osi/include/osi.h" 25 26 struct hash_map_t; 27 28 typedef struct hash_map_bucket_t { 29 list_t *list; 30 } hash_map_bucket_t; 31 32 typedef struct hash_map_t { 33 hash_map_bucket_t *bucket; 34 size_t num_bucket; 35 size_t hash_size; 36 hash_index_fn hash_fn; 37 key_free_fn key_fn; 38 data_free_fn data_fn; 39 const allocator_t *allocator; 40 key_equality_fn keys_are_equal; 41 } hash_map_t; 42 43 // Hidden constructor for list, only to be used by us. 44 list_t *list_new_internal(list_free_cb callback, const allocator_t *zeroed_allocator); 45 46 static void bucket_free_(void *data); 47 static bool default_key_equality(const void *x, const void *y); 48 static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list, 49 const void *key); 50 51 // Hidden constructor, only to be used by the allocation tracker. Behaves the same as 52 // |hash_map_new|, except you get to specify the allocator. 53 hash_map_t *hash_map_new_internal( 54 size_t num_bucket, 55 hash_index_fn hash_fn, 56 key_free_fn key_fn, 57 data_free_fn data_fn, 58 key_equality_fn equality_fn, 59 const allocator_t *zeroed_allocator) { 60 assert(hash_fn != NULL); 61 assert(num_bucket > 0); 62 assert(zeroed_allocator != NULL); 63 64 hash_map_t *hash_map = zeroed_allocator->alloc(sizeof(hash_map_t)); 65 if (hash_map == NULL) 66 return NULL; 67 68 hash_map->hash_fn = hash_fn; 69 hash_map->key_fn = key_fn; 70 hash_map->data_fn = data_fn; 71 hash_map->allocator = zeroed_allocator; 72 hash_map->keys_are_equal = equality_fn ? equality_fn : default_key_equality; 73 74 hash_map->num_bucket = num_bucket; 75 hash_map->bucket = zeroed_allocator->alloc(sizeof(hash_map_bucket_t) * num_bucket); 76 if (hash_map->bucket == NULL) { 77 zeroed_allocator->free(hash_map); 78 return NULL; 79 } 80 return hash_map; 81 } 82 83 hash_map_t *hash_map_new( 84 size_t num_bucket, 85 hash_index_fn hash_fn, 86 key_free_fn key_fn, 87 data_free_fn data_fn, 88 key_equality_fn equality_fn) { 89 return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, equality_fn, &allocator_calloc); 90 } 91 92 void hash_map_free(hash_map_t *hash_map) { 93 if (hash_map == NULL) 94 return; 95 hash_map_clear(hash_map); 96 hash_map->allocator->free(hash_map->bucket); 97 hash_map->allocator->free(hash_map); 98 } 99 100 bool hash_map_is_empty(const hash_map_t *hash_map) { 101 assert(hash_map != NULL); 102 return (hash_map->hash_size == 0); 103 } 104 105 size_t hash_map_size(const hash_map_t *hash_map) { 106 assert(hash_map != NULL); 107 return hash_map->hash_size; 108 } 109 110 size_t hash_map_num_buckets(const hash_map_t *hash_map) { 111 assert(hash_map != NULL); 112 return hash_map->num_bucket; 113 } 114 115 bool hash_map_has_key(const hash_map_t *hash_map, const void *key) { 116 assert(hash_map != NULL); 117 118 hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket; 119 list_t *hash_bucket_list = hash_map->bucket[hash_key].list; 120 121 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); 122 return (hash_map_entry != NULL); 123 } 124 125 bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) { 126 assert(hash_map != NULL); 127 assert(data != NULL); 128 129 hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket; 130 131 if (hash_map->bucket[hash_key].list == NULL) { 132 hash_map->bucket[hash_key].list = list_new_internal(bucket_free_, hash_map->allocator); 133 if (hash_map->bucket[hash_key].list == NULL) 134 return false; 135 } 136 list_t *hash_bucket_list = hash_map->bucket[hash_key].list; 137 138 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); 139 140 if (hash_map_entry) { 141 // Calls hash_map callback to delete the hash_map_entry. 142 UNUSED_ATTR bool rc = list_remove(hash_bucket_list, hash_map_entry); 143 assert(rc == true); 144 } else { 145 hash_map->hash_size++; 146 } 147 hash_map_entry = hash_map->allocator->alloc(sizeof(hash_map_entry_t)); 148 if (hash_map_entry == NULL) 149 return false; 150 151 hash_map_entry->key = key; 152 hash_map_entry->data = data; 153 hash_map_entry->hash_map = hash_map; 154 155 return list_append(hash_bucket_list, hash_map_entry); 156 } 157 158 bool hash_map_erase(hash_map_t *hash_map, const void *key) { 159 assert(hash_map != NULL); 160 161 hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket; 162 list_t *hash_bucket_list = hash_map->bucket[hash_key].list; 163 164 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); 165 if (hash_map_entry == NULL) { 166 return false; 167 } 168 169 hash_map->hash_size--; 170 171 return list_remove(hash_bucket_list, hash_map_entry); 172 } 173 174 void *hash_map_get(const hash_map_t *hash_map, const void *key) { 175 assert(hash_map != NULL); 176 177 hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket; 178 list_t *hash_bucket_list = hash_map->bucket[hash_key].list; 179 180 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); 181 if (hash_map_entry != NULL) 182 return hash_map_entry->data; 183 184 return NULL; 185 } 186 187 void hash_map_clear(hash_map_t *hash_map) { 188 assert(hash_map != NULL); 189 190 for (hash_index_t i = 0; i < hash_map->num_bucket; i++){ 191 if (hash_map->bucket[i].list == NULL) 192 continue; 193 list_free(hash_map->bucket[i].list); 194 hash_map->bucket[i].list = NULL; 195 } 196 } 197 198 void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context) { 199 assert(hash_map != NULL); 200 assert(callback != NULL); 201 202 for (hash_index_t i = 0; i < hash_map->num_bucket; ++i){ 203 if (hash_map->bucket[i].list == NULL) 204 continue; 205 for (const list_node_t *iter = list_begin(hash_map->bucket[i].list); 206 iter != list_end(hash_map->bucket[i].list); 207 iter = list_next(iter)) { 208 hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter); 209 if (!callback(hash_map_entry, context)) 210 return; 211 } 212 } 213 } 214 215 static void bucket_free_(void *data) { 216 assert(data != NULL); 217 hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data; 218 const hash_map_t *hash_map = hash_map_entry->hash_map; 219 220 if (hash_map->key_fn) 221 hash_map->key_fn((void *)hash_map_entry->key); 222 if (hash_map->data_fn) 223 hash_map->data_fn(hash_map_entry->data); 224 hash_map->allocator->free(hash_map_entry); 225 } 226 227 static hash_map_entry_t * find_bucket_entry_(list_t *hash_bucket_list, 228 const void *key) { 229 230 if (hash_bucket_list == NULL) 231 return NULL; 232 233 for (const list_node_t *iter = list_begin(hash_bucket_list); 234 iter != list_end(hash_bucket_list); 235 iter = list_next(iter)) { 236 hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter); 237 if (hash_map_entry->hash_map->keys_are_equal(hash_map_entry->key, key)) { 238 return hash_map_entry; 239 } 240 } 241 return NULL; 242 } 243 244 static bool default_key_equality(const void *x, const void *y) { 245 return x == y; 246 } 247