1 /* MIT License
2 *
3 * Copyright (c) 2023 Brad House
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * SPDX-License-Identifier: MIT
25 */
26 #include "ares_private.h"
27 #include "ares_htable.h"
28 #include "ares_htable_strvp.h"
29
30 struct ares_htable_strvp {
31 ares_htable_strvp_val_free_t free_val;
32 ares_htable_t *hash;
33 };
34
35 typedef struct {
36 char *key;
37 void *val;
38 ares_htable_strvp_t *parent;
39 } ares_htable_strvp_bucket_t;
40
ares_htable_strvp_destroy(ares_htable_strvp_t * htable)41 void ares_htable_strvp_destroy(ares_htable_strvp_t *htable)
42 {
43 if (htable == NULL) {
44 return;
45 }
46
47 ares_htable_destroy(htable->hash);
48 ares_free(htable);
49 }
50
hash_func(const void * key,unsigned int seed)51 static unsigned int hash_func(const void *key, unsigned int seed)
52 {
53 const char *arg = key;
54 return ares_htable_hash_FNV1a_casecmp((const unsigned char *)arg,
55 ares_strlen(arg), seed);
56 }
57
bucket_key(const void * bucket)58 static const void *bucket_key(const void *bucket)
59 {
60 const ares_htable_strvp_bucket_t *arg = bucket;
61 return arg->key;
62 }
63
bucket_free(void * bucket)64 static void bucket_free(void *bucket)
65 {
66 ares_htable_strvp_bucket_t *arg = bucket;
67
68 if (arg->parent->free_val) {
69 arg->parent->free_val(arg->val);
70 }
71 ares_free(arg->key);
72 ares_free(arg);
73 }
74
key_eq(const void * key1,const void * key2)75 static ares_bool_t key_eq(const void *key1, const void *key2)
76 {
77 return ares_strcaseeq(key1, key2);
78 }
79
80 ares_htable_strvp_t *
ares_htable_strvp_create(ares_htable_strvp_val_free_t val_free)81 ares_htable_strvp_create(ares_htable_strvp_val_free_t val_free)
82 {
83 ares_htable_strvp_t *htable = ares_malloc(sizeof(*htable));
84 if (htable == NULL) {
85 goto fail;
86 }
87
88 htable->hash = ares_htable_create(hash_func, bucket_key, bucket_free, key_eq);
89 if (htable->hash == NULL) {
90 goto fail;
91 }
92
93 htable->free_val = val_free;
94
95 return htable;
96
97 fail:
98 if (htable) {
99 ares_htable_destroy(htable->hash);
100 ares_free(htable);
101 }
102 return NULL;
103 }
104
ares_htable_strvp_insert(ares_htable_strvp_t * htable,const char * key,void * val)105 ares_bool_t ares_htable_strvp_insert(ares_htable_strvp_t *htable,
106 const char *key, void *val)
107 {
108 ares_htable_strvp_bucket_t *bucket = NULL;
109
110 if (htable == NULL || key == NULL) {
111 goto fail;
112 }
113
114 bucket = ares_malloc(sizeof(*bucket));
115 if (bucket == NULL) {
116 goto fail;
117 }
118
119 bucket->parent = htable;
120 bucket->key = ares_strdup(key);
121 if (bucket->key == NULL) {
122 goto fail;
123 }
124 bucket->val = val;
125
126 if (!ares_htable_insert(htable->hash, bucket)) {
127 goto fail;
128 }
129
130 return ARES_TRUE;
131
132 fail:
133 if (bucket) {
134 ares_free(bucket->key);
135 ares_free(bucket);
136 }
137 return ARES_FALSE;
138 }
139
ares_htable_strvp_get(const ares_htable_strvp_t * htable,const char * key,void ** val)140 ares_bool_t ares_htable_strvp_get(const ares_htable_strvp_t *htable,
141 const char *key, void **val)
142 {
143 ares_htable_strvp_bucket_t *bucket = NULL;
144
145 if (val) {
146 *val = NULL;
147 }
148
149 if (htable == NULL || key == NULL) {
150 return ARES_FALSE;
151 }
152
153 bucket = ares_htable_get(htable->hash, key);
154 if (bucket == NULL) {
155 return ARES_FALSE;
156 }
157
158 if (val) {
159 *val = bucket->val;
160 }
161 return ARES_TRUE;
162 }
163
ares_htable_strvp_get_direct(const ares_htable_strvp_t * htable,const char * key)164 void *ares_htable_strvp_get_direct(const ares_htable_strvp_t *htable,
165 const char *key)
166 {
167 void *val = NULL;
168 ares_htable_strvp_get(htable, key, &val);
169 return val;
170 }
171
ares_htable_strvp_remove(ares_htable_strvp_t * htable,const char * key)172 ares_bool_t ares_htable_strvp_remove(ares_htable_strvp_t *htable,
173 const char *key)
174 {
175 if (htable == NULL) {
176 return ARES_FALSE;
177 }
178
179 return ares_htable_remove(htable->hash, key);
180 }
181
ares_htable_strvp_claim(ares_htable_strvp_t * htable,const char * key)182 void *ares_htable_strvp_claim(ares_htable_strvp_t *htable, const char *key)
183 {
184 ares_htable_strvp_bucket_t *bucket = NULL;
185 void *val;
186
187 if (htable == NULL || key == NULL) {
188 return NULL;
189 }
190
191 bucket = ares_htable_get(htable->hash, key);
192 if (bucket == NULL) {
193 return NULL;
194 }
195
196 /* Unassociate value from bucket */
197 val = bucket->val;
198 bucket->val = NULL;
199
200 ares_htable_strvp_remove(htable, key);
201 return val;
202 }
203
ares_htable_strvp_num_keys(const ares_htable_strvp_t * htable)204 size_t ares_htable_strvp_num_keys(const ares_htable_strvp_t *htable)
205 {
206 if (htable == NULL) {
207 return 0;
208 }
209 return ares_htable_num_keys(htable->hash);
210 }
211