• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 2024 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_dict.h"
29 
30 struct ares_htable_dict {
31   ares_htable_t *hash;
32 };
33 
34 typedef struct {
35   char               *key;
36   char               *val;
37   ares_htable_dict_t *parent;
38 } ares_htable_dict_bucket_t;
39 
ares_htable_dict_destroy(ares_htable_dict_t * htable)40 void ares_htable_dict_destroy(ares_htable_dict_t *htable)
41 {
42   if (htable == NULL) {
43     return; /* LCOV_EXCL_LINE: DefensiveCoding */
44   }
45 
46   ares_htable_destroy(htable->hash);
47   ares_free(htable);
48 }
49 
hash_func(const void * key,unsigned int seed)50 static unsigned int hash_func(const void *key, unsigned int seed)
51 {
52   return ares_htable_hash_FNV1a_casecmp(key, ares_strlen(key), seed);
53 }
54 
bucket_key(const void * bucket)55 static const void *bucket_key(const void *bucket)
56 {
57   const ares_htable_dict_bucket_t *arg = bucket;
58   return arg->key;
59 }
60 
bucket_free(void * bucket)61 static void bucket_free(void *bucket)
62 {
63   ares_htable_dict_bucket_t *arg = bucket;
64 
65   ares_free(arg->key);
66   ares_free(arg->val);
67 
68   ares_free(arg);
69 }
70 
key_eq(const void * key1,const void * key2)71 static ares_bool_t key_eq(const void *key1, const void *key2)
72 {
73   return ares_strcaseeq(key1, key2);
74 }
75 
ares_htable_dict_create(void)76 ares_htable_dict_t *ares_htable_dict_create(void)
77 {
78   ares_htable_dict_t *htable = ares_malloc(sizeof(*htable));
79   if (htable == NULL) {
80     goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
81   }
82 
83   htable->hash = ares_htable_create(hash_func, bucket_key, bucket_free, key_eq);
84   if (htable->hash == NULL) {
85     goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
86   }
87 
88   return htable;
89 
90 /* LCOV_EXCL_START: OutOfMemory */
91 fail:
92   if (htable) {
93     ares_htable_destroy(htable->hash);
94     ares_free(htable);
95   }
96   return NULL;
97   /* LCOV_EXCL_STOP */
98 }
99 
ares_htable_dict_insert(ares_htable_dict_t * htable,const char * key,const char * val)100 ares_bool_t ares_htable_dict_insert(ares_htable_dict_t *htable, const char *key,
101                                     const char *val)
102 {
103   ares_htable_dict_bucket_t *bucket = NULL;
104 
105   if (htable == NULL || ares_strlen(key) == 0) {
106     goto fail;
107   }
108 
109   bucket = ares_malloc_zero(sizeof(*bucket));
110   if (bucket == NULL) {
111     goto fail;
112   }
113 
114   bucket->parent = htable;
115   bucket->key    = ares_strdup(key);
116   if (bucket->key == NULL) {
117     goto fail;
118   }
119 
120   if (val != NULL) {
121     bucket->val = ares_strdup(val);
122     if (bucket->val == NULL) {
123       goto fail;
124     }
125   }
126 
127   if (!ares_htable_insert(htable->hash, bucket)) {
128     goto fail;
129   }
130 
131   return ARES_TRUE;
132 
133 fail:
134   if (bucket) {
135     ares_free(bucket->val);
136     ares_free(bucket);
137   }
138   return ARES_FALSE;
139 }
140 
ares_htable_dict_get(const ares_htable_dict_t * htable,const char * key,const char ** val)141 ares_bool_t ares_htable_dict_get(const ares_htable_dict_t *htable,
142                                  const char *key, const char **val)
143 {
144   const ares_htable_dict_bucket_t *bucket = NULL;
145 
146   if (val) {
147     *val = NULL;
148   }
149 
150   if (htable == NULL) {
151     return ARES_FALSE;
152   }
153 
154   bucket = ares_htable_get(htable->hash, key);
155   if (bucket == NULL) {
156     return ARES_FALSE;
157   }
158 
159   if (val) {
160     *val = bucket->val;
161   }
162   return ARES_TRUE;
163 }
164 
ares_htable_dict_get_direct(const ares_htable_dict_t * htable,const char * key)165 const char *ares_htable_dict_get_direct(const ares_htable_dict_t *htable,
166                                         const char               *key)
167 {
168   const char *val = NULL;
169   ares_htable_dict_get(htable, key, &val);
170   return val;
171 }
172 
ares_htable_dict_remove(ares_htable_dict_t * htable,const char * key)173 ares_bool_t ares_htable_dict_remove(ares_htable_dict_t *htable, 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_dict_num_keys(const ares_htable_dict_t * htable)182 size_t ares_htable_dict_num_keys(const ares_htable_dict_t *htable)
183 {
184   if (htable == NULL) {
185     return 0;
186   }
187   return ares_htable_num_keys(htable->hash);
188 }
189 
ares_htable_dict_keys(const ares_htable_dict_t * htable,size_t * num)190 char **ares_htable_dict_keys(const ares_htable_dict_t *htable, size_t *num)
191 {
192   const void **buckets = NULL;
193   size_t       cnt     = 0;
194   char       **out     = NULL;
195   size_t       i;
196 
197   if (htable == NULL || num == NULL) {
198     return NULL; /* LCOV_EXCL_LINE: DefensiveCoding */
199   }
200 
201   *num = 0;
202 
203   buckets = ares_htable_all_buckets(htable->hash, &cnt);
204   if (buckets == NULL || cnt == 0) {
205     return NULL;
206   }
207 
208   out = ares_malloc_zero(sizeof(*out) * cnt);
209   if (out == NULL) {
210     goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
211   }
212 
213   for (i = 0; i < cnt; i++) {
214     out[i] = ares_strdup(((const ares_htable_dict_bucket_t *)buckets[i])->key);
215     if (out[i] == NULL) {
216       goto fail;
217     }
218   }
219 
220   ares_free(buckets);
221   *num = cnt;
222   return out;
223 
224 fail:
225   *num = 0;
226   ares_free_array(out, cnt, ares_free);
227   return NULL;
228 }
229