lib: os: add hashmap support
* Add a flexible Hashmap API * Add a Separate-Chaining Hashmap Implementation * Add a Open-Addressing Linear Probe Hashmap Implementation * Add a C-Wrapper for `std::unordered_map` for benchmarking Signed-off-by: Chris Friedt <cfriedt@meta.com>
This commit is contained in:
parent
2d4619b13c
commit
0bda7b30df
366
include/zephyr/sys/hash_map.h
Normal file
366
include/zephyr/sys/hash_map.h
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Hashmap (Hash Table) API
|
||||
*
|
||||
* Hashmaps (a.k.a Hash Tables) sacrifice space for speed. All operations
|
||||
* on a Hashmap (insert, delete, search) are O(1) complexity (on average).
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_HASH_MAP_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_HASH_MAP_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/hash_map_api.h>
|
||||
#include <zephyr/sys/hash_map_cxx.h>
|
||||
#include <zephyr/sys/hash_map_oa_lp.h>
|
||||
#include <zephyr/sys/hash_map_sc.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup hashmap_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Declare a Hashmap (advanced)
|
||||
*
|
||||
* Declare a Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _api API pointer of type @ref sys_hashmap_api.
|
||||
* @param _config_type Variant of @ref sys_hashmap_config.
|
||||
* @param _data_type Variant of @ref sys_hashmap_data.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Variant-specific details for @p _config_type.
|
||||
*/
|
||||
#define SYS_HASHMAP_DEFINE_ADVANCED(_name, _api, _config_type, _data_type, _hash_func, \
|
||||
_alloc_func, ...) \
|
||||
const struct _config_type _name##_config = __VA_ARGS__; \
|
||||
struct _data_type _name##_data; \
|
||||
struct sys_hashmap _name = { \
|
||||
.api = (const struct sys_hashmap_api *)(_api), \
|
||||
.config = (const struct sys_hashmap_config *)&_name##_config, \
|
||||
.data = (struct sys_hashmap_data *)&_name##_data, \
|
||||
.hash_func = (_hash_func), \
|
||||
.alloc_func = (_alloc_func), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Declare a Hashmap (advanced)
|
||||
*
|
||||
* Declare a Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _api API pointer of type @ref sys_hashmap_api.
|
||||
* @param _config_type Variant of @ref sys_hashmap_config.
|
||||
* @param _data_type Variant of @ref sys_hashmap_data.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Variant-specific details for @p _config_type.
|
||||
*/
|
||||
#define SYS_HASHMAP_DEFINE_STATIC_ADVANCED(_name, _api, _config_type, _data_type, _hash_func, \
|
||||
_alloc_func, ...) \
|
||||
static const struct _config_type _name##_config = __VA_ARGS__; \
|
||||
static struct _data_type _name##_data; \
|
||||
static struct sys_hashmap _name = { \
|
||||
.api = (const struct sys_hashmap_api *)(_api), \
|
||||
.config = (const struct sys_hashmap_config *)&_name##_config, \
|
||||
.data = (struct sys_hashmap_data *)&_name##_data, \
|
||||
.hash_func = (_hash_func), \
|
||||
.alloc_func = (_alloc_func), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Declare a Hashmap
|
||||
*
|
||||
* Declare a Hashmap with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_DEFINE(_name) SYS_HASHMAP_DEFAULT_DEFINE(_name)
|
||||
|
||||
/**
|
||||
* @brief Declare a Hashmap statically
|
||||
*
|
||||
* Declare a Hashmap statically with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_DEFINE_STATIC(_name) SYS_HASHMAP_DEFAULT_DEFINE_STATIC(_name)
|
||||
|
||||
/*
|
||||
* A safe wrapper for realloc(), invariant of which libc provides it.
|
||||
*/
|
||||
static inline void *sys_hashmap_default_allocator(void *ptr, size_t size)
|
||||
{
|
||||
if (size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
#define SYS_HASHMAP_DEFAULT_ALLOCATOR sys_hashmap_default_allocator
|
||||
|
||||
/** @brief The default Hashmap load factor (in hundredths) */
|
||||
#define SYS_HASHMAP_DEFAULT_LOAD_FACTOR 75
|
||||
|
||||
/** @brief Generic Hashmap */
|
||||
struct sys_hashmap {
|
||||
const struct sys_hashmap_api *api;
|
||||
const struct sys_hashmap_config *config;
|
||||
struct sys_hashmap_data *data;
|
||||
sys_hash_func32_t hash_func;
|
||||
sys_hashmap_allocator_t alloc_func;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Iterate over all values contained in a @ref sys_hashmap
|
||||
*
|
||||
* @param map Hashmap to iterate over
|
||||
* @param cb Callback to call for each entry
|
||||
* @param cookie User-specified variable
|
||||
*/
|
||||
static inline void sys_hashmap_foreach(const struct sys_hashmap *map, sys_hashmap_callback_t cb,
|
||||
void *cookie)
|
||||
{
|
||||
struct sys_hashmap_iterator it = {0};
|
||||
|
||||
for (map->api->iter(map, &it); sys_hashmap_iterator_has_next(&it);) {
|
||||
it.next(&it);
|
||||
cb(it.key, it.value, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear all entries contained in a @ref sys_hashmap
|
||||
*
|
||||
* @note If the values in a particular Hashmap are
|
||||
*
|
||||
* @param map Hashmap to clear
|
||||
* @param cb Callback to call for each entry
|
||||
* @param cookie User-specified variable
|
||||
*/
|
||||
static inline void sys_hashmap_clear(struct sys_hashmap *map, sys_hashmap_callback_t cb,
|
||||
void *cookie)
|
||||
{
|
||||
map->api->clear(map, cb, cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Insert a new entry into a @ref sys_hashmap
|
||||
*
|
||||
* Insert a new @p key - @p value pair into @p map.
|
||||
*
|
||||
* @param map Hashmap to insert into
|
||||
* @param key Key to associate with @p value
|
||||
* @param value Value to associate with @p key
|
||||
* @param old_value Location to store the value previously associated with @p key or `NULL`
|
||||
* @retval 0 if @p value was inserted for an existing key, in which case @p old_value will contain
|
||||
* the previous value
|
||||
* @retval 1 if a new entry was inserted for the @p key - @p value pair
|
||||
* @retval -ENOMEM if memory allocation failed
|
||||
* @retval -ENOSPC if the size limit has been reached
|
||||
*/
|
||||
static inline int sys_hashmap_insert(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value)
|
||||
{
|
||||
return map->api->insert(map, key, value, old_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove an entry from a @ref sys_hashmap
|
||||
*
|
||||
* Erase the entry associated with key @p key, if one exists.
|
||||
*
|
||||
* @param map Hashmap to remove from
|
||||
* @param key Key to remove from @p map
|
||||
* @param value Location to store a potential value associated with @p key or `NULL`
|
||||
*
|
||||
* @retval true if @p map was modified as a result of this operation.
|
||||
* @retval false if @p map does not contain a value associated with @p key.
|
||||
*/
|
||||
static inline bool sys_hashmap_remove(struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
return map->api->remove(map, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a value from a @ref sys_hashmap
|
||||
*
|
||||
* Look-up the @ref uint64_t associated with @p key, if one exists.
|
||||
*
|
||||
* @param map Hashmap to search through
|
||||
* @param key Key with which to search @p map
|
||||
* @param value Location to store a potential value associated with @p key or `NULL`
|
||||
*
|
||||
* @retval true if @p map contains a value associated with @p key.
|
||||
* @retval false if @p map does not contain a value associated with @p key.
|
||||
*/
|
||||
static inline bool sys_hashmap_get(const struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
return map->api->get(map, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p map contains a value associated with @p key
|
||||
*
|
||||
* @param map Hashmap to search through
|
||||
* @param key Key with which to search @p map
|
||||
*
|
||||
* @retval true if @p map contains a value associated with @p key.
|
||||
* @retval false if @p map does not contain a value associated with @p key.
|
||||
*/
|
||||
static inline bool sys_hashmap_contains_key(const struct sys_hashmap *map, uint64_t key)
|
||||
{
|
||||
return sys_hashmap_get(map, key, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Query the number of entries contained within @p map
|
||||
*
|
||||
* @param map Hashmap to search through
|
||||
*
|
||||
* @return the number of entries contained within @p map.
|
||||
*/
|
||||
static inline size_t sys_hashmap_size(const struct sys_hashmap *map)
|
||||
{
|
||||
return map->data->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if @p map is empty
|
||||
*
|
||||
* @param map Hashmap to query
|
||||
*
|
||||
* @retval true if @p map is empty.
|
||||
* @retval false if @p map is not empty.
|
||||
*/
|
||||
static inline bool sys_hashmap_is_empty(const struct sys_hashmap *map)
|
||||
{
|
||||
return map->data->size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Query the load factor of @p map
|
||||
*
|
||||
* @note To convert the load factor to a floating-point value use
|
||||
* `sys_hash_load_factor(map) / 100.0f`.
|
||||
*
|
||||
* @param map Hashmap to query
|
||||
*
|
||||
* @return Load factor of @p map expressed in hundredths.
|
||||
*/
|
||||
static inline uint8_t sys_hashmap_load_factor(const struct sys_hashmap *map)
|
||||
{
|
||||
if (map->data->n_buckets == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (map->data->size * 100) / map->data->n_buckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Query the number of buckets used in @p map
|
||||
*
|
||||
* @param map Hashmap to query
|
||||
* @return Number of buckets used in @p map
|
||||
*/
|
||||
static inline size_t sys_hashmap_num_buckets(const struct sys_hashmap *map)
|
||||
{
|
||||
return map->data->n_buckets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decide whether the Hashmap should be resized
|
||||
*
|
||||
* This is a simple opportunistic method that implementations
|
||||
* can choose to use. It will grow and shrink the Hashmap by a factor
|
||||
* of 2 when insertion / removal would exceed / fall into the specified
|
||||
* load factor.
|
||||
*
|
||||
* @note Users should call this prior to inserting a new key-value pair and after removing a
|
||||
* key-value pair.
|
||||
*
|
||||
* @note The number of reserved entries is implementation-defined, but it is only considered
|
||||
* as part of the load factor when growing the hash table.
|
||||
*
|
||||
* @param map Hashmap to examine
|
||||
* @param grow true if an entry is to be added. false if an entry has been removed
|
||||
* @param num_reserved the number of reserved entries
|
||||
* @param[out] new_num_buckets variable Hashmap size
|
||||
* @return true if the Hashmap should be rehashed
|
||||
* @return false if the Hashmap should not be rehashed
|
||||
*/
|
||||
static inline bool sys_hashmap_should_rehash(const struct sys_hashmap *map, bool grow,
|
||||
size_t num_reserved, size_t *new_num_buckets)
|
||||
{
|
||||
size_t size;
|
||||
bool should_grow;
|
||||
size_t n_buckets;
|
||||
bool should_shrink;
|
||||
const bool shrink = !grow;
|
||||
struct sys_hashmap_oa_lp_data *const data = (struct sys_hashmap_oa_lp_data *)map->data;
|
||||
const struct sys_hashmap_config *const config = map->config;
|
||||
|
||||
/* All branchless calculations, so very cache-friendly */
|
||||
|
||||
/* calculate new size */
|
||||
size = data->size;
|
||||
size += grow;
|
||||
/* maximum size imposed by the implementation */
|
||||
__ASSERT_NO_MSG(size < SIZE_MAX / 100);
|
||||
|
||||
/* calculate new number of buckets */
|
||||
n_buckets = data->n_buckets;
|
||||
/* initial number of buckets */
|
||||
n_buckets += grow * (size == 1) * config->initial_n_buckets;
|
||||
/* grow at a rate of 2x */
|
||||
n_buckets <<= grow * (size != 1);
|
||||
/* shrink at a rate of 2x */
|
||||
n_buckets >>= shrink;
|
||||
|
||||
/* shrink to zero if empty */
|
||||
n_buckets *= (size != 0);
|
||||
|
||||
__ASSERT_NO_MSG(new_num_buckets != NULL);
|
||||
__ASSERT_NO_MSG(new_num_buckets != &data->n_buckets);
|
||||
*new_num_buckets = n_buckets;
|
||||
|
||||
should_grow =
|
||||
grow && (data->n_buckets == 0 ||
|
||||
(size + num_reserved) * 100 / data->n_buckets > map->config->load_factor);
|
||||
should_shrink =
|
||||
shrink && (n_buckets == 0 || (size * 100) / n_buckets <= map->config->load_factor);
|
||||
|
||||
return should_grow || should_shrink;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_HASH_MAP_H_ */
|
242
include/zephyr/sys/hash_map_api.h
Normal file
242
include/zephyr/sys/hash_map_api.h
Normal file
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Hashmap (Hash Table) API
|
||||
*
|
||||
* Hashmaps (a.k.a Hash Tables) sacrifice space for speed. All operations
|
||||
* on a Hashmap (insert, delete, search) are O(1) complexity (on average).
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/sys/hash_function.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup hashmap_apis Hashmap
|
||||
* @ingroup datastructure_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Generic Hashmap iterator interface
|
||||
*
|
||||
* @note @a next should not be used without first checking
|
||||
* @ref sys_hashmap_iterator_has_next
|
||||
*
|
||||
* @param map Pointer to the associated Hashmap
|
||||
* @param next Modify the iterator in-place to point to the next Hashmap entry
|
||||
* @param state Implementation-specific iterator state
|
||||
* @param key Key associated with the current entry
|
||||
* @param value Value associated with the current entry
|
||||
* @param size Number of entries in the map
|
||||
* @param pos Number of entries already iterated
|
||||
*/
|
||||
struct sys_hashmap_iterator {
|
||||
const struct sys_hashmap *map;
|
||||
void (*next)(struct sys_hashmap_iterator *it);
|
||||
void *state;
|
||||
uint64_t key;
|
||||
uint64_t value;
|
||||
const size_t size;
|
||||
size_t pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check if a Hashmap iterator has a next entry
|
||||
*
|
||||
* @param it Hashmap iterator
|
||||
* @return true if there is a next entry
|
||||
* @return false if there is no next entry
|
||||
*/
|
||||
static inline bool sys_hashmap_iterator_has_next(const struct sys_hashmap_iterator *it)
|
||||
{
|
||||
return it->pos < it->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocator interface for @ref sys_hashmap
|
||||
*
|
||||
* The Hashmap allocator can be any allocator that behaves similarly to `realloc()` with the
|
||||
* additional specification that the allocator behaves like `free()` when @p new_size is zero.
|
||||
*
|
||||
* @param ptr Previously allocated memory region or `NULL` to make a new vallocation.
|
||||
* @param new_size the new size of the allocation, in bytes.
|
||||
*
|
||||
* @see <a href="https://en.cppreference.com/w/c/memory/realloc">realloc</a>
|
||||
*/
|
||||
typedef void *(*sys_hashmap_allocator_t)(void *ptr, size_t new_size);
|
||||
|
||||
/**
|
||||
* @brief In-place iterator constructor for @ref sys_hashmap
|
||||
*
|
||||
* Construct an iterator, @p it, for @p map.
|
||||
*
|
||||
* @param map Hashmap to iterate over.
|
||||
* @param it Iterator to initialize.
|
||||
*/
|
||||
typedef void (*sys_hashmap_iterator_t)(const struct sys_hashmap *map,
|
||||
struct sys_hashmap_iterator *it);
|
||||
|
||||
/**
|
||||
* @brief Callback interface for @ref sys_hashmap
|
||||
*
|
||||
* This callback is used by some Hashmap methods.
|
||||
*
|
||||
* @param key Key corresponding to @p value
|
||||
* @param value Value corresponding to @p key
|
||||
* @param cookie User-specified variable
|
||||
*/
|
||||
typedef void (*sys_hashmap_callback_t)(uint64_t key, uint64_t value, void *cookie);
|
||||
|
||||
/**
|
||||
* @brief Clear all entries contained in a @ref sys_hashmap
|
||||
*
|
||||
* @note If the values in a particular Hashmap are
|
||||
*
|
||||
* @param map Hashmap to clear
|
||||
* @param cb Callback to call for each entry
|
||||
* @param cookie User-specified variable
|
||||
*/
|
||||
typedef void (*sys_hashmap_clear_t)(struct sys_hashmap *map, sys_hashmap_callback_t cb,
|
||||
void *cookie);
|
||||
|
||||
/**
|
||||
* @brief Insert a new entry into a @ref sys_hashmap
|
||||
*
|
||||
* Insert a new @p key - @p value pair into @p map.
|
||||
*
|
||||
* @param map Hashmap to insert into
|
||||
* @param key Key to associate with @p value
|
||||
* @param value Value to associate with @p key
|
||||
* @param old_value Location to store the value previously associated with @p key or `NULL`
|
||||
* @retval 0 if @p value was inserted for an existing key, in which case @p old_value will contain
|
||||
* the previous value
|
||||
* @retval 1 if a new entry was inserted for the @p key - @p value pair
|
||||
* @retval -ENOMEM if memory allocation failed
|
||||
*/
|
||||
typedef int (*sys_hashmap_insert_t)(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value);
|
||||
|
||||
/**
|
||||
* @brief Remove an entry from a @ref sys_hashmap
|
||||
*
|
||||
* Erase the entry associated with key @p key, if one exists.
|
||||
*
|
||||
* @param map Hashmap to remove from
|
||||
* @param key Key to remove from @p map
|
||||
* @param value Location to store a potential value associated with @p key or `NULL`
|
||||
*
|
||||
* @retval true if @p map was modified as a result of this operation.
|
||||
* @retval false if @p map does not contain a value associated with @p key.
|
||||
*/
|
||||
typedef bool (*sys_hashmap_remove_t)(struct sys_hashmap *map, uint64_t key, uint64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get a value from a @ref sys_hashmap
|
||||
*
|
||||
* Look-up the @ref uint64_t associated with @p key, if one exists.
|
||||
*
|
||||
* @param map Hashmap to search through
|
||||
* @param key Key with which to search @p map
|
||||
* @param value Location to store a potential value associated with @p key or `NULL`
|
||||
*
|
||||
* @retval true if @p map contains a value associated with @p key.
|
||||
* @retval false if @p map does not contain a value associated with @p key.
|
||||
*/
|
||||
typedef bool (*sys_hashmap_get_t)(const struct sys_hashmap *map, uint64_t key, uint64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Generic Hashmap API
|
||||
*
|
||||
* @param iter Iterator constructor (in-place)
|
||||
* @param next Forward-incrementer for iterator
|
||||
* @param clear Clear the hash table, freeing all resources
|
||||
* @param insert Insert a key-value pair into the Hashmap
|
||||
* @param remove Remove a key-value pair from the Hashmap
|
||||
* @param get Retrieve a the value associated with a given key from the Hashmap
|
||||
*/
|
||||
struct sys_hashmap_api {
|
||||
sys_hashmap_iterator_t iter;
|
||||
sys_hashmap_clear_t clear;
|
||||
sys_hashmap_insert_t insert;
|
||||
sys_hashmap_remove_t remove;
|
||||
sys_hashmap_get_t get;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Generic Hashmap configuration
|
||||
*
|
||||
* When there is a known limit imposed on the number of entries in the Hashmap,
|
||||
* users should specify that via @a max_size. When the Hashmap should have
|
||||
* no artificial limitation in size (and be bounded only by the provided
|
||||
* allocator), users should specify `SIZE_MAX` here.
|
||||
*
|
||||
* The @a load_factor is defined as the size of the Hashmap divided by the
|
||||
* number of buckets. In this case, the size of the Hashmap is defined as
|
||||
* the number of valid entries plus the number of invalidated entries.
|
||||
*
|
||||
* The @a initial_n_buckets is defined as the number of buckets to allocate
|
||||
* when moving from size 0 to size 1 such that the maximum @a load_factor
|
||||
* property is preserved.
|
||||
*
|
||||
* @param max_size Maximum number of entries
|
||||
* @param load_factor Maximum load factor of expressed in hundredths
|
||||
* @param initial_n_buckets Initial number of buckets to allocate
|
||||
*/
|
||||
struct sys_hashmap_config {
|
||||
size_t max_size;
|
||||
uint8_t load_factor;
|
||||
uint8_t initial_n_buckets;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Initializer for @p sys_hashmap_config
|
||||
*
|
||||
* This macro helps to initialize a structure of type @p sys_hashmap_config.
|
||||
*
|
||||
* @param _max_size Maximum number of entries
|
||||
* @param _load_factor Maximum load factor of expressed in hundredths
|
||||
*/
|
||||
#define SYS_HASHMAP_CONFIG(_max_size, _load_factor) \
|
||||
{ \
|
||||
.max_size = (size_t)_max_size, .load_factor = (uint8_t)_load_factor, \
|
||||
.initial_n_buckets = NHPOT(ceiling_fraction(100, _load_factor)), \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generic Hashmap data
|
||||
*
|
||||
* @note When @a size is zero, @a buckets should be `NULL`.
|
||||
*
|
||||
* @param buckets Pointer for implementation-specific Hashmap storage
|
||||
* @param n_buckets The number of buckets currently allocated
|
||||
* @param size The number of entries currently in the Hashmap
|
||||
*/
|
||||
struct sys_hashmap_data {
|
||||
void *buckets;
|
||||
size_t n_buckets;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_HASHMAP_API_H_ */
|
102
include/zephyr/sys/hash_map_cxx.h
Normal file
102
include/zephyr/sys/hash_map_cxx.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief C++ Hashmap
|
||||
*
|
||||
* This is a C wrapper around `std::unordered_map`. It is mainly used for
|
||||
* benchmarking purposes.
|
||||
*
|
||||
* @note Enable with @kconfig{CONFIG_SYS_HASH_MAP_CXX}
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_HASH_MAP_CXX_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_HASH_MAP_CXX_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/sys/hash_function.h>
|
||||
#include <zephyr/sys/hash_map_api.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Declare a C++ Hashmap (advanced)
|
||||
*
|
||||
* Declare a C++ Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Variant-specific details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_CXX_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_ADVANCED(_name, &sys_hashmap_cxx_api, sys_hashmap_config, \
|
||||
sys_hashmap_data, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a C++ Hashmap (advanced)
|
||||
*
|
||||
* Declare a C++ Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_CXX_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_STATIC_ADVANCED(_name, &sys_hashmap_cxx_api, sys_hashmap_config, \
|
||||
sys_hashmap_data, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a C++ Hashmap statically
|
||||
*
|
||||
* Declare a C++ Hashmap statically with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_CXX_DEFINE_STATIC(_name) \
|
||||
SYS_HASHMAP_CXX_DEFINE_STATIC_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
/**
|
||||
* @brief Declare a C++ Hashmap
|
||||
*
|
||||
* Declare a C++ Hashmap with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_CXX_DEFINE(_name) \
|
||||
SYS_HASHMAP_CXX_DEFINE_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
#ifdef CONFIG_SYS_HASH_MAP_CHOICE_CXX
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE(_name) SYS_HASHMAP_CXX_DEFINE(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC(_name) SYS_HASHMAP_CXX_DEFINE_STATIC(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_CXX_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_CXX_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern const struct sys_hashmap_api sys_hashmap_cxx_api;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_HASH_MAP_CXX_H_ */
|
107
include/zephyr/sys/hash_map_oa_lp.h
Normal file
107
include/zephyr/sys/hash_map_oa_lp.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Open-Addressing / Linear Probe Hashmap Implementation
|
||||
*
|
||||
* @note Enable with @kconfig{CONFIG_SYS_HASH_MAP_OA_LP}
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_HASH_MAP_OA_LP_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_HASH_MAP_OA_LP_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/sys/hash_function.h>
|
||||
#include <zephyr/sys/hash_map_api.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct sys_hashmap_oa_lp_data {
|
||||
void *buckets;
|
||||
size_t n_buckets;
|
||||
size_t size;
|
||||
size_t n_tombstones;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Declare a Open Addressing Linear Probe Hashmap (advanced)
|
||||
*
|
||||
* Declare a Open Addressing Linear Probe Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Variant-specific details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_OA_LP_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_ADVANCED(_name, &sys_hashmap_oa_lp_api, sys_hashmap_config, \
|
||||
sys_hashmap_oa_lp_data, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a Open Addressing Linear Probe Hashmap (advanced)
|
||||
*
|
||||
* Declare a Open Addressing Linear Probe Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_OA_LP_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_STATIC_ADVANCED(_name, &sys_hashmap_oa_lp_api, sys_hashmap_config, \
|
||||
sys_hashmap_oa_lp_data, _hash_func, _alloc_func, \
|
||||
__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a Open Addressing Linear Probe Hashmap statically
|
||||
*
|
||||
* Declare a Open Addressing Linear Probe Hashmap statically with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_OA_LP_DEFINE_STATIC(_name) \
|
||||
SYS_HASHMAP_OA_LP_DEFINE_STATIC_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
/**
|
||||
* @brief Declare a Open Addressing Linear Probe Hashmap
|
||||
*
|
||||
* Declare a Open Addressing Linear Probe Hashmap with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_OA_LP_DEFINE(_name) \
|
||||
SYS_HASHMAP_OA_LP_DEFINE_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
#ifdef CONFIG_SYS_HASH_MAP_CHOICE_OA_LP
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE(_name) SYS_HASHMAP_OA_LP_DEFINE(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC(_name) SYS_HASHMAP_OA_LP_DEFINE_STATIC(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_OA_LP_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_OA_LP_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern const struct sys_hashmap_api sys_hashmap_oa_lp_api;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_HASH_MAP_OA_LP_H_ */
|
102
include/zephyr/sys/hash_map_sc.h
Normal file
102
include/zephyr/sys/hash_map_sc.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Separate Chaining Hashmap Implementation
|
||||
*
|
||||
* @note Enable with @kconfig{CONFIG_SYS_HASH_MAP_SC}
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_HASH_MAP_SC_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_HASH_MAP_SC_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/sys/hash_function.h>
|
||||
#include <zephyr/sys/hash_map_api.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Declare a Separate Chaining Hashmap (advanced)
|
||||
*
|
||||
* Declare a Separate Chaining Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc_func is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_SC_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_ADVANCED(_name, &sys_hashmap_sc_api, sys_hashmap_config, \
|
||||
sys_hashmap_data, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a Separate Chaining Hashmap (advanced)
|
||||
*
|
||||
* Declare a Separate Chaining Hashmap with control over advanced parameters.
|
||||
*
|
||||
* @note The allocator @p _alloc is used for allocating internal Hashmap
|
||||
* entries and does not interact with any user-provided keys or values.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
* @param _hash_func Hash function pointer of type @ref sys_hash_func32_t.
|
||||
* @param _alloc_func Allocator function pointer of type @ref sys_hashmap_allocator_t.
|
||||
* @param ... Details for @ref sys_hashmap_config.
|
||||
*/
|
||||
#define SYS_HASHMAP_SC_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_DEFINE_STATIC_ADVANCED(_name, &sys_hashmap_sc_api, sys_hashmap_config, \
|
||||
sys_hashmap_data, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Declare a Separate Chaining Hashmap statically
|
||||
*
|
||||
* Declare a Separate Chaining Hashmap statically with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_SC_DEFINE_STATIC(_name) \
|
||||
SYS_HASHMAP_SC_DEFINE_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
/**
|
||||
* @brief Declare a Separate Chaining Hashmap
|
||||
*
|
||||
* Declare a Separate Chaining Hashmap with default parameters.
|
||||
*
|
||||
* @param _name Name of the Hashmap.
|
||||
*/
|
||||
#define SYS_HASHMAP_SC_DEFINE(_name) \
|
||||
SYS_HASHMAP_SC_DEFINE_ADVANCED( \
|
||||
_name, sys_hash32, SYS_HASHMAP_DEFAULT_ALLOCATOR, \
|
||||
SYS_HASHMAP_CONFIG(SIZE_MAX, SYS_HASHMAP_DEFAULT_LOAD_FACTOR))
|
||||
|
||||
#ifdef CONFIG_SYS_HASH_MAP_CHOICE_SC
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE(_name) SYS_HASHMAP_SC_DEFINE(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC(_name) SYS_HASHMAP_SC_DEFINE_STATIC(_name)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_SC_DEFINE_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#define SYS_HASHMAP_DEFAULT_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, ...) \
|
||||
SYS_HASHMAP_SC_DEFINE_STATIC_ADVANCED(_name, _hash_func, _alloc_func, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
extern const struct sys_hashmap_api sys_hashmap_sc_api;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_HASH_MAP_SC_H_ */
|
|
@ -67,6 +67,10 @@ zephyr_sources_ifdef(CONFIG_WINSTREAM winstream.c)
|
|||
zephyr_sources_ifdef(CONFIG_SYS_HASH_FUNC32_DJB2 hash_func32_djb2.c)
|
||||
zephyr_sources_ifdef(CONFIG_SYS_HASH_FUNC32_MURMUR3 hash_func32_murmur3.c)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_SYS_HASH_MAP_SC hash_map_sc.c)
|
||||
zephyr_sources_ifdef(CONFIG_SYS_HASH_MAP_OA_LP hash_map_oa_lp.c)
|
||||
zephyr_sources_ifdef(CONFIG_SYS_HASH_MAP_CXX hash_map_cxx.cpp)
|
||||
|
||||
zephyr_library_include_directories(
|
||||
${ZEPHYR_BASE}/kernel/include
|
||||
${ZEPHYR_BASE}/arch/${ARCH}/include
|
||||
|
|
|
@ -177,5 +177,6 @@ rsource "Kconfig.cbprintf"
|
|||
rsource "Kconfig.heap"
|
||||
|
||||
rsource "Kconfig.hash_func"
|
||||
rsource "Kconfig.hash_map"
|
||||
|
||||
endmenu
|
||||
|
|
70
lib/os/Kconfig.hash_map
Normal file
70
lib/os/Kconfig.hash_map
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Copyright (c) 2022 Meta
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menu "Hashmap (Hash Table) Support"
|
||||
|
||||
config SYS_HASH_MAP
|
||||
bool "Hashmap support"
|
||||
help
|
||||
Support for Hashmaps (a.k.a. Hash Tables).
|
||||
|
||||
Hashmaps are data structures that are used when insertion, removal, and
|
||||
lookup of key-value pairs must be done in O(1) time (on average).
|
||||
|
||||
if SYS_HASH_MAP
|
||||
|
||||
config SYS_HASH_MAP_SC
|
||||
bool "Separate-Chaining Hashmap"
|
||||
help
|
||||
Separate-Chaining Hashmaps implement each bucket as a linked-list.
|
||||
|
||||
They are perhaps more useful on resource-constrained systems where
|
||||
large contiguous memory regions are scarce.
|
||||
|
||||
The main disadvantage of Separate-Chaining Hashmaps are that their
|
||||
use tends to incur more cache-misses as nodes are spread throughout
|
||||
the heap.
|
||||
|
||||
config SYS_HASH_MAP_OA_LP
|
||||
bool "Open-Addressing / Linear Probe Hashmap"
|
||||
help
|
||||
Open-Addressing Hashmaps do not chain entries together but instead
|
||||
store all entries in the table itself which is a contiguously allocated
|
||||
memory region.
|
||||
|
||||
The main advantage of Open-Addressing Hashmaps are due to their
|
||||
contiguous allocation which improves performance on systems with
|
||||
memory caching.
|
||||
|
||||
config SYS_HASH_MAP_CXX
|
||||
bool "C++ Hashmap"
|
||||
select CPLUSPLUS
|
||||
select LIB_CPLUSPLUS
|
||||
select EXCEPTIONS
|
||||
help
|
||||
This enables a C wrapper around the C++ std::unordered_map.
|
||||
|
||||
It is mainly used for benchmarking purposes.
|
||||
|
||||
choice SYS_HASH_MAP_CHOICE
|
||||
prompt "Default hashmap implementation"
|
||||
default SYS_HASH_MAP_CHOICE_SC
|
||||
|
||||
config SYS_HASH_MAP_CHOICE_SC
|
||||
bool "Default hash is Separate-Chaining"
|
||||
select SYS_HASH_MAP_SC
|
||||
|
||||
config SYS_HASH_MAP_CHOICE_OA_LP
|
||||
bool "Default hash is Open-Addressing / Linear Probe"
|
||||
select SYS_HASH_MAP_OA_LP
|
||||
|
||||
config SYS_HASH_MAP_CHOICE_CXX
|
||||
bool "Default hash is C++"
|
||||
select SYS_HASH_MAP_CXX
|
||||
|
||||
endchoice # SYS_HASH_MAP_CHOICE
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
157
lib/os/hash_map_cxx.cpp
Normal file
157
lib/os/hash_map_cxx.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <zephyr/sys/hash_map.h>
|
||||
#include <zephyr/sys/hash_map_cxx.h>
|
||||
|
||||
using cxx_map = std::unordered_map<uint64_t, uint64_t>;
|
||||
|
||||
static void sys_hashmap_cxx_iter_next(struct sys_hashmap_iterator *it)
|
||||
{
|
||||
cxx_map *umap = static_cast<cxx_map *>(it->map->data->buckets);
|
||||
|
||||
__ASSERT_NO_MSG(umap != nullptr);
|
||||
|
||||
__ASSERT(it->size == it->map->data->size, "Concurrent modification!");
|
||||
__ASSERT(sys_hashmap_iterator_has_next(it), "Attempt to access beyond current bound!");
|
||||
|
||||
auto it2 = umap->begin();
|
||||
for (size_t i = 0; i < it->pos; ++i, it2++)
|
||||
;
|
||||
|
||||
it->key = it2->first;
|
||||
it->value = it2->second;
|
||||
++it->pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* C++ Wrapped Hashmap API
|
||||
*/
|
||||
|
||||
static void sys_hashmap_cxx_iter(const struct sys_hashmap *map, struct sys_hashmap_iterator *it)
|
||||
{
|
||||
it->map = map;
|
||||
it->next = sys_hashmap_cxx_iter_next;
|
||||
it->state = nullptr;
|
||||
it->key = 0;
|
||||
it->value = 0;
|
||||
it->pos = 0;
|
||||
*((size_t *)&it->size) = map->data->size;
|
||||
}
|
||||
|
||||
static void sys_hashmap_cxx_clear(struct sys_hashmap *map, sys_hashmap_callback_t cb, void *cookie)
|
||||
{
|
||||
cxx_map *umap = static_cast<cxx_map *>(map->data->buckets);
|
||||
|
||||
if (umap == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cb != nullptr) {
|
||||
for (auto &kv : *umap) {
|
||||
cb(kv.first, kv.second, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
delete umap;
|
||||
|
||||
map->data->buckets = nullptr;
|
||||
map->data->n_buckets = 0;
|
||||
map->data->size = 0;
|
||||
}
|
||||
|
||||
static int sys_hashmap_cxx_insert(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value)
|
||||
{
|
||||
cxx_map *umap = static_cast<cxx_map *>(map->data->buckets);
|
||||
|
||||
if (umap == nullptr) {
|
||||
umap = new cxx_map;
|
||||
umap->max_load_factor(map->config->load_factor / 100.0f);
|
||||
map->data->buckets = umap;
|
||||
}
|
||||
|
||||
auto it = umap->find(key);
|
||||
if (it != umap->end() && old_value != nullptr) {
|
||||
*old_value = it->second;
|
||||
it->second = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
(*umap)[key] = value;
|
||||
} catch(...) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
++map->data->size;
|
||||
map->data->n_buckets = umap->bucket_count();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool sys_hashmap_cxx_remove(struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
cxx_map *umap = static_cast<cxx_map *>(map->data->buckets);
|
||||
|
||||
if (umap == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = umap->find(key);
|
||||
if (it == umap->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != nullptr) {
|
||||
*value = it->second;
|
||||
}
|
||||
|
||||
umap->erase(key);
|
||||
--map->data->size;
|
||||
map->data->n_buckets = umap->bucket_count();
|
||||
|
||||
if (map->data->size == 0) {
|
||||
delete umap;
|
||||
map->data->n_buckets = 0;
|
||||
map->data->buckets = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sys_hashmap_cxx_get(const struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
cxx_map *umap = static_cast<cxx_map *>(map->data->buckets);
|
||||
|
||||
if (umap == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = umap->find(key);
|
||||
if (it == umap->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != nullptr) {
|
||||
*value = it->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
const struct sys_hashmap_api sys_hashmap_cxx_api = {
|
||||
.iter = sys_hashmap_cxx_iter,
|
||||
.clear = sys_hashmap_cxx_clear,
|
||||
.insert = sys_hashmap_cxx_insert,
|
||||
.remove = sys_hashmap_cxx_remove,
|
||||
.get = sys_hashmap_cxx_get,
|
||||
};
|
||||
}
|
293
lib/os/hash_map_oa_lp.c
Normal file
293
lib/os/hash_map_oa_lp.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/sys/hash_map.h>
|
||||
#include <zephyr/sys/hash_map_oa_lp.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
enum bucket_state {
|
||||
UNUSED,
|
||||
USED,
|
||||
TOMBSTONE,
|
||||
};
|
||||
|
||||
struct oalp_entry {
|
||||
uint64_t key;
|
||||
uint64_t value;
|
||||
enum bucket_state state;
|
||||
};
|
||||
|
||||
BUILD_ASSERT(offsetof(struct sys_hashmap_oa_lp_data, buckets) ==
|
||||
offsetof(struct sys_hashmap_data, buckets));
|
||||
BUILD_ASSERT(offsetof(struct sys_hashmap_oa_lp_data, n_buckets) ==
|
||||
offsetof(struct sys_hashmap_data, n_buckets));
|
||||
BUILD_ASSERT(offsetof(struct sys_hashmap_oa_lp_data, size) ==
|
||||
offsetof(struct sys_hashmap_data, size));
|
||||
|
||||
static struct oalp_entry *sys_hashmap_oa_lp_find(const struct sys_hashmap *map, uint64_t key,
|
||||
bool used_ok, bool unused_ok, bool tombstone_ok)
|
||||
{
|
||||
struct oalp_entry *entry = NULL;
|
||||
const size_t n_buckets = map->data->n_buckets;
|
||||
uint32_t hash = map->hash_func(&key, sizeof(key));
|
||||
struct oalp_entry *const buckets = map->data->buckets;
|
||||
|
||||
for (size_t i = 0, j = hash; i < n_buckets; ++i, ++j) {
|
||||
j &= (n_buckets - 1);
|
||||
__ASSERT_NO_MSG(j < n_buckets);
|
||||
|
||||
entry = &buckets[j];
|
||||
|
||||
switch (entry->state) {
|
||||
case USED:
|
||||
if (used_ok && entry->key == key) {
|
||||
return entry;
|
||||
}
|
||||
break;
|
||||
case UNUSED:
|
||||
if (unused_ok) {
|
||||
return entry;
|
||||
}
|
||||
break;
|
||||
case TOMBSTONE:
|
||||
if (tombstone_ok) {
|
||||
return entry;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__ASSERT(false, "Invalid entry state. Memory has been corrupted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sys_hashmap_oa_lp_insert_no_rehash(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value)
|
||||
{
|
||||
int ret;
|
||||
struct oalp_entry *entry = NULL;
|
||||
struct sys_hashmap_oa_lp_data *data = (struct sys_hashmap_oa_lp_data *)map->data;
|
||||
|
||||
entry = sys_hashmap_oa_lp_find(map, key, true, true, true);
|
||||
__ASSERT_NO_MSG(entry != NULL);
|
||||
|
||||
switch (entry->state) {
|
||||
case UNUSED:
|
||||
++data->size;
|
||||
ret = 1;
|
||||
break;
|
||||
case TOMBSTONE:
|
||||
--data->n_tombstones;
|
||||
++data->size;
|
||||
ret = 0;
|
||||
break;
|
||||
case USED:
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (old_value != NULL) {
|
||||
*old_value = entry->value;
|
||||
}
|
||||
|
||||
entry->state = USED;
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sys_hashmap_oa_lp_rehash(struct sys_hashmap *map, bool grow)
|
||||
{
|
||||
size_t old_size;
|
||||
size_t old_n_buckets;
|
||||
size_t new_n_buckets = 0;
|
||||
struct oalp_entry *entry;
|
||||
struct oalp_entry *old_buckets;
|
||||
struct oalp_entry *new_buckets;
|
||||
struct sys_hashmap_oa_lp_data *data = (struct sys_hashmap_oa_lp_data *)map->data;
|
||||
|
||||
if (!sys_hashmap_should_rehash(map, grow, data->n_tombstones, &new_n_buckets)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (map->data->size != SIZE_MAX && map->data->size == map->config->max_size) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
/* extract all entries from the hashmap */
|
||||
old_size = data->size;
|
||||
old_n_buckets = data->n_buckets;
|
||||
old_buckets = (struct oalp_entry *)data->buckets;
|
||||
|
||||
new_buckets = (struct oalp_entry *)map->alloc_func(NULL, new_n_buckets * sizeof(*entry));
|
||||
if (new_buckets == NULL && new_n_buckets != 0) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (new_buckets != NULL) {
|
||||
/* ensure all buckets are empty / initialized */
|
||||
memset(new_buckets, 0, new_n_buckets * sizeof(*new_buckets));
|
||||
}
|
||||
|
||||
data->size = 0;
|
||||
data->buckets = new_buckets;
|
||||
data->n_buckets = new_n_buckets;
|
||||
|
||||
/* re-insert all entries into the hashmap */
|
||||
for (size_t i = 0, j = 0; i < old_n_buckets && j < old_size; ++i) {
|
||||
entry = &old_buckets[i];
|
||||
|
||||
if (entry->state == USED) {
|
||||
sys_hashmap_oa_lp_insert_no_rehash(map, entry->key, entry->value, NULL);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
/* free the old Hashmap */
|
||||
map->alloc_func(old_buckets, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sys_hashmap_oa_lp_iter_next(struct sys_hashmap_iterator *it)
|
||||
{
|
||||
size_t i;
|
||||
struct oalp_entry *entry;
|
||||
const struct sys_hashmap *map = (const struct sys_hashmap *)it->map;
|
||||
struct oalp_entry *buckets = map->data->buckets;
|
||||
|
||||
__ASSERT(it->size == map->data->size, "Concurrent modification!");
|
||||
__ASSERT(sys_hashmap_iterator_has_next(it), "Attempt to access beyond current bound!");
|
||||
|
||||
if (it->pos == 0) {
|
||||
it->state = buckets;
|
||||
}
|
||||
|
||||
i = (struct oalp_entry *)it->state - buckets;
|
||||
__ASSERT(i < map->data->n_buckets, "Invalid iterator state %p", it->state);
|
||||
|
||||
for (; i < map->data->n_buckets; ++i) {
|
||||
entry = &buckets[i];
|
||||
if (entry->state == USED) {
|
||||
it->state = &buckets[i + 1];
|
||||
it->key = entry->key;
|
||||
it->value = entry->value;
|
||||
++it->pos;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__ASSERT(false, "Entire Hashmap traversed and no entry was found");
|
||||
}
|
||||
|
||||
/*
|
||||
* Open Addressing / Linear Probe Hashmap API
|
||||
*/
|
||||
|
||||
static void sys_hashmap_oa_lp_iter(const struct sys_hashmap *map, struct sys_hashmap_iterator *it)
|
||||
{
|
||||
it->map = map;
|
||||
it->next = sys_hashmap_oa_lp_iter_next;
|
||||
it->pos = 0;
|
||||
*((size_t *)&it->size) = map->data->size;
|
||||
}
|
||||
|
||||
static void sys_hashmap_oa_lp_clear(struct sys_hashmap *map, sys_hashmap_callback_t cb,
|
||||
void *cookie)
|
||||
{
|
||||
struct oalp_entry *entry;
|
||||
struct sys_hashmap_oa_lp_data *data = (struct sys_hashmap_oa_lp_data *)map->data;
|
||||
struct oalp_entry *buckets = data->buckets;
|
||||
|
||||
for (size_t i = 0, j = 0; cb != NULL && i < data->n_buckets && j < data->size; ++i) {
|
||||
entry = &buckets[i];
|
||||
if (entry->state == USED) {
|
||||
cb(entry->key, entry->value, cookie);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->buckets != NULL) {
|
||||
map->alloc_func(data->buckets, 0);
|
||||
data->buckets = NULL;
|
||||
}
|
||||
|
||||
data->n_buckets = 0;
|
||||
data->size = 0;
|
||||
data->n_tombstones = 0;
|
||||
}
|
||||
|
||||
static inline int sys_hashmap_oa_lp_insert(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sys_hashmap_oa_lp_rehash(map, true);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sys_hashmap_oa_lp_insert_no_rehash(map, key, value, old_value);
|
||||
}
|
||||
|
||||
static bool sys_hashmap_oa_lp_remove(struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
struct oalp_entry *entry;
|
||||
struct sys_hashmap_oa_lp_data *data = (struct sys_hashmap_oa_lp_data *)map->data;
|
||||
|
||||
entry = sys_hashmap_oa_lp_find(map, key, true, true, false);
|
||||
if (entry == NULL || entry->state == UNUSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
*value = entry->value;
|
||||
}
|
||||
|
||||
entry->state = TOMBSTONE;
|
||||
--data->size;
|
||||
++data->n_tombstones;
|
||||
|
||||
/* ignore a possible -ENOMEM since the table will remain intact */
|
||||
(void)sys_hashmap_oa_lp_rehash(map, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sys_hashmap_oa_lp_get(const struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
struct oalp_entry *entry;
|
||||
|
||||
entry = sys_hashmap_oa_lp_find(map, key, true, true, false);
|
||||
if (entry == NULL || entry->state == UNUSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
*value = entry->value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct sys_hashmap_api sys_hashmap_oa_lp_api = {
|
||||
.iter = sys_hashmap_oa_lp_iter,
|
||||
.clear = sys_hashmap_oa_lp_clear,
|
||||
.insert = sys_hashmap_oa_lp_insert,
|
||||
.remove = sys_hashmap_oa_lp_remove,
|
||||
.get = sys_hashmap_oa_lp_get,
|
||||
};
|
302
lib/os/hash_map_sc.c
Normal file
302
lib/os/hash_map_sc.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/sys/dlist.h>
|
||||
#include <zephyr/sys/hash_map.h>
|
||||
#include <zephyr/sys/hash_map_sc.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
struct sys_hashmap_sc_entry {
|
||||
uint64_t key;
|
||||
uint64_t value;
|
||||
sys_dnode_t node;
|
||||
};
|
||||
|
||||
static void sys_hashmap_sc_entry_init(struct sys_hashmap_sc_entry *entry, uint64_t key,
|
||||
uint64_t value)
|
||||
{
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
sys_dnode_init(&entry->node);
|
||||
}
|
||||
|
||||
static void sys_hashmap_sc_insert_entry(struct sys_hashmap *map, struct sys_hashmap_sc_entry *entry)
|
||||
{
|
||||
sys_dlist_t *buckets = map->data->buckets;
|
||||
uint32_t hash = map->hash_func(&entry->key, sizeof(entry->key));
|
||||
|
||||
sys_dlist_append(&buckets[hash % map->data->n_buckets], &entry->node);
|
||||
++map->data->size;
|
||||
}
|
||||
|
||||
static void sys_hashmap_sc_insert_all(struct sys_hashmap *map, sys_dlist_t *list)
|
||||
{
|
||||
__unused int ret;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
while (!sys_dlist_is_empty(list)) {
|
||||
entry = CONTAINER_OF(sys_dlist_get(list), struct sys_hashmap_sc_entry, node);
|
||||
sys_hashmap_sc_insert_entry(map, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void sys_hashmap_sc_to_list(struct sys_hashmap *map, sys_dlist_t *list)
|
||||
{
|
||||
sys_dlist_t *bucket;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
sys_dlist_t *buckets = map->data->buckets;
|
||||
|
||||
sys_dlist_init(list);
|
||||
|
||||
for (size_t i = 0; i < map->data->n_buckets; ++i) {
|
||||
bucket = &buckets[i];
|
||||
while (!sys_dlist_is_empty(bucket)) {
|
||||
entry = CONTAINER_OF(sys_dlist_get(bucket), struct sys_hashmap_sc_entry,
|
||||
node);
|
||||
sys_dlist_append(list, &entry->node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_hashmap_sc_rehash(struct sys_hashmap *map, bool grow)
|
||||
{
|
||||
sys_dlist_t list;
|
||||
sys_dlist_t *bucket;
|
||||
size_t new_n_buckets;
|
||||
sys_dlist_t *new_buckets;
|
||||
|
||||
if (!sys_hashmap_should_rehash(map, grow, 0, &new_n_buckets)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* extract all entries from the hashmap */
|
||||
sys_hashmap_sc_to_list(map, &list);
|
||||
|
||||
/* reallocate memory */
|
||||
new_buckets = (sys_dlist_t *)map->alloc_func(map->data->buckets,
|
||||
new_n_buckets * sizeof(*new_buckets));
|
||||
if (new_buckets == NULL && new_n_buckets != 0) {
|
||||
/* re-insert all entries into the hashmap if reallocation fails */
|
||||
sys_hashmap_sc_insert_all(map, &list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* ensure all buckets are empty / initialized */
|
||||
map->data->size = 0;
|
||||
map->data->buckets = new_buckets;
|
||||
map->data->n_buckets = new_n_buckets;
|
||||
for (size_t i = 0; i < new_n_buckets; ++i) {
|
||||
bucket = &((sys_dlist_t *)(map->data->buckets))[i];
|
||||
sys_dlist_init(bucket);
|
||||
}
|
||||
|
||||
/* re-insert all entries into the hashmap */
|
||||
sys_hashmap_sc_insert_all(map, &list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sys_hashmap_sc_entry *sys_hashmap_sc_find(const struct sys_hashmap *map, uint64_t key)
|
||||
{
|
||||
uint32_t hash;
|
||||
sys_dlist_t *bucket;
|
||||
sys_dlist_t *buckets;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
if (map->data->n_buckets == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(map->data->size > 0);
|
||||
|
||||
hash = map->hash_func(&key, sizeof(key));
|
||||
buckets = (sys_dlist_t *)map->data->buckets;
|
||||
bucket = &buckets[hash % map->data->n_buckets];
|
||||
|
||||
SYS_DLIST_FOR_EACH_CONTAINER(bucket, entry, node) {
|
||||
if (entry->key == key) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sys_hashmap_sc_iter_next(struct sys_hashmap_iterator *it)
|
||||
{
|
||||
sys_dlist_t *bucket;
|
||||
bool found_previous_key = false;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
const struct sys_hashmap *map = it->map;
|
||||
sys_dlist_t *buckets = map->data->buckets;
|
||||
|
||||
__ASSERT(it->size == map->data->size, "Concurrent modification!");
|
||||
__ASSERT(sys_hashmap_iterator_has_next(it), "Attempt to access beyond current bound!");
|
||||
|
||||
if (it->pos == 0) {
|
||||
/* at position 0, state equals the beginning of the bucket array */
|
||||
it->state = buckets;
|
||||
found_previous_key = true;
|
||||
}
|
||||
|
||||
for (bucket = it->state; bucket < &buckets[map->data->n_buckets]; ++bucket) {
|
||||
SYS_DLIST_FOR_EACH_CONTAINER(bucket, entry, node) {
|
||||
if (!found_previous_key) {
|
||||
if (entry->key == it->key) {
|
||||
found_previous_key = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* save the bucket to state so we can restart scanning from a saved position
|
||||
*/
|
||||
it->state = bucket;
|
||||
it->key = entry->key;
|
||||
it->value = entry->value;
|
||||
++it->pos;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__ASSERT(false, "Entire Hashmap traversed and no entry was found");
|
||||
}
|
||||
|
||||
/*
|
||||
* Separate Chaining Hashmap API
|
||||
*/
|
||||
|
||||
static void sys_hashmap_sc_iter(const struct sys_hashmap *map, struct sys_hashmap_iterator *it)
|
||||
{
|
||||
it->map = map;
|
||||
it->next = sys_hashmap_sc_iter_next;
|
||||
it->state = map->data->buckets;
|
||||
it->key = 0;
|
||||
it->value = 0;
|
||||
it->pos = 0;
|
||||
*((size_t *)&it->size) = map->data->size;
|
||||
}
|
||||
|
||||
static void sys_hashmap_sc_clear(struct sys_hashmap *map, sys_hashmap_callback_t cb, void *cookie)
|
||||
{
|
||||
sys_dlist_t list;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
sys_hashmap_sc_to_list(map, &list);
|
||||
|
||||
/* free the buckets */
|
||||
if (map->data->buckets != NULL) {
|
||||
map->alloc_func(map->data->buckets, 0);
|
||||
map->data->buckets = NULL;
|
||||
}
|
||||
|
||||
map->data->n_buckets = 0;
|
||||
map->data->size = 0;
|
||||
|
||||
while (!sys_dlist_is_empty(&list)) {
|
||||
entry = CONTAINER_OF(sys_dlist_get(&list), struct sys_hashmap_sc_entry, node);
|
||||
|
||||
/* call the callback for entry */
|
||||
if (cb != NULL) {
|
||||
cb(entry->key, entry->value, cookie);
|
||||
}
|
||||
|
||||
/* free the entry using the Hashmap's allocator */
|
||||
map->alloc_func(entry, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_hashmap_sc_insert(struct sys_hashmap *map, uint64_t key, uint64_t value,
|
||||
uint64_t *old_value)
|
||||
{
|
||||
int ret;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
entry = sys_hashmap_sc_find(map, key);
|
||||
if (entry != NULL) {
|
||||
if (old_value != NULL) {
|
||||
*old_value = entry->value;
|
||||
}
|
||||
|
||||
entry->value = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = sys_hashmap_sc_rehash(map, true);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
entry = map->alloc_func(NULL, sizeof(*entry));
|
||||
if (entry == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sys_hashmap_sc_entry_init(entry, key, value);
|
||||
sys_hashmap_sc_insert_entry(map, entry);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool sys_hashmap_sc_remove(struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
__unused int ret;
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
entry = sys_hashmap_sc_find(map, key);
|
||||
if (entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
*value = entry->value;
|
||||
}
|
||||
|
||||
sys_dlist_remove(&entry->node);
|
||||
--map->data->size;
|
||||
|
||||
ret = sys_hashmap_sc_rehash(map, false);
|
||||
/* Realloc to a smaller size of memory should *always* work */
|
||||
__ASSERT_NO_MSG(ret >= 0);
|
||||
|
||||
/* free the entry */
|
||||
map->alloc_func(entry, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sys_hashmap_sc_get(const struct sys_hashmap *map, uint64_t key, uint64_t *value)
|
||||
{
|
||||
struct sys_hashmap_sc_entry *entry;
|
||||
|
||||
entry = sys_hashmap_sc_find(map, key);
|
||||
if (entry == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
*value = entry->value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct sys_hashmap_api sys_hashmap_sc_api = {
|
||||
.iter = sys_hashmap_sc_iter,
|
||||
.clear = sys_hashmap_sc_clear,
|
||||
.insert = sys_hashmap_sc_insert,
|
||||
.remove = sys_hashmap_sc_remove,
|
||||
.get = sys_hashmap_sc_get,
|
||||
};
|
Loading…
Reference in a new issue