Mstdlib-1.24.0
m_hash_dict.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2015 Monetra Technologies, LLC.
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 shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_HASH_DICT_H__
25#define __M_HASH_DICT_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31
32/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
33
34__BEGIN_DECLS
35
36/*! \addtogroup m_hash_dict Hashtable - Dictionary (string/string)
37 * \ingroup m_hashtable
38 *
39 * Hashtable, meant for storing string key/value pairs.
40 *
41 * References to the data will always be read-only.
42 * All keys and values will be duplicated by the hashtable.
43 *
44 * @{
45 */
46
47struct M_hash_dict;
48/* Currently a direct map to M_hashtable private opaque type,
49 * simply using casting to prevent the 'wrap' overhead of mallocing when it
50 * is not necessary.
51 */
52typedef struct M_hash_dict M_hash_dict_t;
53
54struct M_hash_dict_enum;
55/* Used for enumerating a M_hash_dict. */
56typedef struct M_hash_dict_enum M_hash_dict_enum_t;
57
58
59/*! Flags for controlling the behavior of the hashtable. */
60typedef enum {
61 M_HASH_DICT_NONE = 0, /*!< Case sensitive single value (new values replace). */
62 M_HASH_DICT_CASECMP = 1 << 0, /*!< Key compare is case insensitive. */
63 M_HASH_DICT_KEYS_UPPER = 1 << 1, /*!< Keys will be upper cased before being inserted. Should be used
64 in conjunction with M_HASH_DICT_CASECMP. */
65 M_HASH_DICT_KEYS_LOWER = 1 << 2, /*!< Keys will be lower cased before being inserted. Should be used
66 in conjunction with M_HASH_DICT_CASECMP. */
67 M_HASH_DICT_KEYS_ORDERED = 1 << 3, /*!< Keys should be ordered. Default is insertion order unless the
68 sorted option is specified. */
69 M_HASH_DICT_KEYS_SORTASC = 1 << 4, /*!< When the keys are ordered sort them using the key_equality function. */
70 M_HASH_DICT_KEYS_SORTDESC = 1 << 5, /*!< When the keys are ordered sort them using the key_equality function. */
71 M_HASH_DICT_MULTI_VALUE = 1 << 6, /*!< Allow keys to contain multiple values.
72 Sorted in insertion order another sorting is specified. */
73 M_HASH_DICT_MULTI_SORTASC = 1 << 7, /*!< Allow keys to contain multiple values sorted in ascending order */
74 M_HASH_DICT_MULTI_SORTDESC = 1 << 8, /*!< Allow keys to contain multiple values sorted in descending order */
75 M_HASH_DICT_MULTI_GETLAST = 1 << 9, /*!< When using get and get_direct function get the last value from the list
76 when allowing multiple values. The default is to get the first value. */
77 M_HASH_DICT_MULTI_CASECMP = 1 << 10, /*!< Value compare is case insensitive. */
78 M_HASH_DICT_STATIC_SEED = 1 << 11, /*!< Use a static seed for hash function initialization. This greatly reduces
79 the security of the hashtable and removes collision attack protections.
80 This should only be used as a performance optimization when creating
81 millions of hashtables with static data specifically for quick look up.
82 DO _NOT_ use this flag with any hashtable that could store user
83 generated data! Be very careful about duplicating a hashtable that
84 was created with this flag. All duplicates will use the static seed. */
85 M_HASH_DICT_DESER_TRIM_WHITESPACE = 1 << 26, /*!< During deserialization, trim whitespace. */
87
88
89/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
90
91/*! Create a new hashtable.
92 *
93 * The hashtable will pre-allocate an array of buckets based on the rounded up size specified. Any hash collisions
94 * will result in those collisions being chained together via a linked list. The hashtable will auto-expand by a
95 * power of 2 when the fill percentage specified is reached. All key entries are compared in a case-insensitive
96 * fashion, and are duplicated internally. Values are duplicated. Case is preserved for both keys and values.
97 *
98 * \param[in] size Size of the hash table. If not specified as a power of 2, will
99 * be rounded up to the nearest power of 2.
100 * \param[in] fillpct The maximum fill percentage before the hash table is expanded. If
101 * 0 is specified, the hashtable will never expand, otherwise the
102 * value must be between 1 and 99 (recommended: 75).
103 * \param[in] flags M_hash_dict_flags_t flags for modifying behavior.
104 *
105 * \return Allocated hashtable.
106 *
107 * \see M_hash_dict_destroy
108 */
109M_API M_hash_dict_t *M_hash_dict_create(size_t size, M_uint8 fillpct, M_uint32 flags) M_MALLOC;
110
111
112/*! Destroy the hashtable.
113 *
114 * \param[in] h Hashtable to destroy
115 */
116M_API void M_hash_dict_destroy(M_hash_dict_t *h) M_FREE(1);
117
118
119/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
120
121/*! Insert an entry into the hashtable.
122 *
123 * If this is a multi-value dictionary (\see M_HASH_DICT_MULTI_VALUE) and an entry already
124 * exists under the given key, the new value is added onto the end of the list. Otherwise,
125 * the new value replaces any previous value stored under the given key.
126 *
127 * \param[in,out] h Hashtable being referenced.
128 * \param[in] key Key to insert.
129 * A NULL or empty string is explicity disallowed.
130 * \param[in] value Value to insert into hashtable. Value will be duplicated,
131 * and case will be preserved. May be NULL.
132 *
133 * \return M_TRUE on success, or M_FALSE on failure.
134 */
135M_API M_bool M_hash_dict_insert(M_hash_dict_t *h, const char *key, const char *value);
136
137
138/*! Remove an entry from the hashtable.
139 *
140 * \param[in,out] h Hashtable being referenced.
141 * \param[in] key Key to remove from the hashtable.
142 * A NULL or empty string is explicitly disallowed.
143 *
144 * \return M_TRUE on success, or M_FALSE if key does not exist.
145 */
146M_API M_bool M_hash_dict_remove(M_hash_dict_t *h, const char *key);
147
148
149/*! Retrieve the value for a key from the hashtable.
150 *
151 * \param[in] h Hashtable being referenced.
152 * \param[in] key Key for value.
153 * A NULL or empty string is explicitly disallowed.
154 * \param[out] value Pointer to value stored in the hashtable. Optional, pass NULL if not needed.
155 *
156 * \return M_TRUE if value retrieved, M_FALSE if key does not exist.
157 */
158M_API M_bool M_hash_dict_get(const M_hash_dict_t *h, const char *key, const char **value);
159
160
161/*! Retrieve the value for a key from the hashtable, and return it directly as the return value.
162 *
163 * This cannot be used if you need to differentiate between a key that doesn't exist vs a key with a NULL value.
164 *
165 * \param[in] h Hashtable being referenced.
166 * \param[in] key Key for value to retrieve from the hashtable.
167 * A NULL or empty string is explicitly disallowed.
168 *
169 * \return NULL if key doesn't exist or NULL value on file, otherwise the value.
170 */
171M_API const char *M_hash_dict_get_direct(const M_hash_dict_t *h, const char *key);
172
173
174/*! Retrieve the value for a key from the hashtable, and return it directly as the return value.
175 *
176 * If the key does not exist or the value is NULL the provided default value will be returned.
177 *
178 * \param[in] h Hashtable being referenced.
179 * \param[in] key Key for value to retrieve from the hashtable.
180 * A NULL or empty string is explicitly disallowed.
181 * \param[in] def A default value.
182 *
183 * \return def if key doesn't exist or NULL value on file, otherwise the value.
184 */
185M_API const char *M_hash_dict_get_direct_default(const M_hash_dict_t *h, const char *key, const char *def);
186
187
188/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
189
190/*! Wether the hashtable a multi value table.
191 *
192 * \param[in] h Hashtable being referenced.
193 *
194 * \return M_TRUE if a multi value hashtable.
195 */
197
198
199/*! Get the number of values for a given key.
200 *
201 * \param[in] h Hashtable being referenced.
202 * \param[in] key Key for value to retrieve.
203 * \param[out] len The number of values.
204 *
205 * \return M_TRUE if length is retrieved, M_FALSE if key does not exist.
206 */
207M_API M_bool M_hash_dict_multi_len(const M_hash_dict_t *h, const char *key, size_t *len);
208
209
210/*! Retrieve the value for a key from the given index when supporting muli-values.
211 *
212 * \param[in] h Hashtable being referenced.
213 * \param[in] key Key for value to retrieve.
214 * \param[in] idx The index the value resides at.
215 * \param[out] value Pointer to value stored. Optional, pass NULL if not needed.
216 *
217 * \return M_TRUE if value retrieved, M_FALSE if key does not exist
218 */
219M_API M_bool M_hash_dict_multi_get(const M_hash_dict_t *h, const char *key, size_t idx, const char **value);
220
221
222/*! Retrieve the value for a key from the given index when supporting muli-values.
223 *
224 * \param[in] h Hashtable being referenced.
225 * \param[in] key Key for value to retrieve.
226 * \param[in] idx The index the value resides at.
227 *
228 * \return M_TRUE if value retrieved, M_FALSE if key does not exist.
229 */
230M_API const char *M_hash_dict_multi_get_direct(const M_hash_dict_t *h, const char *key, size_t idx);
231
232
233/*! Remove a value from the hashtable when supporting muli-values.
234 *
235 * If all values have been removed then the key will be removed.
236 *
237 * \param[in,out] h Hashtable being referenced
238 * \param[in] key Key for value to retrieve.
239 * \param[in] idx The index the value resides at.
240 *
241 * \return M_TRUE if the value was removed, M_FALSE if key does not exist.
242 */
243M_API M_bool M_hash_dict_multi_remove(M_hash_dict_t *h, const char *key, size_t idx);
244
245
246/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
247
248/*! Retrieve the current size (number of buckets/slots, not necessarily used).
249 *
250 * \param[in] h Hashtable being referenced.
251 *
252 * \return Size of the hashtable
253 */
254M_API M_uint32 M_hash_dict_size(const M_hash_dict_t *h);
255
256
257/*! Retrieve the number of collisions for hashtable entries that has occurred since creation.
258 *
259 * \param[in] h Hashtable being referenced.
260 *
261 * \return Number of collisions.
262 */
264
265
266/*! Retrieve the number of expansions/rehashes since creation.
267 *
268 * \param[in] h Hashtable being referenced.
269 *
270 * \return number of expansions/rehashes.
271 */
273
274
275/*! Retrieve the number of entries in the hashtable.
276 *
277 * This is the number of keys stored.
278 *
279 * \param[in] h Hashtable being referenced.
280 *
281 * \return number of entries in the hashtable.
282 */
283M_API size_t M_hash_dict_num_keys(const M_hash_dict_t *h);
284
285
286/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
287
288/*! Start an enumeration of the keys within a hashtable.
289 *
290 * \param[in] h Hashtable being referenced.
291 * \param[out] hashenum Outputs an initialized state variable for starting an enumeration.
292 *
293 * \return Number of items in the hashtable
294 *
295 * \see M_hash_dict_enumerate_free
296 */
297M_API size_t M_hash_dict_enumerate(const M_hash_dict_t *h, M_hash_dict_enum_t **hashenum);
298
299
300/*! Retrieve the next item from a hashtable enumeration.
301 *
302 * If multi-value, keys will appear multiple times as each value will be
303 * retrieved individually.
304 *
305 * \param[in] h Hashtable being referenced.
306 * \param[in,out] hashenum State variable for tracking the enumeration process.
307 * \param[out] key Value of next enumerated key. Optional, pass NULL if not needed.
308 * \param[out] value Value of next enumerated value. Optional, pass NULL if not needed.
309 *
310 * \return M_TRUE if enumeration succeeded, M_FALSE if no more keys.
311 */
312M_API M_bool M_hash_dict_enumerate_next(const M_hash_dict_t *h, M_hash_dict_enum_t *hashenum, const char **key, const char **value);
313
314
315/*! Destroy an enumeration state.
316 *
317 * \param[in] hashenum Enumeration to destroy.
318 */
320
321
322/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
323
324/*! Merge two hashtables together.
325 *
326 * The second (src) hashtable will be destroyed automatically upon completion of this function. Any key/value
327 * pointers for the hashtable will be directly copied over to the destination hashtable, they will not be
328 * duplicated. Any keys which exist in 'dest' that also exist in 'src' will be overwritten by the 'src' value.
329 *
330 * If dest and src are multi-value, all values from src will be copied into dest and the values from
331 * dest will not be removed. If dest is not multi-value and src is, then only the last value in src will
332 * be present in dest. If dest is multi-value and src is not, then the value from src will be added to dest.
333 *
334 * \param[in,out] dest Pointer by reference to the hashtable receiving the key/value pairs.
335 * if dest is NULL, the src address will simply be copied to dest.
336 * \param[in,out] src Pointer to the hashtable giving up its key/value pairs.
337 */
338M_API void M_hash_dict_merge(M_hash_dict_t **dest, M_hash_dict_t *src) M_FREE(2);
339
340
341/*! Duplicate an existing hashtable.
342 *
343 * Copying all keys and values.
344 *
345 * \param[in] h Hashtable to be copied.
346 *
347 * \return Duplicated hashtable.
348 */
350
351
352/*! Possible flags for M_hash_dict_serialize() */
353typedef enum {
354 M_HASH_DICT_SER_FLAG_NONE = 0, /*!< Default flags */
355 M_HASH_DICT_SER_FLAG_ALWAYS_QUOTE = 1 << 0, /*!< Always quote the value even if not necessary. Cannot be used with M_HASH_DICT_SER_FLAG_QUOTE_NON_ANS */
356 M_HASH_DICT_SER_FLAG_QUOTE_NON_ANS = 1 << 1, /*!< Quote any string that contains non Alpha-numeric or single space (0x20). Cannot be used with M_HASH_DICT_SER_FLAG_ALWAYS_QUOTE */
357 M_HASH_DICT_SER_FLAG_HEXENCODE_NONPRINT = 1 << 2, /*!< Any non-printable characters should be hex-encoded as [%02X] in the resulting output string */
358 M_HASH_DICT_SER_FLAG_LF_TO_CRLF = 1 << 3 /*!< If a separator is specified as LF '\n', convert it to CRLF `\r\n`. Necessary since delimiters can only be a single character. */
360
361
362/*! Serialize a dictionary and write it to the provided M_buf_t.
363 *
364 * \param[in] dict Dictionary to serialize
365 * \param[in] buf Destination buffer to write to, already initialized.
366 * \param[in] delim Delimiter between key/value pairs (recommended ';')
367 * \param[in] kv_delim Delimiter between the key and value (recommended '=')
368 * \param[in] quote Quote character (recommended '"')
369 * \param[in] escape Escape character (recommended '\' or '"')
370 * \param[in] flags Bitmap of possible M_hash_dict_ser_flag_t flags
371 * \return M_TRUE on success or M_FALSE on failure.
372 */
373M_API M_bool M_hash_dict_serialize_buf(const M_hash_dict_t *dict, M_buf_t *buf, char delim, char kv_delim, char quote, char escape, M_uint32 flags);
374
375
376/*! Serialize a dictionary into a string as per the definition.
377 *
378 * \param[in] dict Dictionary to serialize
379 * \param[in] delim Delimiter between key/value pairs (recommended ';')
380 * \param[in] kv_delim Delimiter between the key and value (recommended '=')
381 * \param[in] quote Quote character (recommended '"')
382 * \param[in] escape Escape character (recommended '\' or '"')
383 * \param[in] flags Bitmap of possible M_hash_dict_ser_flag_t flags
384 * \return String of serialized data, or NULL on failure.
385 */
386M_API char *M_hash_dict_serialize(const M_hash_dict_t *dict, char delim, char kv_delim, char quote, char escape, M_uint32 flags);
387
388
389/*! Deserialize a string into a hashtable as per the definition.
390 *
391 * \param[in] str String to deserialize
392 * \param[in] len Length of string
393 * \param[in] delim Delimiter between key/value pairs (recommended ';')
394 * \param[in] kv_delim Delimiter between the key and value (recommended '=')
395 * \param[in] quote Quote character (recommended '"')
396 * \param[in] escape Escape character (recommended '\' or '"')
397 * \param[in] flags Bitmap of possible M_hash_dict_flags_t flags
398 * \return Dictionary of key/value pairs, or NULL on failure to parse.
399 */
400M_API M_hash_dict_t *M_hash_dict_deserialize(const char *str, size_t len, char delim, char kv_delim, char quote, char escape, M_uint32 flags);
401
402
403/*! @} */
404
405__END_DECLS
406
407#endif /* __M_HASH_DICT_H__ */
struct M_buf M_buf_t
Definition: m_buf.h:77
struct M_hash_dict M_hash_dict_t
Definition: m_hash_dict.h:52
void M_hash_dict_enumerate_free(M_hash_dict_enum_t *hashenum)
M_hash_dict_t * M_hash_dict_deserialize(const char *str, size_t len, char delim, char kv_delim, char quote, char escape, M_uint32 flags)
size_t M_hash_dict_num_expansions(const M_hash_dict_t *h)
M_bool M_hash_dict_multi_remove(M_hash_dict_t *h, const char *key, size_t idx)
M_hash_dict_flags_t
Definition: m_hash_dict.h:60
M_bool M_hash_dict_get(const M_hash_dict_t *h, const char *key, const char **value)
size_t M_hash_dict_num_keys(const M_hash_dict_t *h)
size_t M_hash_dict_enumerate(const M_hash_dict_t *h, M_hash_dict_enum_t **hashenum)
M_uint32 M_hash_dict_size(const M_hash_dict_t *h)
M_hash_dict_t * M_hash_dict_duplicate(const M_hash_dict_t *h) M_MALLOC
M_bool M_hash_dict_enumerate_next(const M_hash_dict_t *h, M_hash_dict_enum_t *hashenum, const char **key, const char **value)
const char * M_hash_dict_multi_get_direct(const M_hash_dict_t *h, const char *key, size_t idx)
const char * M_hash_dict_get_direct_default(const M_hash_dict_t *h, const char *key, const char *def)
M_bool M_hash_dict_is_multi(const M_hash_dict_t *h)
M_bool M_hash_dict_serialize_buf(const M_hash_dict_t *dict, M_buf_t *buf, char delim, char kv_delim, char quote, char escape, M_uint32 flags)
size_t M_hash_dict_num_collisions(const M_hash_dict_t *h)
struct M_hash_dict_enum M_hash_dict_enum_t
Definition: m_hash_dict.h:56
char * M_hash_dict_serialize(const M_hash_dict_t *dict, char delim, char kv_delim, char quote, char escape, M_uint32 flags)
M_hash_dict_t * M_hash_dict_create(size_t size, M_uint8 fillpct, M_uint32 flags) M_MALLOC
M_bool M_hash_dict_insert(M_hash_dict_t *h, const char *key, const char *value)
M_bool M_hash_dict_remove(M_hash_dict_t *h, const char *key)
const char * M_hash_dict_get_direct(const M_hash_dict_t *h, const char *key)
void M_hash_dict_merge(M_hash_dict_t **dest, M_hash_dict_t *src) M_FREE(2)
M_bool M_hash_dict_multi_get(const M_hash_dict_t *h, const char *key, size_t idx, const char **value)
M_hash_dict_ser_flag_t
Definition: m_hash_dict.h:353
void M_hash_dict_destroy(M_hash_dict_t *h) M_FREE(1)
M_bool M_hash_dict_multi_len(const M_hash_dict_t *h, const char *key, size_t *len)
@ M_HASH_DICT_STATIC_SEED
Definition: m_hash_dict.h:78
@ M_HASH_DICT_MULTI_SORTASC
Definition: m_hash_dict.h:73
@ M_HASH_DICT_KEYS_ORDERED
Definition: m_hash_dict.h:67
@ M_HASH_DICT_CASECMP
Definition: m_hash_dict.h:62
@ M_HASH_DICT_KEYS_SORTDESC
Definition: m_hash_dict.h:70
@ M_HASH_DICT_MULTI_GETLAST
Definition: m_hash_dict.h:75
@ M_HASH_DICT_DESER_TRIM_WHITESPACE
Definition: m_hash_dict.h:85
@ M_HASH_DICT_KEYS_SORTASC
Definition: m_hash_dict.h:69
@ M_HASH_DICT_MULTI_SORTDESC
Definition: m_hash_dict.h:74
@ M_HASH_DICT_KEYS_UPPER
Definition: m_hash_dict.h:63
@ M_HASH_DICT_MULTI_CASECMP
Definition: m_hash_dict.h:77
@ M_HASH_DICT_NONE
Definition: m_hash_dict.h:61
@ M_HASH_DICT_KEYS_LOWER
Definition: m_hash_dict.h:65
@ M_HASH_DICT_MULTI_VALUE
Definition: m_hash_dict.h:71
@ M_HASH_DICT_SER_FLAG_LF_TO_CRLF
Definition: m_hash_dict.h:358
@ M_HASH_DICT_SER_FLAG_HEXENCODE_NONPRINT
Definition: m_hash_dict.h:357
@ M_HASH_DICT_SER_FLAG_QUOTE_NON_ANS
Definition: m_hash_dict.h:356
@ M_HASH_DICT_SER_FLAG_ALWAYS_QUOTE
Definition: m_hash_dict.h:355
@ M_HASH_DICT_SER_FLAG_NONE
Definition: m_hash_dict.h:354