You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by st...@apache.org on 2002/05/04 21:54:39 UTC
cvs commit: httpd-2.0/modules/experimental cache_hash.c cache_hash.h mod_mem_cache.c mod_cache.dsp
stoddard 02/05/04 12:54:39
Modified: modules/experimental mod_mem_cache.c mod_cache.dsp
Added: modules/experimental cache_hash.c cache_hash.h
Log:
Introduce a dedicated cache hash adt that does not rely on pools. This
cures a storage leak when garbage collecting expired entries out of the
hash table. cache_hash is just apr_hash with some of the function removed
and reimplemented to use malloc/free in place of apr_pool calls.
Revision Changes Path
1.53 +19 -31 httpd-2.0/modules/experimental/mod_mem_cache.c
Index: mod_mem_cache.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/experimental/mod_mem_cache.c,v
retrieving revision 1.52
retrieving revision 1.53
diff -u -r1.52 -r1.53
--- mod_mem_cache.c 3 May 2002 20:13:57 -0000 1.52
+++ mod_mem_cache.c 4 May 2002 19:54:39 -0000 1.53
@@ -58,6 +58,7 @@
#define CORE_PRIVATE
#include "mod_cache.h"
+#include "cache_hash.h"
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
#if APR_HAVE_UNISTD_H
@@ -70,13 +71,6 @@
module AP_MODULE_DECLARE_DATA mem_cache_module;
-/*
- * XXX
- * This cache uses apr_hash functions which leak storage when something is removed
- * from the cache. This can be fixed in the apr_hash functions by making them use
- * malloc/free rather than pools to manage their storage requirements.
- */
-
typedef enum {
CACHE_TYPE_FILE = 1,
CACHE_TYPE_HEAP,
@@ -103,7 +97,7 @@
typedef struct {
apr_thread_mutex_t *lock;
- apr_hash_t *cacheht;
+ cache_hash_t *cacheht;
apr_size_t cache_size;
apr_size_t object_cnt;
@@ -206,7 +200,7 @@
* removed the object from the cache (and set obj->cleanup)
*/
if (!obj->cleanup) {
- apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+ cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
sconf->object_cnt--;
sconf->cache_size -= mobj->m_len;
obj->cleanup = 1;
@@ -243,7 +237,7 @@
static apr_status_t cleanup_cache_mem(void *sconfv)
{
cache_object_t *obj;
- apr_hash_index_t *hi;
+ cache_hash_index_t *hi;
mem_cache_conf *co = (mem_cache_conf*) sconfv;
if (!co) {
@@ -254,12 +248,12 @@
apr_thread_mutex_lock(sconf->lock);
}
/* Iterate over the cache and clean up each entry */
- while ((hi = apr_hash_first(NULL, co->cacheht)) != NULL) {
+ while ((hi = cache_hash_first(NULL, co->cacheht)) != NULL) {
/* Fetch the object from the cache */
- apr_hash_this(hi, NULL, NULL, (void **)&obj);
+ cache_hash_this(hi, NULL, NULL, (void **)&obj);
if (obj) {
/* Remove the object from the cache */
- apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+ cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
/* Free the object if the recount == 0 */
#ifdef USE_ATOMICS
apr_atomic_inc(&obj->refcount);
@@ -288,12 +282,12 @@
sconf = apr_pcalloc(p, sizeof(mem_cache_conf));
-
ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
if (threaded_mpm) {
apr_thread_mutex_create(&sconf->lock, APR_THREAD_MUTEX_DEFAULT, p);
}
- sconf->cacheht = apr_hash_make(p);
+ /* Todo: determine hash table size from max_cache_object_cnt */
+ sconf->cacheht = cache_hash_make(512);
sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE;
sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE;
@@ -399,11 +393,11 @@
if (sconf->lock) {
apr_thread_mutex_lock(sconf->lock);
}
- tmp_obj = (cache_object_t *) apr_hash_get(sconf->cacheht,
+ tmp_obj = (cache_object_t *) cache_hash_get(sconf->cacheht,
key,
- APR_HASH_KEY_STRING);
+ CACHE_HASH_KEY_STRING);
if (!tmp_obj) {
- apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
+ cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj);
sconf->object_cnt++;
sconf->cache_size += len;
}
@@ -445,8 +439,8 @@
if (sconf->lock) {
apr_thread_mutex_lock(sconf->lock);
}
- obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key,
- APR_HASH_KEY_STRING);
+ obj = (cache_object_t *) cache_hash_get(sconf->cacheht, key,
+ CACHE_HASH_KEY_STRING);
if (obj) {
if (obj->complete) {
request_rec *rmain=r, *rtmp;
@@ -508,10 +502,11 @@
*/
if (!obj->cleanup) {
mem_cache_object_t *mobj = (mem_cache_object_t *) obj->vobj;
- apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
+ cache_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL);
sconf->object_cnt--;
sconf->cache_size -= mobj->m_len;
obj->cleanup = 1;
+ ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, 0, NULL, "gcing a cache entry");
}
if (sconf->lock) {
@@ -585,13 +580,6 @@
if (strcasecmp(type, "mem") && strcasecmp(type, "fd")) {
return DECLINED;
}
-
- /* WIBNIF
- * apr_hash_set(..,..,..,NULL) returned pointer to the object just removed.
- * That way, we could free the object w/o doing another call to an
- * apr_hash function.
- */
-
/* Order of the operations is important to avoid race conditions.
* First, remove the object from the cache. Remember, all additions
* deletions from the cache are protected by sconf->lock.
@@ -604,11 +592,11 @@
if (sconf->lock) {
apr_thread_mutex_lock(sconf->lock);
}
- obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key,
- APR_HASH_KEY_STRING);
+ /* This call will return a pointer to the cache_object just removed */
+ obj = cache_hash_set(sconf->cacheht, key, CACHE_HASH_KEY_STRING, NULL);
if (obj) {
+ /* obj has been removed from the cache */
mem_cache_object_t *mobj = (mem_cache_object_t *) obj->vobj;
- apr_hash_set(sconf->cacheht, key, APR_HASH_KEY_STRING, NULL);
sconf->object_cnt--;
sconf->cache_size -= mobj->m_len;
1.7 +10 -2 httpd-2.0/modules/experimental/mod_cache.dsp
Index: mod_cache.dsp
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/experimental/mod_cache.dsp,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- mod_cache.dsp 3 Apr 2002 06:09:18 -0000 1.6
+++ mod_cache.dsp 4 May 2002 19:54:39 -0000 1.7
@@ -52,8 +52,8 @@
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
-# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /machine:I386
-# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /machine:I386 /out:"Release/mod_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_cache.so" /base:@..\..\os\win32\BaseAddr.ref,mod_cache
!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug"
@@ -92,6 +92,10 @@
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
+SOURCE=.\cache_hash.c
+# End Source File
+# Begin Source File
+
SOURCE=.\cache_storage.c
# End Source File
# Begin Source File
@@ -106,6 +110,10 @@
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\cache_hash.h
+# End Source File
# Begin Source File
SOURCE=.\mod_cache.h
1.1 httpd-2.0/modules/experimental/cache_hash.c
Index: cache_hash.c
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#include "apr_general.h"
#include "mod_cache.h"
#include "cache_hash.h"
#if APR_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if APR_HAVE_STRING_H
#include <string.h>
#endif
/*
* The internal form of a hash table.
*
* The table is an array indexed by the hash of the key; collisions
* are resolved by hanging a linked list of hash entries off each
* element of the array. Although this is a really simple design it
* isn't too bad given that pools have a low allocation overhead.
*/
typedef struct cache_hash_entry_t cache_hash_entry_t;
struct cache_hash_entry_t {
cache_hash_entry_t *next;
int hash;
const void *key;
apr_ssize_t klen;
const void *val;
};
/*
* Data structure for iterating through a hash table.
*
* We keep a pointer to the next hash entry here to allow the current
* hash entry to be freed or otherwise mangled between calls to
* cache_hash_next().
*/
struct cache_hash_index_t {
cache_hash_t *ht;
cache_hash_entry_t *this, *next;
int index;
};
/*
* The size of the array is always a power of two. We use the maximum
* index rather than the size so that we can use bitwise-AND for
* modular arithmetic.
* The count of hash entries may be greater depending on the chosen
* collision rate.
*/
struct cache_hash_t {
cache_hash_entry_t **array;
cache_hash_index_t iterator; /* For cache_hash_first(NULL, ...) */
int count, max;
};
/*
* Hash creation functions.
*/
static cache_hash_entry_t **alloc_array(cache_hash_t *ht, int max)
{
return calloc(1, sizeof(*ht->array) * (max + 1));
}
CACHE_DECLARE(cache_hash_t *) cache_hash_make(apr_size_t size)
{
cache_hash_t *ht;
ht = malloc(sizeof(cache_hash_t));
if (!ht) {
return NULL;
}
ht->count = 0;
ht->max = size;
ht->array = alloc_array(ht, ht->max);
if (!ht->array) {
free(ht);
return NULL;
}
return ht;
}
/*
* Hash iteration functions.
*/
CACHE_DECLARE(cache_hash_index_t *) cache_hash_next(cache_hash_index_t *hi)
{
hi->this = hi->next;
while (!hi->this) {
if (hi->index > hi->ht->max)
return NULL;
hi->this = hi->ht->array[hi->index++];
}
hi->next = hi->this->next;
return hi;
}
CACHE_DECLARE(cache_hash_index_t *) cache_hash_first(cache_hash_t *ht)
{
cache_hash_index_t *hi;
hi = &ht->iterator;
hi->ht = ht;
hi->index = 0;
hi->this = NULL;
hi->next = NULL;
return cache_hash_next(hi);
}
CACHE_DECLARE(void) cache_hash_this(cache_hash_index_t *hi,
const void **key,
apr_ssize_t *klen,
void **val)
{
if (key) *key = hi->this->key;
if (klen) *klen = hi->this->klen;
if (val) *val = (void *)hi->this->val;
}
/*
* This is where we keep the details of the hash function and control
* the maximum collision rate.
*
* If val is non-NULL it creates and initializes a new hash entry if
* there isn't already one there; it returns an updatable pointer so
* that hash entries can be removed.
*/
static cache_hash_entry_t **find_entry(cache_hash_t *ht,
const void *key,
apr_ssize_t klen,
const void *val)
{
cache_hash_entry_t **hep, *he;
const unsigned char *p;
int hash;
apr_ssize_t i;
/*
* This is the popular `times 33' hash algorithm which is used by
* perl and also appears in Berkeley DB. This is one of the best
* known hash functions for strings because it is both computed
* very fast and distributes very well.
*
* The originator may be Dan Bernstein but the code in Berkeley DB
* cites Chris Torek as the source. The best citation I have found
* is "Chris Torek, Hash function for text in C, Usenet message
* <27...@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich
* Salz's USENIX 1992 paper about INN which can be found at
* <http://citeseer.nj.nec.com/salz92internetnews.html>.
*
* The magic of number 33, i.e. why it works better than many other
* constants, prime or not, has never been adequately explained by
* anyone. So I try an explanation: if one experimentally tests all
* multipliers between 1 and 256 (as I did while writing a low-level
* data structure library some time ago) one detects that even
* numbers are not useable at all. The remaining 128 odd numbers
* (except for the number 1) work more or less all equally well.
* They all distribute in an acceptable way and this way fill a hash
* table with an average percent of approx. 86%.
*
* If one compares the chi^2 values of the variants (see
* Bob Jenkins ``Hashing Frequently Asked Questions'' at
* http://burtleburtle.net/bob/hash/hashfaq.html for a description
* of chi^2), the number 33 not even has the best value. But the
* number 33 and a few other equally good numbers like 17, 31, 63,
* 127 and 129 have nevertheless a great advantage to the remaining
* numbers in the large set of possible multipliers: their multiply
* operation can be replaced by a faster operation based on just one
* shift plus either a single addition or subtraction operation. And
* because a hash function has to both distribute good _and_ has to
* be very fast to compute, those few numbers should be preferred.
*
* -- Ralf S. Engelschall <rs...@engelschall.com>
*/
hash = 0;
if (klen == CACHE_HASH_KEY_STRING) {
for (p = key; *p; p++) {
hash = hash * 33 + *p;
}
klen = p - (const unsigned char *)key;
}
else {
for (p = key, i = klen; i; i--, p++) {
hash = hash * 33 + *p;
}
}
/* scan linked list */
for (hep = &ht->array[hash & ht->max], he = *hep;
he;
hep = &he->next, he = *hep) {
if (he->hash == hash &&
he->klen == klen &&
memcmp(he->key, key, klen) == 0)
break;
}
if (he || !val)
return hep;
/* add a new entry for non-NULL values */
he = malloc(sizeof(*he));
if (!he) {
return NULL;
}
he->next = NULL;
he->hash = hash;
he->key = key;
he->klen = klen;
he->val = val;
*hep = he;
ht->count++;
return hep;
}
CACHE_DECLARE(void *) cache_hash_get(cache_hash_t *ht,
const void *key,
apr_ssize_t klen)
{
cache_hash_entry_t *he;
he = *find_entry(ht, key, klen, NULL);
if (he)
return (void *)he->val;
else
return NULL;
}
CACHE_DECLARE(void*) cache_hash_set(cache_hash_t *ht,
const void *key,
apr_ssize_t klen,
const void *val)
{
cache_hash_entry_t **hep, *tmp;
const void *tval;
hep = find_entry(ht, key, klen, val);
if (*hep) {
if (!val) {
/* delete entry */
tval = (*hep)->val;
tmp = *hep;
*hep = (*hep)->next;
free(tmp);
--ht->count;
}
else {
/* replace entry */
tval = (*hep)->val;
(*hep)->val = val;
}
/* Return the object just removed from the cache
* to let the caller clean it up
*/
return tval;
}
/* else key not present and val==NULL */
return NULL;
}
CACHE_DECLARE(int) cache_hash_count(cache_hash_t *ht)
{
return ht->count;
}
1.1 httpd-2.0/modules/experimental/cache_hash.h
Index: cache_hash.h
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#ifndef CACHE_HASH_H
#define CACHE_HASH_H
#ifdef __cplusplus
extern "C" {
#endif
#include "mod_cache.h"
/**
* @file cache_hash.h
* @brief Cache Hash Tables
*/
/**
* @defgroup Cache_Hash Hash Tables
* @ingroup CACHE
* @{
*/
/**
* When passing a key to cache_hash_set or cache_hash_get, this value can be
* passed to indicate a string-valued key, and have cache_hash compute the
* length automatically.
*
* @remark cache_hash will use strlen(key) for the length. The null-terminator
* is not included in the hash value (why throw a constant in?).
* Since the hash table merely references the provided key (rather
* than copying it), cache_hash_this() will return the null-term'd key.
*/
#define CACHE_HASH_KEY_STRING (-1)
/**
* Abstract type for hash tables.
*/
typedef struct cache_hash_t cache_hash_t;
/**
* Abstract type for scanning hash tables.
*/
typedef struct cache_hash_index_t cache_hash_index_t;
/**
* Create a hash table.
* @param size
* @return The hash table just created
*/
CACHE_DECLARE(cache_hash_t *) cache_hash_make(apr_size_t size);
/**
* Associate a value with a key in a hash table.
* @param ht The hash table
* @param key Pointer to the key
* @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
* @param val Value to associate with the key
* @remark If the value is NULL the hash entry is deleted.
* @return The value of the deleted cache entry (so the caller can clean it up).
*/
CACHE_DECLARE(void *) cache_hash_set(cache_hash_t *ht, const void *key,
apr_ssize_t klen, const void *val);
/**
* Look up the value associated with a key in a hash table.
* @param ht The hash table
* @param key Pointer to the key
* @param klen Length of the key. Can be CACHE_HASH_KEY_STRING to use the string length.
* @return Returns NULL if the key is not present.
*/
CACHE_DECLARE(void *) cache_hash_get(cache_hash_t *ht, const void *key,
apr_ssize_t klen);
/**
* Start iterating over the entries in a hash table.
* @param p The pool to allocate the cache_hash_index_t iterator. If this
* pool is NULL, then an internal, non-thread-safe iterator is used.
* @param ht The hash table
* @example
*/
/**
* <PRE>
*
* int sum_values(apr_pool_t *p, cache_hash_t *ht)
* {
* cache_hash_index_t *hi;
* void *val;
* int sum = 0;
* for (hi = cache_hash_first(p, ht); hi; hi = cache_hash_next(hi)) {
* cache_hash_this(hi, NULL, NULL, &val);
* sum += *(int *)val;
* }
* return sum;
* }
*
* There is no restriction on adding or deleting hash entries during an
* iteration (although the results may be unpredictable unless all you do
* is delete the current entry) and multiple iterations can be in
* progress at the same time.
* </PRE>
*/
CACHE_DECLARE(cache_hash_index_t *) cache_hash_first(cache_hash_t *ht);
/**
* Continue iterating over the entries in a hash table.
* @param hi The iteration state
* @return a pointer to the updated iteration state. NULL if there are no more
* entries.
*/
CACHE_DECLARE(cache_hash_index_t *) cache_hash_next(cache_hash_index_t *hi);
/**
* Get the current entry's details from the iteration state.
* @param hi The iteration state
* @param key Return pointer for the pointer to the key.
* @param klen Return pointer for the key length.
* @param val Return pointer for the associated value.
* @remark The return pointers should point to a variable that will be set to the
* corresponding data, or they may be NULL if the data isn't interesting.
*/
CACHE_DECLARE(void) cache_hash_this(cache_hash_index_t *hi, const void **key,
apr_ssize_t *klen, void **val);
/**
* Get the number of key/value pairs in the hash table.
* @param ht The hash table
* @return The number of key/value pairs in the hash table.
*/
CACHE_DECLARE(int) cache_hash_count(cache_hash_t *ht);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* !CACHE_HASH_H */