forked from Telodendria/Cytoplasm
Jordan Bancino
461357b526
This makes it easier to build Cytoplasm as a component of another program (for example, Telodendria), or as a standalone system library.
185 lines
7.2 KiB
C
185 lines
7.2 KiB
C
/*
|
|
* 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 <stddef.h>
|
|
|
|
#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 */
|