/* * Copyright (C) 2022-2024 Jordan Bancino <@jordan:bancino.net> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef CYTOPLASM_HASHMAP_H #define CYTOPLASM_HASHMAP_H /*** * @Nm HashMap * @Nd A simple hash map implementation. * @Dd October 11 2022 * @Xr Array Queue * * This is the public interface for Cytoplasm's hash map * implementation. This hash map is designed to be simple, * well-documented, and generally readable and understandable, yet also * performant enough to be useful, because it is used extensively * throughout the project. * .Pp * Fundamentally, this is an entirely generic map implementation. It * can be used for many general purposes, but it is designed only to * implement the features Cytoplasm needs to be functional. One * example of a Cytoplasm-specific feature is that keys cannot be * arbitrary data; they are NULL-terminated C strings. */ #include #include "Array.h" /** * These functions operate on an opaque structure, which the caller * has no knowledge about. */ typedef struct HashMap HashMap; /** * Create a new hash map that is ready to be used with the rest of the * functions defined here. */ extern HashMap * HashMapCreate(void); /** * Free the specified hash map such that it becomes invalid and any * future use results in undefined behavior. Note that this function * does not free the values stored in the hash map, but since it stores * the keys internally, it will free the keys. You should use * .Fn HashMapIterate * to free the values stored in this map appropriately before calling * this function. */ extern void HashMapFree(HashMap *); /** * Control the maximum load of the hash map before it is expanded. * When the hash map reaches the given capacity, it is grown. You * don't want to only grow hash maps when they are full, because that * makes them perform very poorly. The maximum load value is a * percentage of how full the hash map is, and it should be between * 0 and 1, where 0 means that no elements will cause the map to be * expanded, and 1 means that the hash map must be completely full * before it is expanded. The default maximum load on a new hash map * is 0.75, which should be good enough for most purposes, however, * this function exists specifically so that the maximum load can be * fine-tuned. */ extern void HashMapMaxLoadSet(HashMap *, float); /** * Use a custom hashing function with the given hash map. New hash * maps have a sane hashing function that should work okay for most * use cases, but if you have a better hashing function, it can be * specified this way. Do not change the hash function after keys have * been added; doing so results in undefined behavior. Only set a new * hash function immediately after constructing a new hash map, before * anything has been added to it. * .Pp * The hash function takes a pointer to a C string, and is expected * to return a fairly unique numerical hash value which will be * converted into an array index. */ extern void HashMapFunctionSet(HashMap *, unsigned long (*) (const char *)); /** * Set the given string key to the given value. Note that the key is * copied into the hash map's own memory space, but the value is not. * It is the caller's job to ensure that the value pointer remains * valid for the life of the hash map, and are freed when no longer * needed. */ extern void * HashMapSet(HashMap *, char *, void *); /** * Retrieve the value for the given key, or return NULL if no such * key exists in the hash map. */ extern void * HashMapGet(HashMap *, const char *); /** * Remove a value specified by the given key from the hash map, and * return it to the caller to deal with. This function returns NULL * if no such key exists. */ extern void * HashMapDelete(HashMap *, const char *); /** * Iterate over all the keys and values of a hash map. This function * works very similarly to * .Xr getopt 3 , * where calls are repeatedly made in a while loop until there are no * more items to go over. The difference is that this function does not * rely on globals; it takes pointer pointers, and stores all * necessary state inside the hash map itself. * .Pp * Note that this function is not thread-safe; two threads cannot be * iterating over any given hash map at the same time, though they * can each be iterating over different hash maps. * .Pp * This function can be tricky to use in some scenarios, as it * continues where it left off on each call, until there are no more * elements to go through in the hash map. If you are not iterating * over the entire map in one go, and happen to break the loop, then * the next time you attempt to iterate the hash map, you'll start * somewhere in the middle, which is most likely not the intended * behavior. Thus, it is always recommended to iterate over the entire * hash map if you're going to use this function. * .Pp * Also note that the behavior of this function is undefined if * insertions or deletions occur during the iteration. This * functionality has not been tested, and will likely not work. */ extern bool HashMapIterate(HashMap *, char **, void **); /** * A reentrant version of * .Fn HashMapIterate * that allows the caller to overcome the flaws of that function by * storing the cursor outside of the hash map structure itself. This * allows multiple threads to iterate over the same hash map at the * same time, and it allows the iteration to be halted midway through * without causing any unintended side effects. * .Pp * The cursor should be initialized to 0 at the start of iteration. */ extern bool HashMapIterateReentrant(HashMap *, char **, void **, size_t *); /** * Collect the string keys of a hash map and return them as an array. * The returned array holds pointers to the strings stored in the * hash map, so the strings should NOT be freed; it is sufficient to * free the array itself. Likewise, once the hash map is freed, the * array elements are invalid and the array should be freed. */ extern Array * HashMapKeys(HashMap *); /** * Collect the values of a hash map and return them as an array. The * returned array holds the same pointers to the values as the hash * map. */ extern Array * HashMapValues(HashMap *); #endif /* CYTOPLASM_HASHMAP_H */