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 "bt_common.h"
20 #include "osi/list.h"
21 #include "osi/hash_map.h"
22 #include "osi/allocator.h"
23 
24 struct hash_map_t;
25 
26 typedef struct hash_map_bucket_t {
27     list_t *list;
28 } hash_map_bucket_t;
29 
30 typedef struct hash_map_t {
31     hash_map_bucket_t *bucket;
32     size_t num_bucket;
33     size_t hash_size;
34     hash_index_fn hash_fn;
35     key_free_fn key_fn;
36     data_free_fn data_fn;
37     key_equality_fn keys_are_equal;
38 } hash_map_t;
39 
40 // Hidden constructor for list, only to be used by us.
41 list_t *list_new_internal(list_free_cb callback);
42 
43 static void bucket_free_(void *data);
44 static bool default_key_equality(const void *x, const void *y);
45 static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
46         const void *key);
47 
48 // Hidden constructor, only to be used by the allocation tracker. Behaves the same as
49 // |hash_map_new|, except you get to specify the allocator.
hash_map_new_internal(size_t num_bucket,hash_index_fn hash_fn,key_free_fn key_fn,data_free_fn data_fn,key_equality_fn equality_fn)50 hash_map_t *hash_map_new_internal(
51     size_t num_bucket,
52     hash_index_fn hash_fn,
53     key_free_fn key_fn,
54     data_free_fn data_fn,
55     key_equality_fn equality_fn)
56 {
57     assert(hash_fn != NULL);
58     assert(num_bucket > 0);
59     hash_map_t *hash_map = osi_calloc(sizeof(hash_map_t));
60     if (hash_map == NULL) {
61         return NULL;
62     }
63 
64     hash_map->hash_fn = hash_fn;
65     hash_map->key_fn = key_fn;
66     hash_map->data_fn = data_fn;
67     hash_map->keys_are_equal = equality_fn ? equality_fn : default_key_equality;
68 
69     hash_map->num_bucket = num_bucket;
70     hash_map->bucket = osi_calloc(sizeof(hash_map_bucket_t) * num_bucket);
71     if (hash_map->bucket == NULL) {
72         osi_free(hash_map);
73         return NULL;
74     }
75     return hash_map;
76 }
77 
hash_map_new(size_t num_bucket,hash_index_fn hash_fn,key_free_fn key_fn,data_free_fn data_fn,key_equality_fn equality_fn)78 hash_map_t *hash_map_new(
79     size_t num_bucket,
80     hash_index_fn hash_fn,
81     key_free_fn key_fn,
82     data_free_fn data_fn,
83     key_equality_fn equality_fn)
84 {
85     return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, equality_fn);
86 }
87 
hash_map_free(hash_map_t * hash_map)88 void hash_map_free(hash_map_t *hash_map)
89 {
90     if (hash_map == NULL) {
91         return;
92     }
93     hash_map_clear(hash_map);
94     osi_free(hash_map->bucket);
95     osi_free(hash_map);
96 }
97 
98 /*
99 bool hash_map_is_empty(const hash_map_t *hash_map) {
100   assert(hash_map != NULL);
101   return (hash_map->hash_size == 0);
102 }
103 
104 size_t hash_map_size(const hash_map_t *hash_map) {
105   assert(hash_map != NULL);
106   return hash_map->hash_size;
107 }
108 
109 size_t hash_map_num_buckets(const hash_map_t *hash_map) {
110   assert(hash_map != NULL);
111   return hash_map->num_bucket;
112 }
113 */
114 
hash_map_has_key(const hash_map_t * hash_map,const void * key)115 bool hash_map_has_key(const hash_map_t *hash_map, const void *key)
116 {
117     assert(hash_map != NULL);
118 
119     hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
120     list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
121 
122     hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
123     return (hash_map_entry != NULL);
124 }
125 
hash_map_set(hash_map_t * hash_map,const void * key,void * data)126 bool hash_map_set(hash_map_t *hash_map, const void *key, void *data)
127 {
128     assert(hash_map != NULL);
129     assert(data != NULL);
130 
131     hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
132 
133     if (hash_map->bucket[hash_key].list == NULL) {
134         hash_map->bucket[hash_key].list = list_new_internal(bucket_free_);
135         if (hash_map->bucket[hash_key].list == NULL) {
136             return false;
137         }
138     }
139     list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
140 
141     hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
142 
143     if (hash_map_entry) {
144         // Calls hash_map callback to delete the hash_map_entry.
145         bool rc = list_remove(hash_bucket_list, hash_map_entry);
146         assert(rc == true);
147         (void)rc;
148     } else {
149         hash_map->hash_size++;
150     }
151     hash_map_entry = osi_calloc(sizeof(hash_map_entry_t));
152     if (hash_map_entry == NULL) {
153         return false;
154     }
155 
156     hash_map_entry->key = key;
157     hash_map_entry->data = data;
158     hash_map_entry->hash_map = hash_map;
159 
160     return list_append(hash_bucket_list, hash_map_entry);
161 }
162 
hash_map_erase(hash_map_t * hash_map,const void * key)163 bool hash_map_erase(hash_map_t *hash_map, const void *key)
164 {
165     assert(hash_map != NULL);
166 
167     hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
168     list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
169 
170     hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
171     if (hash_map_entry == NULL) {
172         return false;
173     }
174 
175     hash_map->hash_size--;
176     bool remove = list_remove(hash_bucket_list, hash_map_entry);
177     if(list_is_empty(hash_map->bucket[hash_key].list)) {
178         list_free(hash_map->bucket[hash_key].list);
179         hash_map->bucket[hash_key].list = NULL;
180     }
181 
182     return remove;
183 }
184 
hash_map_get(const hash_map_t * hash_map,const void * key)185 void *hash_map_get(const hash_map_t *hash_map, const void *key)
186 {
187     assert(hash_map != NULL);
188 
189     hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
190     list_t *hash_bucket_list = hash_map->bucket[hash_key].list;
191 
192     hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key);
193     if (hash_map_entry != NULL) {
194         return hash_map_entry->data;
195     }
196 
197     return NULL;
198 }
199 
hash_map_clear(hash_map_t * hash_map)200 void hash_map_clear(hash_map_t *hash_map)
201 {
202     assert(hash_map != NULL);
203 
204     for (hash_index_t i = 0; i < hash_map->num_bucket; i++) {
205         if (hash_map->bucket[i].list == NULL) {
206             continue;
207         }
208         list_free(hash_map->bucket[i].list);
209         hash_map->bucket[i].list = NULL;
210     }
211 }
212 
hash_map_foreach(hash_map_t * hash_map,hash_map_iter_cb callback,void * context)213 void hash_map_foreach(hash_map_t *hash_map, hash_map_iter_cb callback, void *context)
214 {
215     assert(hash_map != NULL);
216     assert(callback != NULL);
217 
218     for (hash_index_t i = 0; i < hash_map->num_bucket; ++i) {
219         if (hash_map->bucket[i].list == NULL) {
220             continue;
221         }
222         for (const list_node_t *iter = list_begin(hash_map->bucket[i].list);
223                 iter != list_end(hash_map->bucket[i].list);
224                 iter = list_next(iter)) {
225             hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
226             if (!callback(hash_map_entry, context)) {
227                 return;
228             }
229         }
230     }
231 }
232 
bucket_free_(void * data)233 static void bucket_free_(void *data)
234 {
235     assert(data != NULL);
236     hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data;
237     const hash_map_t *hash_map = hash_map_entry->hash_map;
238 
239     if (hash_map->key_fn) {
240         hash_map->key_fn((void *)hash_map_entry->key);
241     }
242     if (hash_map->data_fn) {
243         hash_map->data_fn(hash_map_entry->data);
244     }
245     osi_free(hash_map_entry);
246 }
247 
find_bucket_entry_(list_t * hash_bucket_list,const void * key)248 static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
249         const void *key)
250 {
251 
252     if (hash_bucket_list == NULL) {
253         return NULL;
254     }
255 
256     for (const list_node_t *iter = list_begin(hash_bucket_list);
257             iter != list_end(hash_bucket_list);
258             iter = list_next(iter)) {
259         hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)list_node(iter);
260         if (hash_map_entry->hash_map->keys_are_equal(hash_map_entry->key, key)) {
261             return hash_map_entry;
262         }
263     }
264     return NULL;
265 }
266 
default_key_equality(const void * x,const void * y)267 static bool default_key_equality(const void *x, const void *y)
268 {
269     return x == y;
270 }
271