Files
asterisk/main/hashtab.c
Matt Jordan 4a58261694 git migration: Refactor the ASTERISK_FILE_VERSION macro
Git does not support the ability to replace a token with a version
string during check-in. While it does have support for replacing a
token on clone, this is somewhat sub-optimal: the token is replaced
with the object hash, which is not particularly easy for human
consumption. What's more, in practice, the source file version was often
not terribly useful. Generally, when triaging bugs, the overall version
of Asterisk is far more useful than an individual SVN version of a file. As a
result, this patch removes Asterisk's support for showing source file
versions.

Specifically, it does the following:

* Rename ASTERISK_FILE_VERSION macro to ASTERISK_REGISTER_FILE, and
  remove passing the version in with the macro. Other facilities
  than 'core show file version' make use of the file names, such as
  setting a debug level only on a specific file. As such, the act of
  registering source files with the Asterisk core still has use. The
  macro rename now reflects the new macro purpose.

* main/asterisk:
  - Refactor the file_version structure to reflect that it no longer
    tracks a version field.
  - Remove the "core show file version" CLI command. Without the file
    version, it is no longer useful.
  - Remove the ast_file_version_find function. The file version is no
    longer tracked.
  - Rename ast_register_file_version/ast_unregister_file_version to
    ast_register_file/ast_unregister_file, respectively.

* main/manager: Remove value from the Version key of the ModuleCheck
  Action. The actual key itself has not been removed, as doing so would
  absolutely constitute a backwards incompatible change. However, since
  the file version is no longer tracked, there is no need to attempt to
  include it in the Version key.

* UPGRADE: Add notes for:
  - Modification to the ModuleCheck AMI Action
  - Removal of the "core show file version" CLI command

Change-Id: I6cf0ff280e1668bf4957dc21f32a5ff43444a40e
2015-04-13 03:48:57 -04:00

