diff --git a/Makefile.am b/Makefile.am index 6923b530dc..3d948a80fb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -220,7 +220,8 @@ library_include_HEADERS = \ libs/libtpl-1.5/src/tpl.h \ src/include/switch_limit.h \ src/include/switch_odbc.h \ - src/include/switch_pgsql.h + src/include/switch_pgsql.h \ + src/include/switch_hashtable.h nodist_libfreeswitch_la_SOURCES = \ src/include/switch_frame.h \ @@ -287,6 +288,7 @@ libfreeswitch_la_SOURCES = \ src/switch_profile.c \ src/switch_json.c \ src/switch_curl.c \ + src/switch_hashtable.c\ libs/libtpl-1.5/src/tpl.c \ libs/stfu/stfu.c \ libs/libteletone/src/libteletone_detect.c \ diff --git a/src/include/private/switch_hashtable_private.h b/src/include/private/switch_hashtable_private.h new file mode 100644 index 0000000000..d8220310a0 --- /dev/null +++ b/src/include/private/switch_hashtable_private.h @@ -0,0 +1,130 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * switch_hashtable_private.h -- Hashtable + * + */ + +/* hashtable_private.h -- Copyright (C) 2002, 2004 Christopher Clark */ + +#ifndef __HASHTABLE_PRIVATE_CWC22_H__ +#define __HASHTABLE_PRIVATE_CWC22_H__ + +#include "switch_hashtable.h" + +#ifdef __cplusplus +extern "C" { +#endif +/*****************************************************************************/ + +struct entry +{ + void *k, *v; + unsigned int h; + hashtable_flag_t flags; + struct entry *next; +}; + +struct switch_hashtable_iterator { + unsigned int pos; + struct entry *e; + struct switch_hashtable *h; +}; + +struct switch_hashtable { + unsigned int tablelength; + struct entry **table; + unsigned int entrycount; + unsigned int loadlimit; + unsigned int primeindex; + unsigned int (*hashfn) (void *k); + int (*eqfn) (void *k1, void *k2); + struct switch_hashtable_iterator iterator; +}; + +/*****************************************************************************/ + +/*****************************************************************************/ +static inline unsigned int +hash(switch_hashtable_t *h, void *k) +{ + /* Aim to protect against poor hash functions by adding logic here + * - logic taken from java 1.4 hashtable source */ + unsigned int i = h->hashfn(k); + i += ~(i << 9); + i ^= ((i >> 14) | (i << 18)); /* >>> */ + i += (i << 4); + i ^= ((i >> 10) | (i << 22)); /* >>> */ + return i; +} + + +/*****************************************************************************/ +/* indexFor */ +static __inline__ unsigned int +indexFor(unsigned int tablelength, unsigned int hashvalue) { + return (hashvalue % tablelength); +} + +/* Only works if tablelength == 2^N */ +/*static inline unsigned int + indexFor(unsigned int tablelength, unsigned int hashvalue) + { + return (hashvalue & (tablelength - 1u)); + } +*/ + +/*****************************************************************************/ +#define freekey(X) free(X) +/*define freekey(X) ; */ + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif /* __HASHTABLE_PRIVATE_CWC22_H__*/ + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/include/switch_hashtable.h b/src/include/switch_hashtable.h new file mode 100644 index 0000000000..4056064a32 --- /dev/null +++ b/src/include/switch_hashtable.h @@ -0,0 +1,281 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * switch_hashtable.h -- Hashtable + * + */ + + +/* hashtable.h Copyright (C) 2002 Christopher Clark */ + +#ifndef __HASHTABLE_CWC22_H__ +#define __HASHTABLE_CWC22_H__ +#ifdef _MSC_VER +#ifndef __inline__ +#define __inline__ __inline +#endif +#endif +#include "switch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct switch_hashtable switch_hashtable_t; +typedef struct switch_hashtable_iterator switch_hashtable_iterator_t; + + +/* Example of use: + * + * switch_hashtable_t *h; + * struct some_key *k; + * struct some_value *v; + * + * static unsigned int hash_from_key_fn( void *k ); + * static int keys_equal_fn ( void *key1, void *key2 ); + * + * h = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + * k = (struct some_key *) malloc(sizeof(struct some_key)); + * v = (struct some_value *) malloc(sizeof(struct some_value)); + * + * (initialise k and v to suitable values) + * + * if (! hashtable_insert(h,k,v) ) + * { exit(-1); } + * + * if (NULL == (found = hashtable_search(h,k) )) + * { printf("not found!"); } + * + * if (NULL == (found = hashtable_remove(h,k) )) + * { printf("Not found\n"); } + * + */ + +/* Macros may be used to define type-safe(r) hashtable access functions, with + * methods specialized to take known key and value types as parameters. + * + * Example: + * + * Insert this at the start of your file: + * + * DEFINE_HASHTABLE_INSERT(insert_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_SEARCH(search_some, struct some_key, struct some_value); + * DEFINE_HASHTABLE_REMOVE(remove_some, struct some_key, struct some_value); + * + * This defines the functions 'insert_some', 'search_some' and 'remove_some'. + * These operate just like hashtable_insert etc., with the same parameters, + * but their function signatures have 'struct some_key *' rather than + * 'void *', and hence can generate compile time errors if your program is + * supplying incorrect data as a key (and similarly for value). + * + * Note that the hash and key equality functions passed to create_hashtable + * still take 'void *' parameters instead of 'some key *'. This shouldn't be + * a difficult issue as they're only defined and passed once, and the other + * functions will ensure that only valid keys are supplied to them. + * + * The cost for this checking is increased code size and runtime overhead + * - if performance is important, it may be worth switching back to the + * unsafe methods once your program has been debugged with the safe methods. + * This just requires switching to some simple alternative defines - eg: + * #define insert_some hashtable_insert + * + */ + +/***************************************************************************** + * create_hashtable + + * @name create_hashtable + * @param minsize minimum initial size of hashtable + * @param hashfunction function for hashing keys + * @param key_eq_fn function for determining key equality + * @return newly created hashtable or NULL on failure + */ + +SWITCH_DECLARE(switch_status_t) +switch_create_hashtable(switch_hashtable_t **hp, unsigned int minsize, + unsigned int (*hashfunction) (void*), + int (*key_eq_fn) (void*,void*)); + +/***************************************************************************** + * hashtable_insert + + * @name hashtable_insert + * @param h the hashtable to insert into + * @param k the key - hashtable claims ownership and will free on removal + * @param v the value - does not claim ownership + * @return non-zero for successful insertion + * + * This function will cause the table to expand if the insertion would take + * the ratio of entries to table size over the maximum load factor. + * + * This function does not check for repeated insertions with a duplicate key. + * The value returned when using a duplicate key is undefined -- when + * the hashtable changes size, the order of retrieval of duplicate key + * entries is reversed. + * If in doubt, remove before insert. + */ + + +typedef enum { + HASHTABLE_FLAG_NONE = 0, + HASHTABLE_FLAG_FREE_KEY = (1 << 0), + HASHTABLE_FLAG_FREE_VALUE = (1 << 1) +} hashtable_flag_t; + +SWITCH_DECLARE(int) +switch_hashtable_insert(switch_hashtable_t *h, void *k, void *v, hashtable_flag_t flags); + +#define DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ + int fnname (switch_hashtable_t *h, keytype *k, valuetype *v) \ + { \ + return hashtable_insert(h,k,v); \ + } + +/***************************************************************************** + * hashtable_search + + * @name hashtable_search + * @param h the hashtable to search + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +SWITCH_DECLARE(void *) +switch_hashtable_search(switch_hashtable_t *h, void *k); + +#define DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ + valuetype * fnname (switch_hashtable_t *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_search(h,k)); \ + } + +/***************************************************************************** + * hashtable_remove + + * @name hashtable_remove + * @param h the hashtable to remove the item from + * @param k the key to search for - does not claim ownership + * @return the value associated with the key, or NULL if none found + */ + +SWITCH_DECLARE(void *) /* returns value */ +switch_hashtable_remove(switch_hashtable_t *h, void *k); + +#define DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ + valuetype * fnname (switch_hashtable_t *h, keytype *k) \ + { \ + return (valuetype *) (hashtable_remove(h,k)); \ + } + + +/***************************************************************************** + * hashtable_count + + * @name hashtable_count + * @param h the hashtable + * @return the number of items stored in the hashtable + */ +SWITCH_DECLARE(unsigned int) +switch_hashtable_count(switch_hashtable_t *h); + + +/***************************************************************************** + * hashtable_destroy + + * @name hashtable_destroy + * @param h the hashtable + * @param free_values whether to call 'free' on the remaining values + */ + +SWITCH_DECLARE(void) +switch_hashtable_destroy(switch_hashtable_t **h); + +SWITCH_DECLARE(switch_hashtable_iterator_t*) switch_hashtable_first(switch_hashtable_t *h); +SWITCH_DECLARE(switch_hashtable_iterator_t*) switch_hashtable_next(switch_hashtable_iterator_t *i); +SWITCH_DECLARE(void) switch_hashtable_this(switch_hashtable_iterator_t *i, const void **key, switch_ssize_t *klen, void **val); + +static inline int switch_hash_equalkeys(void *k1, void *k2) +{ + return strcmp((char *) k1, (char *) k2) ? 0 : 1; +} + +static inline uint32_t switch_hash_default(void *ky) +{ + unsigned char *str = (unsigned char *) ky; + uint32_t hash = 0; + int c; + + while ((c = *str)) { + str++; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +static inline uint32_t switch_hash_default_ci(void *ky) +{ + unsigned char *str = (unsigned char *) ky; + uint32_t hash = 0; + int c; + + while ((c = switch_tolower(*str))) { + str++; + hash = c + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + + + + +#ifdef __cplusplus +} /* extern C */ +#endif + +#endif /* __HASHTABLE_CWC22_H__ */ + +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/switch_hashtable.c b/src/switch_hashtable.c new file mode 100644 index 0000000000..a550aa9fd6 --- /dev/null +++ b/src/switch_hashtable.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2002, Christopher Clark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "switch.h" +#include "private/switch_hashtable_private.h" + +/* + Credit for primes table: Aaron Krowne + http://br.endernet.org/~akrowne/ + http://planetmath.org/encyclopedia/GoodHashTablePrimes.html +*/ +static const unsigned int primes[] = { + 53, 97, 193, 389, + 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, + 196613, 393241, 786433, 1572869, + 3145739, 6291469, 12582917, 25165843, + 50331653, 100663319, 201326611, 402653189, + 805306457, 1610612741 +}; +const unsigned int prime_table_length = sizeof(primes)/sizeof(primes[0]); +const float max_load_factor = 0.65f; + +/*****************************************************************************/ +SWITCH_DECLARE(switch_status_t) +switch_create_hashtable(switch_hashtable_t **hp, unsigned int minsize, + unsigned int (*hashf) (void*), + int (*eqf) (void*,void*)) +{ + switch_hashtable_t *h; + unsigned int pindex, size = primes[0]; + + /* Check requested hashtable isn't too large */ + if (minsize > (1u << 30)) {*hp = NULL; return SWITCH_STATUS_FALSE;} + /* Enforce size as prime */ + for (pindex=0; pindex < prime_table_length; pindex++) { + if (primes[pindex] > minsize) { + size = primes[pindex]; + break; + } + } + h = (switch_hashtable_t *) malloc(sizeof(switch_hashtable_t)); + + if (NULL == h) abort(); /*oom*/ + + h->table = (struct entry **)malloc(sizeof(struct entry*) * size); + + if (NULL == h->table) abort(); /*oom*/ + + memset(h->table, 0, size * sizeof(struct entry *)); + h->tablelength = size; + h->primeindex = pindex; + h->entrycount = 0; + h->hashfn = hashf; + h->eqfn = eqf; + h->loadlimit = (unsigned int) ceil(size * max_load_factor); + + *hp = h; + return SWITCH_STATUS_SUCCESS; +} + +/*****************************************************************************/ +static int +hashtable_expand(switch_hashtable_t *h) +{ + /* Double the size of the table to accomodate more entries */ + struct entry **newtable; + struct entry *e; + struct entry **pE; + unsigned int newsize, i, index; + /* Check we're not hitting max capacity */ + if (h->primeindex == (prime_table_length - 1)) return 0; + newsize = primes[++(h->primeindex)]; + + newtable = (struct entry **)malloc(sizeof(struct entry*) * newsize); + if (NULL != newtable) + { + memset(newtable, 0, newsize * sizeof(struct entry *)); + /* This algorithm is not 'stable'. ie. it reverses the list + * when it transfers entries between the tables */ + for (i = 0; i < h->tablelength; i++) { + while (NULL != (e = h->table[i])) { + h->table[i] = e->next; + index = indexFor(newsize,e->h); + e->next = newtable[index]; + newtable[index] = e; + } + } + switch_safe_free(h->table); + h->table = newtable; + } + /* Plan B: realloc instead */ + else + { + newtable = (struct entry **) + realloc(h->table, newsize * sizeof(struct entry *)); + if (NULL == newtable) { (h->primeindex)--; return 0; } + h->table = newtable; + memset(newtable[h->tablelength], 0, newsize - h->tablelength); + for (i = 0; i < h->tablelength; i++) { + for (pE = &(newtable[i]), e = *pE; e != NULL; e = *pE) { + index = indexFor(newsize,e->h); + + if (index == i) { + pE = &(e->next); + } else { + *pE = e->next; + e->next = newtable[index]; + newtable[index] = e; + } + } + } + } + h->tablelength = newsize; + h->loadlimit = (unsigned int) ceil(newsize * max_load_factor); + return -1; +} + +/*****************************************************************************/ +SWITCH_DECLARE(unsigned int) +switch_hashtable_count(switch_hashtable_t *h) +{ + return h->entrycount; +} + +/*****************************************************************************/ +SWITCH_DECLARE(int) +switch_hashtable_insert(switch_hashtable_t *h, void *k, void *v, hashtable_flag_t flags) +{ + /* This method allows duplicate keys - but they shouldn't be used */ + unsigned int index; + struct entry *e; + if (++(h->entrycount) > h->loadlimit) + { + /* Ignore the return value. If expand fails, we should + * still try cramming just this value into the existing table + * -- we may not have memory for a larger table, but one more + * element may be ok. Next time we insert, we'll try expanding again.*/ + hashtable_expand(h); + } + e = (struct entry *)malloc(sizeof(struct entry)); + if (NULL == e) { --(h->entrycount); return 0; } /*oom*/ + e->h = hash(h,k); + index = indexFor(h->tablelength,e->h); + e->k = k; + e->v = v; + e->flags = flags; + e->next = h->table[index]; + h->table[index] = e; + return -1; +} + +/*****************************************************************************/ +SWITCH_DECLARE(void *) /* returns value associated with key */ +switch_hashtable_search(switch_hashtable_t *h, void *k) +{ + struct entry *e; + unsigned int hashvalue, index; + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hashvalue); + e = h->table[index]; + while (NULL != e) { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) return e->v; + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +SWITCH_DECLARE(void *) /* returns value associated with key */ +switch_hashtable_remove(switch_hashtable_t *h, void *k) +{ + /* TODO: consider compacting the table when the load factor drops enough, + * or provide a 'compact' method. */ + + struct entry *e; + struct entry **pE; + void *v; + unsigned int hashvalue, index; + + hashvalue = hash(h,k); + index = indexFor(h->tablelength,hash(h,k)); + pE = &(h->table[index]); + e = *pE; + while (NULL != e) { + /* Check hash value to short circuit heavier comparison */ + if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { + *pE = e->next; + h->entrycount--; + v = e->v; + if (e->flags & HASHTABLE_FLAG_FREE_KEY) { + freekey(e->k); + } + switch_safe_free(e); + return v; + } + pE = &(e->next); + e = e->next; + } + return NULL; +} + +/*****************************************************************************/ +/* destroy */ +SWITCH_DECLARE(void) +switch_hashtable_destroy(switch_hashtable_t **h) +{ + unsigned int i; + struct entry *e, *f; + struct entry **table = (*h)->table; + + for (i = 0; i < (*h)->tablelength; i++) { + e = table[i]; + while (NULL != e) { + f = e; e = e->next; + + if (f->flags & HASHTABLE_FLAG_FREE_KEY) { + freekey(f->k); + } + + if (f->flags & HASHTABLE_FLAG_FREE_VALUE) { + switch_safe_free(f->v); + } + switch_safe_free(f); + } + } + + switch_safe_free((*h)->table); + free(*h); + *h = NULL; +} + +SWITCH_DECLARE(switch_hashtable_iterator_t *) switch_hashtable_next(switch_hashtable_iterator_t *i) +{ + + if (i->e) { + if ((i->e = i->e->next) != 0) { + return i; + } else { + i->pos++; + } + } + + while(i->pos < i->h->tablelength && !i->h->table[i->pos]) { + i->pos++; + } + + if (i->pos >= i->h->tablelength) { + return NULL; + } + + if ((i->e = i->h->table[i->pos]) != 0) { + return i; + } + + return NULL; +} + +SWITCH_DECLARE(switch_hashtable_iterator_t *) switch_hashtable_first(switch_hashtable_t *h) +{ + h->iterator.pos = 0; + h->iterator.e = NULL; + h->iterator.h = h; + return switch_hashtable_next(&h->iterator); +} + + + +SWITCH_DECLARE(void) switch_hashtable_this(switch_hashtable_iterator_t *i, const void **key, switch_ssize_t *klen, void **val) +{ + if (i->e) { + if (key) { + *key = i->e->k; + } + if (klen) { + *klen = (int)strlen(i->e->k); + } + if (val) { + *val = i->e->v; + } + } else { + if (key) { + *key = NULL; + } + if (klen) { + *klen = 0; + } + if (val) { + *val = NULL; + } + } +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ +