Android str_parms
str_parms深入分析
在了解android audio,梳理audio hal的流程时,遇到这样一个数据结构,struct str_parms
, 查看代码的上下文,它在这里的主要功能是用来对各种参数进行set与get操作的。其底层采用hash map实现,并通过链接法解决哈希碰撞。
str_parms_create_str
- str_parms_create
调用hashmapCreate来创建hash map,其中str_hash_fn为哈希的算法。
接下来的是将传入的字符串解析为[key, value]的形式, 添加到hashmap中,形如"key=value",如有多个字符串,通过";"来分割。
43Hashmap* hashmapCreate(size_t initialCapacity,
44 int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {
45 assert(hash != NULL);
46 assert(equals != NULL);
47
48 Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));
49 if (map == NULL) {
50 return NULL;
51 }
52
53 // 0.75 load factor.
54 size_t minimumBucketCount = initialCapacity * 4 / 3;
55 map->bucketCount = 1;
56 while (map->bucketCount <= minimumBucketCount) {
57 // Bucket count must be power of 2.
58 map->bucketCount <<= 1;
59 }
60
61 map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));
62 if (map->buckets == NULL) {
63 free(map);
64 return NULL;
65 }
66
67 map->size = 0;
68
69 map->hash = hash;
70 map->equals = equals;
71
72 pthread_mutex_init(&map->lock, nullptr);
73
74 return map;
75}
70struct str_parms *str_parms_create(void)
71{
72 str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));
73 if (!s) return NULL;
74
75 s->map = hashmapCreate(5, str_hash_fn, str_eq);
76 if (!s->map) {
77 free(s);
78 return NULL;
79 }
80
81 return s;
82}
139struct str_parms *str_parms_create_str(const char *_string)
140{
141 struct str_parms *str_parms;
142 char *str;
143 char *kvpair;
144 char *tmpstr;
145 int items = 0;
146
147 str_parms = str_parms_create(); /* 创建哈希表 */
148 if (!str_parms)
149 goto err_create_str_parms;
150
151 str = strdup(_string);
152 if (!str)
153 goto err_strdup;
154
155 ALOGV("%s: source string == '%s'\n", __func__, _string);
156
157 kvpair = strtok_r(str, ";", &tmpstr); /* 选取第一个";"前的字符串 */
158 while (kvpair && *kvpair) {
159 char *eq = strchr(kvpair, '='); /* would love strchrnul */
160 char *value;
161 char *key;
162 void *old_val;
163
164 if (eq == kvpair) /* 字符串中没有"=", 不符合key value形式,直接跳过 */
165 goto next_pair;
166
167 if (eq) {
168 key = strndup(kvpair, eq - kvpair);
169 if (*(++eq))
170 value = strdup(eq);
171 else
172 value = strdup("");
173 } else {
174 key = strdup(kvpair);
175 value = strdup("");
176 }
177
178 /* if we replaced a value, free it */
179 old_val = hashmapPut(str_parms->map, key, value);
180 RELEASE_OWNERSHIP(value);
181 if (old_val) {
182 free(old_val);
183 free(key);
184 } else {
185 RELEASE_OWNERSHIP(key);
186 }
187
188 items++;
189next_pair:
190 kvpair = strtok_r(NULL, ";", &tmpstr);
191 }
192
193 if (!items)
194 ALOGV("%s: no items found in string\n", __func__);
195
196 free(str);
197
198 return str_parms;
199
200err_strdup:
201 str_parms_destroy(str_parms);
202err_create_str_parms:
203 return NULL;
204}
str_parms_add_str
向创建好的hashmap中添加键值对
192void* hashmapPut(Hashmap* map, void* key, void* value) {
193 int hash = hashKey(map, key); /* 计算哈希值 */
194 size_t index = calculateIndex(map->bucketCount, hash); /* 根据hash计算对应的哈希桶index */
195
196 Entry** p = &(map->buckets[index]);
197 while (true) {
198 Entry* current = *p;
199
200 // Add a new entry.
201 if (current == NULL) {
202 *p = createEntry(key, hash, value);
203 if (*p == NULL) {
204 errno = ENOMEM;
205 return NULL;
206 }
207 map->size++;
/* 根据哈希桶中元素数量选择是否增大哈希桶,现在的阈值时0.75 */
208 expandIfNecessary(map);
209 return NULL;
210 }
211
212 // Replace existing entry.
213 if (equalKeys(current->key, current->hash, key, hash, map->equals)) {
214 void* oldValue = current->value;
215 current->value = value;
216 return oldValue;
217 }
218
219 // Move to next entry.
220 p = ¤t->next;
221 }
222}
206int str_parms_add_str(struct str_parms *str_parms, const char *key,
207 const char *value)
208{
209 void *tmp_key = NULL;
210 void *tmp_val = NULL;
211 void *old_val = NULL;
212
213 // strdup and hashmapPut both set errno on failure.
214 // Set errno to 0 so we can recognize whether anything went wrong.
215 int saved_errno = errno;
216 errno = 0;
217
218 tmp_key = strdup(key);
219 if (tmp_key == NULL) {
220 goto clean_up;
221 }
222
223 tmp_val = strdup(value);
224 if (tmp_val == NULL) {
225 goto clean_up;
226 }
227
228 old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);
229 if (old_val == NULL) {
230 // Did hashmapPut fail?
231 if (errno == ENOMEM) {
232 goto clean_up;
233 }
234 // For new keys, hashmap takes ownership of tmp_key and tmp_val.
235 RELEASE_OWNERSHIP(tmp_key);
236 RELEASE_OWNERSHIP(tmp_val);
237 tmp_key = tmp_val = NULL;
238 } else {
239 // For existing keys, hashmap takes ownership of tmp_val.
240 // (It also gives up ownership of old_val.)
241 RELEASE_OWNERSHIP(tmp_val);
242 tmp_val = NULL;
243 }
244
245clean_up:
246 free(tmp_key);
247 free(tmp_val);
248 free(old_val);
249 int result = -errno;
250 errno = saved_errno;
251 return result;
252}
str_parms_get_str
获取hashmap中key对应的value
224void* hashmapGet(Hashmap* map, void* key) {
225 int hash = hashKey(map, key);
226 size_t index = calculateIndex(map->bucketCount, hash);
227
228 Entry* entry = map->buckets[index];
229 while (entry != NULL) {
230 if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {
231 return entry->value;
232 }
233 entry = entry->next;
234 }
235
236 return NULL;
237}
285int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,
286 int len)
287{
288 // TODO: hashmapGet should take a const* key.
289 char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));
290 if (value)
291 return strlcpy(val, value, len);
292
293 return -ENOENT;
294}
str_parms_to_str
将hashmap中的key,value以形如"key1=value1;key2=value2"的形式保存到context。
331static bool combine_strings(void *key, void *value, void *context)
332{
333 char** old_str = static_cast<char**>(context);
334 char *new_str;
335 int ret;
336
337 ret = asprintf(&new_str, "%s%s%s=%s",
338 *old_str ? *old_str : "",
339 *old_str ? ";" : "",
340 (char *)key,
341 (char *)value);
342 if (*old_str)
343 free(*old_str);
344
345 if (ret >= 0) {
346 *old_str = new_str;
347 return true;
348 }
349
350 *old_str = NULL;
351 return false;
352}
261void hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),
262 void* context) {
263 size_t i;
264 for (i = 0; i < map->bucketCount; i++) {
265 Entry* entry = map->buckets[i];
266 while (entry != NULL) {
267 Entry *next = entry->next;
268 if (!callback(entry->key, entry->value, context)) {
269 return;
270 }
271 entry = next;
272 }
273 }
274}
354char *str_parms_to_str(struct str_parms *str_parms)
355{
356 char *str = NULL;
357 hashmapForEach(str_parms->map, combine_strings, &str);
358 return (str != NULL) ? str : strdup("");
359}