blob: c96eca1d7f2d309ec47f419d68e00395ef19a1bc [file] [log] [blame]
/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: qdf_ptr_hash.h
*
* A minimal hashtable implementation for doing fast lookups via pointer.
*
* qdf_ptr_hash also has the benefit of knowing its own size, allowing a pointer
* to the hashtable to be passed around and embedded in other structs. Since
* every hashtable is not necessarily of the same size, this allows using hash
* tables in a lot of new places which would be impossible with the current
* kernel hashtable implementation.
*
* Because the size of the hashtable varies with the number of bits used in the
* hash, declaring a qdf_ptr_hash is a bit different. If you want to embed a
* qdf_ptr_hash in another type, use a combination of qdf_ptr_hash_declare() and
* qdf_ptr_hash_ptr(). If you just want to declare and use a qdf_ptr_hash, use
* qdf_ptr_hash_declare_ptr() instead. Either method will ensure the appropriate
* number of bytes is accounted for using an internal union, and provides the
* consumer with a pointer to a qdf_ptr_hash type which can be used with all of
* the other qdf_ptr_hash APIs. Alternatively, you can skip these complexities
* by simply dynamically allocating the qdf_ptr_hash via qdf_ptr_hash_create().
*/
#ifndef __QDF_PTR_HASH_H
#define __QDF_PTR_HASH_H
#include "i_qdf_ptr_hash.h"
#include "qdf_mem.h"
#include "qdf_slist.h"
#include "qdf_types.h"
#include "qdf_util.h"
/**
* struct qdf_ptr_hash_bucket - a type representing a hash bucket
* @list: the list used for hash chaining
*/
struct qdf_ptr_hash_bucket {
struct qdf_slist list;
};
/**
* struct qdf_ptr_hash - a hash table type for doing fast lookups via pointer
* @bits: the number of bits to use when hashing keys
* @count: the number of buckets, always equal to 2^@bits
* @buckets: empty bucket array for accessing a variable length array of buckets
*/
struct qdf_ptr_hash {
int8_t bits;
int16_t count;
struct qdf_ptr_hash_bucket buckets[0];
};
/**
* struct qdf_ptr_hash_entry - entry type of membership in a qdf_ptr_hash
* @key: the value used as the key for insertion/lookup
* @node: the list node used for hash chaining
*/
struct qdf_ptr_hash_entry {
uintptr_t key;
struct qdf_slist_node node;
};
#define __qdf_ptr_hash_size(bits) (sizeof(struct qdf_ptr_hash) + \
sizeof(((struct qdf_ptr_hash *)0)->buckets[0]) * (1 << bits))
/**
* qdf_ptr_hash_declare() - declare a new qdf_ptr_hash
* @name: the C identifier to use for the new hash table
* @bits: The number of bits to use for hashing
*
* Return: None
*/
#define qdf_ptr_hash_declare(name, _bits) \
union { \
struct qdf_ptr_hash ht; \
uint8_t __raw[__qdf_ptr_hash_size(_bits)]; \
} __##name = { .ht = { .bits = _bits, .count = (1 << _bits) } }
/**
* qdf_ptr_hash_ptr() - get a pointer to a declared qdf_ptr_hash
* @name: the C identifier of the declared qdf_ptr_hash
*
* Return: pointer to a qdf_ptr_hash
*/
#define qdf_ptr_hash_ptr(name) &__##name.ht
/**
* qdf_ptr_hash_declare_ptr() - declare a pointer to a new qdf_ptr_hash
* @name: the C identifier to use for the pointer to the new qdf_ptr_hash
* @bits: The number of bits to use for hashing
*
* Return: None
*/
#define qdf_ptr_hash_declare_ptr(name, bits) \
qdf_ptr_hash_declare(name, bits); \
struct qdf_ptr_hash *name = qdf_ptr_hash_ptr(name)
#define __qdf_ptr_hash_for_each_bucket(ht, bkt) \
for ((bkt) = (ht)->buckets; \
(bkt) < (ht)->buckets + (ht)->count; \
(bkt)++)
/**
* qdf_ptr_hash_init() - initialize a qdf_ptr_hash
* @ht: the hash table to initialize
*
* Return: None
*/
static inline void qdf_ptr_hash_init(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
qdf_slist_init(&bucket->list);
}
/**
* qdf_ptr_hash_deinit() - de-initialize a qdf_ptr_hash
* @ht: the hash table to de-initialize
*
* Return: None
*/
static inline void qdf_ptr_hash_deinit(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
qdf_slist_deinit(&bucket->list);
}
/**
* qdf_ptr_hash_create() - allocate and initialize a qdf_ptr_hash
* @bits: the number of bits to use for hashing
*
* Return: qdf_ptr_hash pointer on succes, NULL on allocation failure
*/
static inline struct qdf_ptr_hash *qdf_ptr_hash_create(uint8_t bits)
{
struct qdf_ptr_hash *ht = qdf_mem_malloc(__qdf_ptr_hash_size(bits));
if (!ht)
return NULL;
ht->bits = bits;
ht->count = 1 << bits;
qdf_ptr_hash_init(ht);
return ht;
}
/**
* qdf_ptr_hash_destroy() - de-initialize and de-allocate a qdf_ptr_hash
* @ht: the qdf_ptr_hash to destroy
*
* Return: None
*/
static inline void qdf_ptr_hash_destroy(struct qdf_ptr_hash *ht)
{
qdf_ptr_hash_deinit(ht);
qdf_mem_free(ht);
}
/**
* qdf_ptr_hash_empty() - check if a qdf_ptr_hash has any entries
* @ht: the qdf_ptr_hash to check
*
* Return: true if @ht contains no entries
*/
static inline bool qdf_ptr_hash_empty(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
if (!qdf_slist_empty(&bucket->list))
return false;
return true;
}
static inline struct qdf_ptr_hash_bucket *
__qdf_ptr_hash_get_bucket(struct qdf_ptr_hash *ht, uintptr_t key)
{
return ht->buckets + __qdf_ptr_hash_key(key, ht->bits);
}
/**
* qdf_ptr_hash_add() - insert an entry into a qdf_ptr_hash
* @ht: the qdf_ptr_hash to insert into
* @key: the pointer to use as an insertion/lookup key
* @item: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @item
*
* Return: None
*/
#define qdf_ptr_hash_add(ht, key, item, entry_field) \
__qdf_ptr_hash_add(ht, (uintptr_t)key, &(item)->entry_field)
static inline void __qdf_ptr_hash_add(struct qdf_ptr_hash *ht, uintptr_t key,
struct qdf_ptr_hash_entry *entry)
{
entry->key = key;
qdf_slist_push(&__qdf_ptr_hash_get_bucket(ht, key)->list, entry, node);
}
/**
* qdf_ptr_hash_remove() - remove an entry from a qdf_ptr_hash
* @ht: the qdf_ptr_hash to remove from
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*
* Return: removed item of type @cursor on success, NULL otherwise
*/
#define qdf_ptr_hash_remove(ht, key, cursor, entry_field) ({ \
struct qdf_ptr_hash_entry *_e = \
__qdf_ptr_hash_remove(ht, (uintptr_t)key); \
cursor = _e ? qdf_container_of(_e, typeof(*(cursor)), \
entry_field) : NULL; \
cursor; })
static inline struct qdf_ptr_hash_entry *
__qdf_ptr_hash_remove(struct qdf_ptr_hash *ht, uintptr_t key)
{
struct qdf_ptr_hash_bucket *bucket = __qdf_ptr_hash_get_bucket(ht, key);
struct qdf_ptr_hash_entry *prev;
struct qdf_ptr_hash_entry *entry;
qdf_slist_for_each_del(&bucket->list, prev, entry, node) {
if (entry->key == key) {
qdf_slist_remove(&bucket->list, prev, node);
entry->key = 0;
return entry;
}
}
return NULL;
}
#define __qdf_ptr_hash_for_each_in_bucket(bucket, cursor, entry_field) \
qdf_slist_for_each(&(bucket)->list, cursor, entry_field.node)
/**
* qdf_ptr_hash_for_each() - qdf_ptr_hash item iterator for all items
* @ht: the qdf_ptr_hash to iterate over
* @bucket: qdf_ptr_hash_bucket cursor pointer
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each(ht, bucket, cursor, entry_field) \
__qdf_ptr_hash_for_each_bucket(ht, bucket) \
__qdf_ptr_hash_for_each_in_bucket(bucket, cursor, entry_field)
/**
* qdf_ptr_hash_for_each_by_hash() - qdf_ptr_hash item iterator for items which
* hash to the same value as @key
* @ht: the qdf_ptr_hash to iterate over
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each_by_hash(ht, key, cursor, entry_field) \
__qdf_ptr_hash_for_each_in_bucket( \
__qdf_ptr_hash_get_bucket(ht, (uintptr_t)key), \
cursor, entry_field)
/**
* qdf_ptr_hash_for_each_by_key() - qdf_ptr_hash item iterator for items whose
* keys equal @key
* @ht: the qdf_ptr_hash to iterate over
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each_by_key(ht, _key, cursor, entry_field) \
qdf_ptr_hash_for_each_by_hash(ht, _key, cursor, entry_field) \
if ((cursor)->entry_field.key == (uintptr_t)_key)
/**
* qdf_ptr_hash_get() - get the first item whose key matches @key
* @ht: the qdf_ptr_hash to look in
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*
* Return: first item matching @key of type @cursor on success, NULL otherwise
*/
#define qdf_ptr_hash_get(ht, key, cursor, entry_field) ({ \
cursor = NULL; \
qdf_ptr_hash_for_each_by_key(ht, key, cursor, entry_field) \
break; \
cursor; })
#endif /* __QDF_PTR_HASH_H */