1 /******************************************************************************
3 * Copyright (C) 2014 Google, Inc.
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 ******************************************************************************/
23 #include "osi/include/allocator.h"
24 #include "osi/include/osi.h"
28 typedef struct hash_map_bucket_t {
32 typedef struct hash_map_t {
33 hash_map_bucket_t *bucket;
36 hash_index_fn hash_fn;
39 const allocator_t *allocator;
40 key_equality_fn keys_are_equal;
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);
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,
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(
55 hash_index_fn hash_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);
64 hash_map_t *hash_map = zeroed_allocator->alloc(sizeof(hash_map_t));
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;
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);
83 hash_map_t *hash_map_new(
85 hash_index_fn hash_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);
92 void hash_map_free(hash_map_t *hash_map) {
95 hash_map_clear(hash_map);
96 hash_map->allocator->free(hash_map->bucket);
97 hash_map->allocator->free(hash_map);
100 bool hash_map_is_empty(const hash_map_t *hash_map) {
101 assert(hash_map != NULL);
102 return (hash_map->hash_size == 0);
105 size_t hash_map_size(const hash_map_t *hash_map) {
106 assert(hash_map != NULL);
107 return hash_map->hash_size;
110 size_t hash_map_num_buckets(const hash_map_t *hash_map) {
111 assert(hash_map != NULL);
112 return hash_map->num_bucket;
115 bool hash_map_has_key(const hash_map_t *hash_map, const void *key) {
116 assert(hash_map != NULL);
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;
121 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
122 return (hash_map_entry != NULL);
125 bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) {
126 assert(hash_map != NULL);
127 assert(data != NULL);
129 hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
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)
136 list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
138 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
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);
145 hash_map->hash_size++;
147 hash_map_entry = hash_map->allocator->alloc(sizeof(hash_map_entry_t));
148 if (hash_map_entry == NULL)
151 hash_map_entry->key = key;
152 hash_map_entry->data = data;
153 hash_map_entry->hash_map = hash_map;
155 return list_append(hash_bucket_list, hash_map_entry);
158 bool hash_map_erase(hash_map_t *hash_map, const void *key) {
159 assert(hash_map != NULL);
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;
164 hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
165 if (hash_map_entry == NULL) {
169 hash_map->hash_size--;
171 return list_remove(hash_bucket_list, hash_map_entry);
174 void *hash_map_get(const hash_map_t *hash_map, const void *key) {
175 assert(hash_map != NULL);
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;
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;
187 void hash_map_clear(hash_map_t *hash_map) {
188 assert(hash_map != NULL);
190 for (hash_index_t i = 0; i < hash_map->num_bucket; i++){
191 if (hash_map->bucket[i].list == NULL)
193 list_free(hash_map->bucket[i].list);
194 hash_map->bucket[i].list = NULL;
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);
202 for (hash_index_t i = 0; i < hash_map->num_bucket; ++i){
203 if (hash_map->bucket[i].list == NULL)
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))
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;
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);
227 static hash_map_entry_t * find_bucket_entry_(list_t *hash_bucket_list,
230 if (hash_bucket_list == NULL)
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;
244 static bool default_key_equality(const void *x, const void *y) {