905 lines
22 KiB
C

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Steve Murphy <murf@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief code to implement generic hash tables
*
* \author Steve Murphy <murf@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#define WRAP_LIBC_MALLOC
#include "asterisk.h"
ASTERISK_REGISTER_FILE()
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/frame.h"
#include "asterisk/channel.h"
#include "asterisk/cli.h"
#include "asterisk/term.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/linkedlists.h"
#include "asterisk/hashtab.h"
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
static void _ast_hashtab_resize(struct ast_hashtab *tab, const char *file, int lineno, const char *func);
#define ast_hashtab_resize(a) _ast_hashtab_resize(a,__FILE__, __LINE__, __PRETTY_FUNCTION__)
#else
static void ast_hashtab_resize(struct ast_hashtab *tab);
#endif
static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h);
/* some standard, default routines for general use */
int ast_hashtab_compare_strings(const void *a, const void *b)
{
return strcmp(a, b);
}
int ast_hashtab_compare_strings_nocase(const void *a, const void *b)
{
return strcasecmp(a, b);
}
int ast_hashtab_compare_ints(const void *a, const void *b)
{
int ai = *((int *) a);
int bi = *((int *) b);
if (ai < bi)
return -1;
return !(ai == bi);
}
int ast_hashtab_compare_shorts(const void *a, const void *b)
{
short as = *((short *) a);
short bs = *((short *) b);
if (as < bs)
return -1;
return !(as == bs);
}
int ast_hashtab_resize_java(struct ast_hashtab *tab)
{
double loadfactor = (double) tab->hash_tab_elements / (double) tab->hash_tab_size;
return (loadfactor > 0.75);
}
int ast_hashtab_resize_tight(struct ast_hashtab *tab)
{
return (tab->hash_tab_elements > tab->hash_tab_size); /* this is quicker than division */
}
int ast_hashtab_resize_none(struct ast_hashtab *tab) /* always return 0 -- no resizing */
{
return 0;
}
int ast_is_prime(int num)
{
int tnum, limit;
if (!(num & 0x1)) /* even number -- not prime */
return 0;
/* Loop through ODD numbers starting with 3 */
tnum = 3;
limit = num;
while (tnum < limit) {
if (!(num % tnum))
return 0;
/* really, we only need to check sqrt(num) numbers */
limit = num / tnum;
/* we only check odd numbers */
tnum = tnum + 2;
}
/* if we made it through the loop, the number is a prime */
return 1;
}
int ast_hashtab_newsize_java(struct ast_hashtab *tab)
{
int i = (tab->hash_tab_size << 1); /* multiply by two */
while (!ast_is_prime(i))
i++;
return i;
}
int ast_hashtab_newsize_tight(struct ast_hashtab *tab)
{
int x = (tab->hash_tab_size << 1);
int i = (tab->hash_tab_size + x);
while (!ast_is_prime(i))
i++;
return i;
}
int ast_hashtab_newsize_none(struct ast_hashtab *tab) /* always return current size -- no resizing */
{
return tab->hash_tab_size;
}
unsigned int ast_hashtab_hash_string(const void *obj)
{
unsigned char *str = (unsigned char *) obj;
unsigned int total;
for (total = 0; *str; str++) {
unsigned int tmp = total;
total <<= 1; /* multiply by 2 */
total += tmp; /* multiply by 3 */
total <<= 2; /* multiply by 12 */
total += tmp; /* multiply by 13 */
total += ((unsigned int)(*str));
}
return total;
}
unsigned int ast_hashtab_hash_string_sax(const void *obj) /* from Josh */
{
const unsigned char *str = obj;
unsigned int total = 0, c = 0;
while ((c = *str++))
total ^= (total << 5) + (total >> 2) + (total << 10) + c;
return total;
}
unsigned int ast_hashtab_hash_string_nocase(const void *obj)
{
const unsigned char *str = obj;
unsigned int total;
for (total = 0; *str; str++) {
unsigned int tmp = total;
unsigned int charval = toupper(*str);
/* hopefully, the following is faster than multiplication by 7 */
/* why do I go to this bother? A good compiler will do this
anyway, if I say total *= 13 */
/* BTW, tried *= 7, and it doesn't do as well in spreading things around! */
total <<= 1; /* multiply by 2 */
total += tmp; /* multiply by 3 */
total <<= 2; /* multiply by 12 */
total += tmp; /* multiply by 13 */
total += (charval);
}
return total;
}
unsigned int ast_hashtab_hash_int(const int x)
{
return x;
}
unsigned int ast_hashtab_hash_short(const short x)
{
/* hmmmm.... modulus is best < 65535 !! */
return x;
}
struct ast_hashtab *
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
_ast_hashtab_create
#else
ast_hashtab_create
#endif
(int initial_buckets,
int (*compare)(const void *a, const void *b),
int (*resize)(struct ast_hashtab *),
int (*newsize)(struct ast_hashtab *tab),
unsigned int (*hash)(const void *obj),
int do_locking
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
, const char *file, int lineno, const char *function
#endif
)
{
struct ast_hashtab *ht;
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
if (!(ht = __ast_calloc(1, sizeof(*ht), file, lineno, function)))
#else
if (!(ht = ast_calloc(1, sizeof(*ht))))
#endif
return NULL;
while (!ast_is_prime(initial_buckets)) /* make sure this is prime */
initial_buckets++;
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
if (!(ht->array = __ast_calloc(initial_buckets, sizeof(*(ht->array)), file, lineno, function))) {
#else
if (!(ht->array = ast_calloc(initial_buckets, sizeof(*(ht->array))))) {
#endif
ast_free(ht);
return NULL;
}
ht->hash_tab_size = initial_buckets;
ht->compare = compare;
ht->resize = resize;
ht->newsize = newsize;
ht->hash = hash;
ht->do_locking = do_locking;
if (do_locking)
ast_rwlock_init(&ht->lock);
if (!ht->resize)
ht->resize = ast_hashtab_resize_java;
if (!ht->newsize)
ht->newsize = ast_hashtab_newsize_java;
return ht;
}
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
struct ast_hashtab *_ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj), const char *file, int lineno, const char *func)
#else
struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj))
#endif
{
struct ast_hashtab *ht;
unsigned int i;
if (!(ht = ast_calloc(1, sizeof(*ht))))
return NULL;
if (!(ht->array =
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
__ast_calloc(tab->hash_tab_size, sizeof(*(ht->array)), file, lineno, func)
#else
ast_calloc(tab->hash_tab_size, sizeof(*(ht->array)))
#endif
)) {
free(ht);
return NULL;
}
ht->hash_tab_size = tab->hash_tab_size;
ht->compare = tab->compare;
ht->resize = tab->resize;
ht->newsize = tab->newsize;
ht->hash = tab->hash;
ht->do_locking = tab->do_locking;
if (ht->do_locking)
ast_rwlock_init(&ht->lock);
/* now, dup the objects in the buckets and get them into the table */
/* the fast way is to use the existing array index, and not have to hash
the objects again */
for (i = 0; i < ht->hash_tab_size; i++) {
struct ast_hashtab_bucket *b = tab->array[i];
while (b) {
void *newobj = (*obj_dup_func)(b->object);
if (newobj)
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
_ast_hashtab_insert_immediate_bucket(ht, newobj, i, file, lineno, func);
#else
ast_hashtab_insert_immediate_bucket(ht, newobj, i);
#endif
b = b->next;
}
}
return ht;
}
static void tlist_del_item(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
{
/* item had better be in the list! or suffer the weirdness that occurs, later! */
if (*head == item) { /* first item in the list */
*head = item->tnext;
if (item->tnext)
item->tnext->tprev = NULL;
} else {
/* short circuit stuff */
item->tprev->tnext = item->tnext;
if (item->tnext)
item->tnext->tprev = item->tprev;
}
}
static void tlist_add_head(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
{
if (*head) {
item->tnext = *head;
item->tprev = NULL;
(*head)->tprev = item;
*head = item;
} else {
/* the list is empty */
*head = item;
item->tprev = NULL;
item->tnext = NULL;
}
}
/* user-controlled hashtab locking. Create a hashtab without locking, then call the
following locking routines yourself to lock the table between threads. */
void ast_hashtab_wrlock(struct ast_hashtab *tab)
{
ast_rwlock_wrlock(&tab->lock);
}
void ast_hashtab_rdlock(struct ast_hashtab *tab)
{
ast_rwlock_rdlock(&tab->lock);
}
void ast_hashtab_initlock(struct ast_hashtab *tab)
{
ast_rwlock_init(&tab->lock);
}
void ast_hashtab_destroylock(struct ast_hashtab *tab)
{
ast_rwlock_destroy(&tab->lock);
}
void ast_hashtab_unlock(struct ast_hashtab *tab)
{
ast_rwlock_unlock(&tab->lock);
}
void ast_hashtab_destroy(struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj))
{
/* this func will free the hash table and all its memory. It
doesn't touch the objects stored in it */
if (tab) {
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
if (tab->array) {
/* go thru and destroy the buckets */
struct ast_hashtab_bucket *t;
int i;
while (tab->tlist) {
t = tab->tlist;
if (t->object && objdestroyfunc) {
/* I cast this because I'm not going to MOD it, I'm going to DESTROY
* it.
*/
(*objdestroyfunc)((void *) t->object);
}
tlist_del_item(&(tab->tlist), tab->tlist);
free(t);
}
for (i = 0; i < tab->hash_tab_size; i++) {
/* Not totally necessary, but best to destroy old pointers */
tab->array[i] = NULL;
}
free(tab->array);
}
if (tab->do_locking) {
ast_rwlock_unlock(&tab->lock);
ast_rwlock_destroy(&tab->lock);
}
free(tab);
}
}
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
int _ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj, const char *file, int lineno, const char *func)
#else
int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj)
#endif
{
unsigned int h;
int res=0;
if (!tab || !obj)
return res;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
res = _ast_hashtab_insert_immediate_bucket(tab, obj, h, file, lineno, func);
#else
res = ast_hashtab_insert_immediate_bucket(tab, obj, h);
#endif
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return res;
}
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
int _ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, unsigned int h, const char *file, int lineno, const char *func)
#else
int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, unsigned int h)
#endif
{
int c;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
for (c = 0, b = tab->array[h]; b; b= b->next)
c++;
if (c + 1 > tab->largest_bucket_size)
tab->largest_bucket_size = c + 1;
if (!(b =
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
__ast_calloc(1, sizeof(*b), file, lineno, func)
#else
ast_calloc(1, sizeof(*b))
#endif
)) return 0;
b->object = obj;
b->next = tab->array[h];
tab->array[h] = b;
if (b->next)
b->next->prev = b;
tlist_add_head(&(tab->tlist), b);
tab->hash_tab_elements++;
if ((*tab->resize)(tab))
ast_hashtab_resize(tab);
return 1;
}
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
int _ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj, const char *file, int lineno, const char *func)
#else
int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj)
#endif
{
/* check to see if the element is already there; insert only if
it is not there. */
/* will force a resize if the resize func returns 1 */
/* returns 1 on success, 0 if there's a problem, or it's already there. */
unsigned int bucket = 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
if (!ast_hashtab_lookup_bucket(tab, obj, &bucket)) {
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
int ret2 = _ast_hashtab_insert_immediate_bucket(tab, obj, bucket, file, lineno, func);
#else
int ret2 = ast_hashtab_insert_immediate_bucket(tab, obj, bucket);
#endif
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return ret2;
}
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return 0;
}
void *ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
void *ret;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
h = (*tab->hash)(obj) % tab->hash_tab_size;
ret = ast_hashtab_lookup_internal(tab,obj,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return ret;
}
void *ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
void *ret;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
h = hashval % tab->hash_tab_size;
ret = ast_hashtab_lookup_internal(tab,obj,h);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return ret;
}
void *ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, unsigned int *bucket)
{
/* lookup this object in the hash table. return a ptr if found, or NULL if not */
unsigned int h;
void *ret;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
ret = ast_hashtab_lookup_internal(tab,obj,h);
*bucket = h;
return ret;
}
static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h)
{
struct ast_hashtab_bucket *b;
for (b = tab->array[h]; b; b = b->next) {
if (!(*tab->compare)(obj,b->object)) {
/* I can't touch obj in this func, but the outside world is welcome to */
return (void*) b->object;
}
}
return NULL;
}
void ast_hashtab_get_stats(struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets)
{
/* returns key stats for the table */
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
*biggest_bucket_size = tab->largest_bucket_size;
*resize_count = tab->resize_count;
*num_objects = tab->hash_tab_elements;
*num_buckets = tab->hash_tab_size;
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
}
/* this function returns the number of elements stored in the hashtab */
int ast_hashtab_size(struct ast_hashtab *tab)
{
return tab->hash_tab_elements;
}
/* this function returns the size of the bucket array in the hashtab */
int ast_hashtab_capacity( struct ast_hashtab *tab)
{
return tab->hash_tab_size;
}
/* the insert operation calls this, and is wrlock'd when it does. */
/* if you want to call it, you should set the wrlock yourself */
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
static void _ast_hashtab_resize(struct ast_hashtab *tab, const char *file, int lineno, const char *func)
#else
static void ast_hashtab_resize(struct ast_hashtab *tab)
#endif
{
/* this function is called either internally, when the resize func returns 1, or
externally by the user to force a resize of the hash table */
int newsize = (*tab->newsize)(tab), i, c;
unsigned int h;
struct ast_hashtab_bucket *b,*bn;
/* Since we keep a DLL of all the buckets in tlist,
all we have to do is free the array, malloc a new one,
and then go thru the tlist array and reassign them into
the bucket arrayj.
*/
for (i = 0; i < tab->hash_tab_size; i++) { /* don't absolutely have to do this, but
why leave ptrs laying around */
tab->array[i] = 0; /* erase old ptrs */
}
free(tab->array);
if (!(tab->array =
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
__ast_calloc(newsize, sizeof(*(tab->array)), file, lineno, func)
#else
ast_calloc(newsize, sizeof(*(tab->array)))
#endif
))
return;
/* now sort the buckets into their rightful new slots */
tab->resize_count++;
tab->hash_tab_size = newsize;
tab->largest_bucket_size = 0;
for (b = tab->tlist; b; b = bn) {
b->prev = 0;
bn = b->tnext;
h = (*tab->hash)(b->object) % tab->hash_tab_size;
b->next = tab->array[h];
if (b->next)
b->next->prev = b;
tab->array[h] = b;
}
/* recalc the largest bucket size */
for (i = 0; i < tab->hash_tab_size; i++) {
for (c = 0, b = tab->array[i]; b; b = b->next)
c++;
if (c > tab->largest_bucket_size)
tab->largest_bucket_size = c;
}
}
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
struct ast_hashtab_iter *_ast_hashtab_start_traversal(struct ast_hashtab *tab, const char *file, int lineno, const char *func)
#else
struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab)
#endif
{
/* returns an iterator */
struct ast_hashtab_iter *it;
if (!(it =
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
__ast_calloc(1, sizeof(*it), file, lineno, func)
#else
ast_calloc(1, sizeof(*it))
#endif
))
return NULL;
it->next = tab->tlist;
it->tab = tab;
if (tab->do_locking)
ast_rwlock_rdlock(&tab->lock);
return it;
}
/* use this function to get a write lock */
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
struct ast_hashtab_iter *_ast_hashtab_start_write_traversal(struct ast_hashtab *tab, const char *file, int lineno, const char *func)
#else
struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab)
#endif
{
/* returns an iterator */
struct ast_hashtab_iter *it;
if (!(it =
#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
__ast_calloc(1, sizeof(*it), file, lineno, func)
#else
ast_calloc(1, sizeof(*it))
#endif
))
return NULL;
it->next = tab->tlist;
it->tab = tab;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
return it;
}
void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
{
if (it->tab->do_locking)
ast_rwlock_unlock(&it->tab->lock);
free(it);
}
void *ast_hashtab_next(struct ast_hashtab_iter *it)
{
/* returns the next object in the list, advances iter one step */
struct ast_hashtab_bucket *retval;
if (it && it->next) { /* there's a next in the bucket list */
retval = it->next;
it->next = retval->tnext;
return (void *) retval->object;
}
return NULL;
}
static void *ast_hashtab_remove_object_internal(struct ast_hashtab *tab, struct ast_hashtab_bucket *b, int h)
{
const void *obj2;
if (b->prev)
b->prev->next = b->next;
else
tab->array[h] = b->next;
if (b->next)
b->next->prev = b->prev;
tlist_del_item(&(tab->tlist), b);
obj2 = b->object;
b->object = b->next = (void*)2;
free(b); /* free up the hashbucket */
tab->hash_tab_elements--;
#ifdef DEBUG
{
int c2;
struct ast_hashtab_bucket *b2;
/* do a little checking */
for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
c2++;
}
if (c2 != tab->hash_tab_elements) {
printf("Hey! we didn't delete right! there are %d elements in the list, and we expected %d\n",
c2, tab->hash_tab_elements);
}
for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
unsigned int obj3 = (unsigned long) obj2;
unsigned int b3 = (unsigned long) b;
if (b2->object == obj2)
printf("Hey-- you've still got a bucket pointing at ht_element %x\n", obj3);
if (b2->next == b)
printf("Hey-- you've still got a bucket with next ptr pointing to deleted bucket %x\n", b3);
if (b2->prev == b)
printf("Hey-- you've still got a bucket with prev ptr pointing to deleted bucket %x\n", b3);
if (b2->tprev == b)
printf("Hey-- you've still got a bucket with tprev ptr pointing to deleted bucket %x\n", b3);
if (b2->tnext == b)
printf("Hey-- you've still got a bucket with tnext ptr pointing to deleted bucket %x\n", b3);
}
}
#endif
return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj)
{
/* looks up the object; removes the corresponding bucket */
const void *obj2;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
obj2 = ast_hashtab_remove_object_via_lookup_nolock(tab,obj);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void *)obj2;
}
void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj)
{
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b = tab->array[h]; b; b = b->next) {
if (!(*tab->compare)(obj, b->object)) {
const void *obj2;
obj2 = ast_hashtab_remove_object_internal(tab, b, h);
return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
return 0;
}
void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj)
{
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket -- a slightly cheaper operation */
/* looks up the object; removes the corresponding bucket */
const void *obj2;
if (!tab || !obj)
return 0;
if (tab->do_locking)
ast_rwlock_wrlock(&tab->lock);
obj2 = ast_hashtab_remove_this_object_nolock(tab,obj);
if (tab->do_locking)
ast_rwlock_unlock(&tab->lock);
return (void *)obj2;
}
void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj)
{
/* looks up the object by hash and then comparing pts in bucket list instead of
calling the compare routine; removes the bucket -- a slightly cheaper operation */
/* looks up the object; removes the corresponding bucket */
unsigned int h;
struct ast_hashtab_bucket *b;
if (!tab || !obj)
return 0;
h = (*tab->hash)(obj) % tab->hash_tab_size;
for (b = tab->array[h]; b; b = b->next) {
if (obj == b->object) {
const void *obj2;
obj2 = ast_hashtab_remove_object_internal(tab, b, h);
return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
}
}
return 0;
}