]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/store.cc
Import of fix-ranges branch
[thirdparty/squid.git] / src / store.cc
index c4c21d89325e6baf36b66e0e5a574e4804496d43..8f94f14af60ee6240950ad9f3b59819f3607f3cb 100644 (file)
@@ -1,32 +1,32 @@
 
 /*
- * $Id: store.cc,v 1.513 2000/02/01 05:17:58 wessels Exp $
+ * $Id: store.cc,v 1.554 2003/01/23 00:37:26 robertc Exp $
  *
  * DEBUG: section 20    Storage Manager
  * AUTHOR: Harvest Derived
  *
- * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
+ * SQUID Web Proxy Cache          http://www.squid-cache.org/
  * ----------------------------------------------------------
  *
- *  Squid is the result of efforts by numerous individuals from the
- *  Internet community.  Development is led by Duane Wessels of the
- *  National Laboratory for Applied Network Research and funded by the
- *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
- *  Duane Wessels and the University of California San Diego.  Please
- *  see the COPYRIGHT file for full details.  Squid incorporates
- *  software developed and/or copyrighted by other sources.  Please see
- *  the CREDITS file for full details.
+ *  Squid is the result of efforts by numerous individuals from
+ *  the Internet community; see the CONTRIBUTORS file for full
+ *  details.   Many organizations have provided support for Squid's
+ *  development; see the SPONSORS file for full details.  Squid is
+ *  Copyrighted (C) 2001 by the Regents of the University of
+ *  California; see the COPYRIGHT file for full details.  Squid
+ *  incorporates software developed and/or copyrighted by other
+ *  sources; see the CREDITS file for full details.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
- *
+ *  
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *
+ *  
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  */
 
 #include "squid.h"
+#include "Store.h"
+#include "StoreClient.h"
+#include "stmem.h"
+#include "HttpReply.h"
+#include "HttpRequest.h"
+#include "MemObject.h"
+#include "mem_node.h"
+#include "StoreMeta.h"
+#include "SwapDir.h"
+
+static STMCB storeWriteComplete;
 
 #define REBUILD_TIMESTAMP_DELTA_MAX 2
 
@@ -71,128 +82,171 @@ typedef struct lock_ctrl_t {
     StoreEntry *e;
 } lock_ctrl_t;
 
+extern OBJH storeIOStats;
+
 /*
  * local function prototypes
  */
-static int storeCheckExpired(const StoreEntry *);
-static int storeEntryLocked(const StoreEntry *);
-static int storeEntryValidLength(const StoreEntry *);
 static void storeGetMemSpace(int);
 static void storeHashDelete(StoreEntry *);
-static MemObject *new_MemObject(const char *, const char *);
 static void destroy_MemObject(StoreEntry *);
-static FREE destroy_StoreEntry;
+static FREE destroyStoreEntry;
 static void storePurgeMem(StoreEntry *);
+static void storeEntryReferenced(StoreEntry *);
+static void storeEntryDereferenced(StoreEntry *);
 static int getKeyCounter(void);
 static int storeKeepInMemory(const StoreEntry *);
 static OBJH storeCheckCachableStats;
 static EVH storeLateRelease;
-#if HEAP_REPLACEMENT
-static heap_key_func HeapKeyGen_StoreEntry_LFUDA;
-static heap_key_func HeapKeyGen_StoreEntry_GDSF;
-static heap_key_func HeapKeyGen_StoreEntry_LRU;
-#endif
 
 /*
  * local variables
  */
-#if HEAP_REPLACEMENT
-/*
- * The heap equivalent of inmem_list, inmem_heap, is in globals.c so other
- * modules can access it when updating object metadata (e.g., refcount)
- */
-#else
-static dlink_list inmem_list;
-#endif
-static int store_pages_max = 0;
-static int store_swap_high = 0;
-static int store_swap_low = 0;
 static Stack LateReleaseStack;
+MemPool *StoreEntry::pool = NULL;
 
-#if URL_CHECKSUM_DEBUG
-unsigned int
-url_checksum(const char *url)
-{
-    unsigned int ck;
-    MD5_CTX M;
-    static unsigned char digest[16];
-    MD5Init(&M);
-    MD5Update(&M, (unsigned char *) url, strlen(url));
-    MD5Final(digest, &M);
-    xmemcpy(&ck, digest, sizeof(ck));
-    return ck;
+void *
+StoreEntry::operator new (size_t bytecount)
+{
+    assert (bytecount == sizeof (StoreEntry));
+    if (!pool) {
+       pool = memPoolCreate ("StoreEntry", bytecount);
+       memPoolSetChunkSize(pool, 2048 * 1024);
+    }
+    return memPoolAlloc (pool);
+}
+
+void
+StoreEntry::operator delete (void *address)
+{
+    memPoolFree(pool, address);
+}
+
+size_t
+StoreEntry::inUseCount()
+{
+    if (!pool)
+       return 0;
+    MemPoolStats stats;
+    memPoolGetStats (&stats, pool);
+    return stats.items_inuse;
+}
+
+const char *
+StoreEntry::getMD5Text() const
+{
+    return storeKeyText((const cache_key *)key);
 }
-#endif
 
-static MemObject *
-new_MemObject(const char *url, const char *log_url)
+int
+StoreEntry::CheckDeferRead(int fd, void *data)
 {
-    MemObject *mem = memAllocate(MEM_MEMOBJECT);
-    mem->reply = httpReplyCreate();
-    mem->url = xstrdup(url);
+    assert (data);
+    return ((StoreEntry *)data)->checkDeferRead(fd);
+}
+
+int
+StoreEntry::checkDeferRead(int fd) const
+{
+    int rc = 0;
+    if (mem_obj == NULL)
+       return 0;
 #if URL_CHECKSUM_DEBUG
-    mem->chksum = url_checksum(mem->url);
+    mem_obj->checkUrlChecksum();
 #endif
-    mem->log_url = xstrdup(log_url);
-    mem->object_sz = -1;
-    mem->fd = -1;
-    /* XXX account log_url */
-    debug(20, 3) ("new_MemObject: returning %p\n", mem);
-    return mem;
+#if DELAY_POOLS
+    if (fd < 0)
+       (void) 0;
+    else if (! delayIsNoDelay(fd)) {
+       int i = delayMostBytesWanted(mem_obj, INT_MAX);
+       if (0 == i)
+           return 1;
+       /* was: rc = -(rc != INT_MAX); */
+       else if (INT_MAX == i)
+           rc = 0;
+       else
+           rc = -1;
+    }
+#endif
+    if (EBIT_TEST(flags, ENTRY_FWD_HDR_WAIT))
+       return rc;
+    if (mem_obj->readAheadPolicyCanRead())
+       return rc;
+    return 1;
+}
+
+store_client_t
+StoreEntry::storeClientType() const
+{
+    if (mem_obj->inmem_lo)
+       return STORE_DISK_CLIENT;
+    if (EBIT_TEST(flags, ENTRY_ABORTED)) {
+       /* I don't think we should be adding clients to aborted entries */
+       debug(20, 1) ("storeClientType: adding to ENTRY_ABORTED entry\n");
+       return STORE_MEM_CLIENT;
+    }
+    if (store_status == STORE_OK) {
+       if (mem_obj->inmem_lo == 0 && !isEmpty())
+           return STORE_MEM_CLIENT;
+       else
+           return STORE_DISK_CLIENT;
+    }
+    /* here and past, entry is STORE_PENDING */
+    /*
+     * If this is the first client, let it be the mem client
+     */
+    if (mem_obj->nclients == 1)
+       return STORE_MEM_CLIENT;
+    /*
+     * If there is no disk file to open yet, we must make this a
+     * mem client.  If we can't open the swapin file before writing
+     * to the client, there is no guarantee that we will be able
+     * to open it later when we really need it.
+     */
+    if (swap_status == SWAPOUT_NONE)
+       return STORE_MEM_CLIENT;
+    /*
+     * otherwise, make subsequent clients read from disk so they
+     * can not delay the first, and vice-versa.
+     */
+    return STORE_DISK_CLIENT;
 }
 
 StoreEntry *
 new_StoreEntry(int mem_obj_flag, const char *url, const char *log_url)
 {
     StoreEntry *e = NULL;
-    e = memAllocate(MEM_STOREENTRY);
+    e = new StoreEntry;
     if (mem_obj_flag)
-       e->mem_obj = new_MemObject(url, log_url);
-    debug(20, 3) ("new_StoreEntry: returning %p\n", e);
+       e->mem_obj = new MemObject(url, log_url);
+    debug(20, 3) ("newStoreEntry: returning %p\n", e);
     e->expires = e->lastmod = e->lastref = e->timestamp = -1;
-    e->swap_file_number = -1;
+    e->swap_filen = -1;
+    e->swap_dirn = -1;
     return e;
 }
 
 static void
 destroy_MemObject(StoreEntry * e)
 {
+    storeSetMemStatus(e, NOT_IN_MEMORY);
     MemObject *mem = e->mem_obj;
-    const Ctx ctx = ctx_enter(mem->url);
-    debug(20, 3) ("destroy_MemObject: destroying %p\n", mem);
-#if URL_CHECKSUM_DEBUG
-    assert(mem->chksum == url_checksum(mem->url));
-#endif
     e->mem_obj = NULL;
-    if (!shutting_down)
-       assert(mem->swapout.sio == NULL);
-    stmemFree(&mem->data_hdr);
-    mem->inmem_hi = 0;
-    /*
-     * There is no way to abort FD-less clients, so they might
-     * still have mem->clients set if mem->fd == -1
-     */
-    assert(mem->fd == -1 || mem->clients == NULL);
-    httpReplyDestroy(mem->reply);
-    requestUnlink(mem->request);
-    mem->request = NULL;
-    ctx_exit(ctx);             /* must exit before we free mem->url */
-    safe_free(mem->url);
-    safe_free(mem->log_url);   /* XXX account log_url */
-    memFree(mem, MEM_MEMOBJECT);
+    delete mem;
 }
 
 static void
-destroy_StoreEntry(void *data)
+destroyStoreEntry(void *data)
 {
-    StoreEntry *e = data;
-    debug(20, 3) ("destroy_StoreEntry: destroying %p\n", e);
+    StoreEntry *e = static_cast<StoreEntry *>(data);
+    debug(20, 3) ("destroyStoreEntry: destroying %p\n", e);
     assert(e != NULL);
-    if (e->mem_obj)
-       destroy_MemObject(e);
+    if (e == NullStoreEntry::getInstance())
+       return;
+    destroy_MemObject(e);
     storeHashDelete(e);
     assert(e->key == NULL);
-    memFree(e, MEM_STOREENTRY);
+    delete e;
 }
 
 /* ----- INTERFACE BETWEEN STORAGE MANAGER AND HASH TABLE FUNCTIONS --------- */
@@ -203,29 +257,14 @@ storeHashInsert(StoreEntry * e, const cache_key * key)
     debug(20, 3) ("storeHashInsert: Inserting Entry %p key '%s'\n",
        e, storeKeyText(key));
     e->key = storeKeyDup(key);
-    hash_join(store_table, (hash_link *) e);
-#if HEAP_REPLACEMENT
-    if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-       (void) 0;
-    } else {
-       e->node = heap_insert(store_heap, e);
-       debug(20, 4) ("storeHashInsert: inserted node %p\n", e->node);
-    }
-#endif
+    hash_join(store_table, e);
 }
 
 static void
 storeHashDelete(StoreEntry * e)
 {
-    hash_remove_link(store_table, (hash_link *) e);
-#if HEAP_REPLACEMENT
-    if (e->node) {
-       debug(20, 4) ("storeHashDelete: deleting node %p\n", e->node);
-       heap_delete(store_heap, e->node);
-       e->node = NULL;
-    }
-#endif
-    storeKeyFree(e->key);
+    hash_remove_link(store_table, e);
+    storeKeyFree((const cache_key *)e->key);
     e->key = NULL;
 }
 
@@ -233,41 +272,52 @@ storeHashDelete(StoreEntry * e)
 
 
 /* get rid of memory copy of the object */
-/* Only call this if storeCheckPurgeMem(e) returns 1 */
 static void
 storePurgeMem(StoreEntry * e)
 {
     if (e->mem_obj == NULL)
        return;
     debug(20, 3) ("storePurgeMem: Freeing memory-copy of %s\n",
-       storeKeyText(e->key));
-    storeSetMemStatus(e, NOT_IN_MEMORY);
+       e->getMD5Text());
     destroy_MemObject(e);
     if (e->swap_status != SWAPOUT_DONE)
        storeRelease(e);
 }
 
+static void
+storeEntryReferenced(StoreEntry * e)
+{
+    /* Notify the fs that we're referencing this object again */
+    if (e->swap_dirn > -1)
+       INDEXSD(e->swap_dirn)->reference(*e);
+    /* Notify the memory cache that we're referencing this object again */
+    if (e->mem_obj) {
+       if (mem_policy->Referenced)
+           mem_policy->Referenced(mem_policy, e, &e->mem_obj->repl);
+    }
+}
+
+static void
+storeEntryDereferenced(StoreEntry * e)
+{
+    /* Notify the fs that we're not referencing this object any more */
+    if (e->swap_filen > -1)
+       INDEXSD(e->swap_dirn)->dereference(*e);
+    /* Notify the memory cache that we're not referencing this object any more */
+    if (e->mem_obj) {
+       if (mem_policy->Dereferenced)
+           mem_policy->Dereferenced(mem_policy, e, &e->mem_obj->repl);
+    }
+}
+
 void
 storeLockObject(StoreEntry * e)
 {
-    if (e->lock_count++ == 0) {
-#if HEAP_REPLACEMENT
-       /*
-        * There is no reason to take any action here.  Squid by
-        * default is moving locked objects to the end of the LRU
-        * list to keep them from getting bumped into by the
-        * replacement algorithm.  We can't do that so we will just
-        * have to handle them.
-        */
-       debug(20, 4) ("storeLockObject: just locked node %p\n", e->node);
-#else
-       storeDirLRUDelete(e);
-       storeDirLRUAdd(e);
-#endif
-    }
+    e->lock_count++;
     debug(20, 3) ("storeLockObject: key '%s' count=%d\n",
-       storeKeyText(e->key), (int) e->lock_count);
+       e->getMD5Text(), (int) e->lock_count);
     e->lastref = squid_curtime;
+    storeEntryReferenced(e);
 }
 
 void
@@ -275,7 +325,7 @@ storeReleaseRequest(StoreEntry * e)
 {
     if (EBIT_TEST(e->flags, RELEASE_REQUEST))
        return;
-    debug(20, 3) ("storeReleaseRequest: '%s'\n", storeKeyText(e->key));
+    debug(20, 3) ("storeReleaseRequest: '%s'\n", e->getMD5Text());
     EBIT_SET(e->flags, RELEASE_REQUEST);
     /*
      * Clear cachable flag here because we might get called before
@@ -293,26 +343,21 @@ storeUnlockObject(StoreEntry * e)
 {
     e->lock_count--;
     debug(20, 3) ("storeUnlockObject: key '%s' count=%d\n",
-       storeKeyText(e->key), e->lock_count);
+       e->getMD5Text(), e->lock_count);
     if (e->lock_count)
        return (int) e->lock_count;
     if (e->store_status == STORE_PENDING)
        EBIT_SET(e->flags, RELEASE_REQUEST);
     assert(storePendingNClients(e) == 0);
-#if HEAP_REPLACEMENT
-    storeHeapPositionUpdate(e);
-#else
-    storeDirLRUDelete(e);
-    storeDirLRUAdd(e);
-#endif
     if (EBIT_TEST(e->flags, RELEASE_REQUEST))
        storeRelease(e);
     else if (storeKeepInMemory(e)) {
+       storeEntryDereferenced(e);
        storeSetMemStatus(e, IN_MEMORY);
-       requestUnlink(e->mem_obj->request);
-       e->mem_obj->request = NULL;
+       e->mem_obj->unlinkRequest();
     } else {
        storePurgeMem(e);
+       storeEntryDereferenced(e);
        if (EBIT_TEST(e->flags, KEY_PRIVATE))
            debug(20, 1) ("WARNING: %s:%d: found KEY_PRIVATE\n", __FILE__, __LINE__);
     }
@@ -324,8 +369,42 @@ storeUnlockObject(StoreEntry * e)
 StoreEntry *
 storeGet(const cache_key * key)
 {
+    PROF_start(storeGet);
     debug(20, 3) ("storeGet: looking up %s\n", storeKeyText(key));
-    return (StoreEntry *) hash_lookup(store_table, key);
+    StoreEntry *p = static_cast<StoreEntry *>(hash_lookup(store_table, key));
+    PROF_stop(storeGet);
+    return p;
+}
+
+void
+StoreEntry::getPublicByRequestMethod  (StoreClient *aClient, request_t * request, const method_t method)
+{
+    assert (aClient);
+    StoreEntry *result = storeGetPublicByRequestMethod( request, method);
+    if (!result)
+       aClient->created (NullStoreEntry::getInstance());
+    else
+       aClient->created (result);
+}
+
+void
+StoreEntry::getPublicByRequest (StoreClient *aClient, request_t * request)
+{
+    assert (aClient);
+    StoreEntry *result = storeGetPublicByRequest (request);
+    if (!result)
+       result = NullStoreEntry::getInstance();
+    aClient->created (result);
+}
+
+void
+StoreEntry::getPublic (StoreClient *aClient, const char *uri, const method_t method)
+{
+    assert (aClient);
+    StoreEntry *result = storeGetPublic (uri, method);
+    if (!result)
+       result = NullStoreEntry::getInstance();
+    aClient->created (result);
 }
 
 StoreEntry *
@@ -334,6 +413,22 @@ storeGetPublic(const char *uri, const method_t method)
     return storeGet(storeKeyPublic(uri, method));
 }
 
+StoreEntry *
+storeGetPublicByRequestMethod(request_t * req, const method_t method)
+{
+    return storeGet(storeKeyPublicByRequestMethod(req, method));
+}
+
+StoreEntry *
+storeGetPublicByRequest(request_t * req)
+{
+    StoreEntry *e = storeGetPublicByRequestMethod(req, req->method);
+    if (e == NULL && req->method == METHOD_HEAD)
+       /* We can generate a HEAD reply from a cached GET object */
+       e = storeGetPublicByRequestMethod(req, METHOD_GET);
+    return e;
+}
+
 static int
 getKeyCounter(void)
 {
@@ -351,7 +446,7 @@ storeSetPrivateKey(StoreEntry * e)
     if (e->key && EBIT_TEST(e->flags, KEY_PRIVATE))
        return;                 /* is already private */
     if (e->key) {
-       if (e->swap_file_number > -1)
+       if (e->swap_filen > -1)
            storeDirSwapLog(e, SWAP_LOG_DEL);
        storeHashDelete(e);
     }
@@ -385,24 +480,86 @@ storeSetPublicKey(StoreEntry * e)
      * If RELEASE_REQUEST is set, then ENTRY_CACHABLE should not
      * be set, and storeSetPublicKey() should not be called.
      */
-#if HEAP_REPLACEMENT
+#if MORE_DEBUG_OUTPUT
     if (EBIT_TEST(e->flags, RELEASE_REQUEST))
        debug(20, 1) ("assertion failed: RELEASE key %s, url %s\n",
            e->key, mem->url);
 #endif
     assert(!EBIT_TEST(e->flags, RELEASE_REQUEST));
-    newkey = storeKeyPublic(mem->url, mem->method);
+    if (mem->request) {
+       StoreEntry *pe;
+       request_t *request = mem->request;
+       if (!mem->vary_headers) {
+           /* First handle the case where the object no longer varies */
+           safe_free(request->vary_headers);
+       } else {
+           if (request->vary_headers && strcmp(request->vary_headers, mem->vary_headers) != 0) {
+               /* Oops.. the variance has changed. Kill the base object
+                * to record the new variance key
+                */
+               safe_free(request->vary_headers);       /* free old "bad" variance key */
+               pe = storeGetPublic(mem->url, mem->method);
+               if (pe)
+                   storeRelease(pe);
+           }
+           /* Make sure the request knows the variance status */
+           if (!request->vary_headers) {
+               const char *vary = httpMakeVaryMark(request, mem->getReply());
+               if (vary)
+                   request->vary_headers = xstrdup(vary);
+           }
+       }
+       if (mem->vary_headers && !storeGetPublic(mem->url, mem->method)) {
+           /* Create "vary" base object */
+           http_version_t version;
+           String vary;
+           pe = storeCreateEntry(mem->url, mem->log_url, request->flags, request->method);
+           httpBuildVersion(&version, 1, 0);
+           /* We are allowed to do this typecast */
+           httpReplySetHeaders((HttpReply *)pe->getReply(), version, HTTP_OK, "Internal marker object", "x-squid-internal/vary", -1, -1, squid_curtime + 100000);
+           vary = httpHeaderGetList(&mem->getReply()->header, HDR_VARY);
+           if (vary.size()) {
+               /* Again, we own this structure layout */
+               httpHeaderPutStr((HttpHeader *)&pe->getReply()->header, HDR_VARY, vary.buf());
+               vary.clean();
+           }
+#if X_ACCELERATOR_VARY
+           vary = httpHeaderGetList(&mem->getReply()->header, HDR_X_ACCELERATOR_VARY);
+           if (vary.buf()) {
+               httpHeaderPutStr(&pe->getReply()->header, HDR_X_ACCELERATOR_VARY, vary.buf());
+               vary.clean();
+           }
+#endif
+           storeSetPublicKey(pe);
+           /* TODO: remove this when the metadata is separated */
+             {
+               Packer p;
+               packerToStoreInit(&p, pe);
+               httpReplyPackHeadersInto(pe->getReply(), &p);
+               packerClean(&p);
+             }
+           storeBufferFlush(pe);
+           storeTimestampsSet(pe);
+           pe->complete();
+           storeUnlockObject(pe);
+       }
+       newkey = storeKeyPublicByRequest(mem->request);
+    } else
+       newkey = storeKeyPublic(mem->url, mem->method);
     if ((e2 = (StoreEntry *) hash_lookup(store_table, newkey))) {
        debug(20, 3) ("storeSetPublicKey: Making old '%s' private.\n", mem->url);
        storeSetPrivateKey(e2);
        storeRelease(e2);
-       newkey = storeKeyPublic(mem->url, mem->method);
+       if (mem->request)
+           newkey = storeKeyPublicByRequest(mem->request);
+       else
+           newkey = storeKeyPublic(mem->url, mem->method);
     }
     if (e->key)
        storeHashDelete(e);
     EBIT_CLR(e->flags, KEY_PRIVATE);
     storeHashInsert(e, newkey);
-    if (e->swap_file_number > -1)
+    if (e->swap_filen > -1)
        storeDirSwapLog(e, SWAP_LOG_ADD);
 }
 
@@ -431,10 +588,11 @@ storeCreateEntry(const char *url, const char *log_url, request_flags flags, meth
     e->store_status = STORE_PENDING;
     storeSetMemStatus(e, NOT_IN_MEMORY);
     e->swap_status = SWAPOUT_NONE;
-    e->swap_file_number = -1;
+    e->swap_filen = -1;
+    e->swap_dirn = -1;
     e->refcount = 0;
     e->lastref = squid_curtime;
-    e->timestamp = 0;          /* set in storeTimestampsSet() */
+    e->timestamp = -1;         /* set in storeTimestampsSet() */
     e->ping_status = PING_NONE;
     EBIT_SET(e->flags, ENTRY_VALIDATED);
     return e;
@@ -444,10 +602,35 @@ storeCreateEntry(const char *url, const char *log_url, request_flags flags, meth
 void
 storeExpireNow(StoreEntry * e)
 {
-    debug(20, 3) ("storeExpireNow: '%s'\n", storeKeyText(e->key));
+    debug(20, 3) ("storeExpireNow: '%s'\n", e->getMD5Text());
     e->expires = squid_curtime;
 }
 
+void
+storeWriteComplete (void *data, StoreIOBuffer wroteBuffer)
+{
+    StoreEntry *e = (StoreEntry *)data;
+    if (EBIT_TEST(e->flags, DELAY_SENDING))
+       return;
+    InvokeHandlers(e);
+}
+
+void
+StoreEntry::write (StoreIOBuffer writeBuffer)
+{
+    assert(mem_obj != NULL);
+    assert(writeBuffer.length >= 0);
+    /* This assert will change when we teach the store to update */
+    assert(store_status == STORE_PENDING);
+    if (!writeBuffer.length)
+       return;
+    
+    debug(20, 5) ("storeWrite: writing %u bytes for '%s'\n",
+       writeBuffer.length, getMD5Text());
+    storeGetMemSpace(writeBuffer.length);
+    mem_obj->write (writeBuffer, storeWriteComplete, this);
+}
+
 /* Append incoming data from a primary server to an entry. */
 void
 storeAppend(StoreEntry * e, const char *buf, int len)
@@ -456,18 +639,12 @@ storeAppend(StoreEntry * e, const char *buf, int len)
     assert(mem != NULL);
     assert(len >= 0);
     assert(e->store_status == STORE_PENDING);
-    if (len) {
-       debug(20, 5) ("storeAppend: appending %d bytes for '%s'\n",
-           len,
-           storeKeyText(e->key));
-       storeGetMemSpace(len);
-       stmemAppend(&mem->data_hdr, buf, len);
-       mem->inmem_hi += len;
-    }
-    if (EBIT_TEST(e->flags, DELAY_SENDING))
-       return;
-    InvokeHandlers(e);
-    storeSwapOut(e);
+
+    StoreIOBuffer tempBuffer;
+    tempBuffer.data = (char *)buf;
+    tempBuffer.length = len;
+    tempBuffer.offset = mem->endOffset() - (e->getReply() ? e->getReply()->hdr_sz : 0);
+    e->write(tempBuffer);
 }
 
 void
@@ -511,6 +688,7 @@ struct _store_check_cachable_hist {
        int wrong_content_length;
        int negative_cached;
        int too_big;
+       int too_small;
        int private_key;
        int too_many_open_files;
        int too_many_open_fds;
@@ -530,6 +708,22 @@ storeTooManyDiskFilesOpen(void)
     return 0;
 }
 
+static int
+storeCheckTooSmall(StoreEntry * e)
+{
+    MemObject * const mem = e->mem_obj;
+    if (EBIT_TEST(e->flags, ENTRY_SPECIAL))
+       return 0;
+    if (STORE_OK == e->store_status)
+       if (mem->object_sz < 0 ||
+           static_cast<size_t>(mem->object_sz) < Config.Store.minObjectSize)
+           return 1;
+    if (e->getReply()->content_length > -1)
+       if (e->getReply()->content_length < (int) Config.Store.minObjectSize)
+           return 1;
+    return 0;
+}
+
 int
 storeCheckCachable(StoreEntry * e)
 {
@@ -552,12 +746,17 @@ storeCheckCachable(StoreEntry * e)
        debug(20, 3) ("storeCheckCachable: NO: negative cached\n");
        store_check_cachable_hist.no.negative_cached++;
        return 0;               /* avoid release call below */
-    } else if (e->mem_obj->inmem_hi > Config.Store.maxObjectSize) {
+    } else if ((e->getReply()->content_length > 0 &&
+               static_cast<size_t>(e->getReply()->content_length) > Config.Store.maxObjectSize) ||
+       static_cast<size_t>(e->mem_obj->endOffset()) > Config.Store.maxObjectSize) {
        debug(20, 2) ("storeCheckCachable: NO: too big\n");
        store_check_cachable_hist.no.too_big++;
-    } else if (e->mem_obj->reply->content_length > (int) Config.Store.maxObjectSize) {
+    } else if (e->getReply()->content_length > (int) Config.Store.maxObjectSize) {
        debug(20, 2) ("storeCheckCachable: NO: too big\n");
        store_check_cachable_hist.no.too_big++;
+    } else if (storeCheckTooSmall(e)) {
+       debug(20, 2) ("storeCheckCachable: NO: too small\n");
+       store_check_cachable_hist.no.too_small++;
     } else if (EBIT_TEST(e->flags, KEY_PRIVATE)) {
        debug(20, 3) ("storeCheckCachable: NO: private key\n");
        store_check_cachable_hist.no.private_key++;
@@ -600,6 +799,8 @@ storeCheckCachableStats(StoreEntry * sentry)
        store_check_cachable_hist.no.negative_cached);
     storeAppendPrintf(sentry, "no.too_big\t%d\n",
        store_check_cachable_hist.no.too_big);
+    storeAppendPrintf(sentry, "no.too_small\t%d\n",
+       store_check_cachable_hist.no.too_small);
     storeAppendPrintf(sentry, "no.private_key\t%d\n",
        store_check_cachable_hist.no.private_key);
     storeAppendPrintf(sentry, "no.too_many_open_files\t%d\n",
@@ -610,33 +811,37 @@ storeCheckCachableStats(StoreEntry * sentry)
        store_check_cachable_hist.yes.Default);
 }
 
-/* Complete transfer into the local cache.  */
 void
-storeComplete(StoreEntry * e)
+StoreEntry::complete()
 {
-    debug(20, 3) ("storeComplete: '%s'\n", storeKeyText(e->key));
-    if (e->store_status != STORE_PENDING) {
+    debug(20, 3) ("storeComplete: '%s'\n", getMD5Text());
+    if (store_status != STORE_PENDING) {
        /*
         * if we're not STORE_PENDING, then probably we got aborted
         * and there should be NO clients on this entry
         */
-       assert(EBIT_TEST(e->flags, ENTRY_ABORTED));
-       assert(e->mem_obj->nclients == 0);
+       assert(EBIT_TEST(flags, ENTRY_ABORTED));
+       assert(mem_obj->nclients == 0);
        return;
     }
-    e->mem_obj->object_sz = e->mem_obj->inmem_hi;
-    e->store_status = STORE_OK;
-    assert(e->mem_status == NOT_IN_MEMORY);
-    if (!storeEntryValidLength(e)) {
-       EBIT_SET(e->flags, ENTRY_BAD_LENGTH);
-       storeReleaseRequest(e);
+    mem_obj->object_sz = mem_obj->endOffset();
+    store_status = STORE_OK;
+    assert(mem_status == NOT_IN_MEMORY);
+    if (!validLength()) {
+       EBIT_SET(flags, ENTRY_BAD_LENGTH);
+       storeReleaseRequest(this);
     }
 #if USE_CACHE_DIGESTS
-    if (e->mem_obj->request)
-       e->mem_obj->request->hier.store_complete_stop = current_time;
+    if (mem_obj->request)
+       mem_obj->request->hier.store_complete_stop = current_time;
 #endif
-    InvokeHandlers(e);
-    storeSwapOut(e);
+    /*
+     * We used to call InvokeHandlers, then storeSwapOut.  However,
+     * Madhukar Reddy <myreddy@persistence.com> reported that
+     * responses without content length would sometimes get released
+     * in client_side, thinking that the response is incomplete.
+     */
+    InvokeHandlers(this);
 }
 
 /*
@@ -647,10 +852,11 @@ storeComplete(StoreEntry * e)
 void
 storeAbort(StoreEntry * e)
 {
+    statCounter.aborted_requests++;
     MemObject *mem = e->mem_obj;
     assert(e->store_status == STORE_PENDING);
     assert(mem != NULL);
-    debug(20, 6) ("storeAbort: %s\n", storeKeyText(e->key));
+    debug(20, 6) ("storeAbort: %s\n", e->getMD5Text());
     storeLockObject(e);                /* lock while aborting */
     storeNegativeCache(e);
     storeReleaseRequest(e);
@@ -661,7 +867,11 @@ storeAbort(StoreEntry * e)
      * We assign an object length here.  The only other place we assign
      * the object length is in storeComplete()
      */
-    mem->object_sz = mem->inmem_hi;
+    /* RBC: What do we need an object length for? we've just aborted the
+     * request, the request is private and negatively cached. Surely
+     * the object length is inappropriate to set.
+     */
+    mem->object_sz = mem->endOffset();
     /* Notify the server side */
     if (mem->abort.callback) {
        eventAdd("mem->abort.callback",
@@ -672,13 +882,13 @@ storeAbort(StoreEntry * e)
        mem->abort.callback = NULL;
        mem->abort.data = NULL;
     }
+    /* XXX Should we reverse these two, so that there is no 
+     * unneeded disk swapping triggered? 
+     */
     /* Notify the client side */
     InvokeHandlers(e);
-    /* Do we need to close the swapout file? */
-    /* Not if we never started swapping out */
-    if (e->swap_file_number > -1) {
-       storeSwapOutFileClose(e);
-    }
+    /* Close any swapout file */
+    storeSwapOutFileClose(e);
     storeUnlockObject(e);      /* unlock */
 }
 
@@ -689,77 +899,24 @@ storeGetMemSpace(int size)
     StoreEntry *e = NULL;
     int released = 0;
     static time_t last_check = 0;
-    int pages_needed;
-    int locked = 0;
-#if !HEAP_REPLACEMENT
-    dlink_node *head;
-    dlink_node *m;
-    dlink_node *prev = NULL;
-#else
-    heap_key age;
-    heap_key min_age = 0.0;
-    link_list *locked_entries = NULL;
-#endif
+    size_t pages_needed;
+    RemovalPurgeWalker *walker;
     if (squid_curtime == last_check)
        return;
     last_check = squid_curtime;
     pages_needed = (size / SM_PAGE_SIZE) + 1;
-    if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
-       return;
-    if (store_dirs_rebuilding)
+    if (mem_node::InUseCount() + pages_needed < store_pages_max)
        return;
     debug(20, 2) ("storeGetMemSpace: Starting, need %d pages\n", pages_needed);
-#if HEAP_REPLACEMENT
-    while (heap_nodes(inmem_heap) > 0) {
-       age = heap_peepminkey(inmem_heap);
-       e = heap_extractmin(inmem_heap);
-       e->mem_obj->node = NULL;        /* no longer in the heap */
-       if (storeEntryLocked(e)) {
-           locked++;
-           debug(20, 5) ("storeGetMemSpace: locked key %s\n",
-               storeKeyText(e->key));
-           linklistPush(&locked_entries, e);
-           continue;
-       }
-       released++;
-       debug(20, 3) ("Released memory object with key %f size %d refs %d url %s\n",
-           age, e->swap_file_sz, e->refcount, e->mem_obj->url);
-       min_age = age;
+    /* XXX what to set as max_scan here? */
+    walker = mem_policy->PurgeInit(mem_policy, 100000);
+    while ((e = walker->Next(walker))) {
        storePurgeMem(e);
-       if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
-           break;
-    }
-    /*
-     * Increase the heap age factor.
-     */
-    if (min_age > 0)
-       inmem_heap->age = min_age;
-    /*
-     * Reinsert all bumped locked entries back into heap...
-     */
-    while ((e = linklistShift(&locked_entries)))
-       e->mem_obj->node = heap_insert(inmem_heap, e);
-#else
-    head = inmem_list.head;
-    for (m = inmem_list.tail; m; m = prev) {
-       if (m == head)
-           break;
-       prev = m->prev;
-       e = m->data;
-       if (storeEntryLocked(e)) {
-           locked++;
-           dlinkDelete(m, &inmem_list);
-           dlinkAdd(e, m, &inmem_list);
-           continue;
-       }
        released++;
-       storePurgeMem(e);
-       if (memInUse(MEM_STMEM_BUF) + pages_needed < store_pages_max)
+       if (mem_node::InUseCount() + pages_needed < store_pages_max)
            break;
     }
-#endif
-    debug(20, 3) ("storeGetMemSpace: released %d/%d locked %d\n",
-       released, hot_obj_count, locked);
+    walker->Done(walker);
     debug(20, 3) ("storeGetMemSpace stats:\n");
     debug(20, 3) ("  %6d HOT objects\n", hot_obj_count);
     debug(20, 3) ("  %6d were released\n", released);
@@ -772,219 +929,88 @@ storeGetMemSpace(int size)
 /*
  * This routine is to be called by main loop in main.c.
  * It removes expired objects on only one bucket for each time called.
- * returns the number of objects removed
  *
  * This should get called 1/s from main().
  */
 void
 storeMaintainSwapSpace(void *datanotused)
 {
-    StoreEntry *e = NULL;
-    int scanned = 0;
-    int locked = 0;
-    int expired = 0;
-    int max_scan;
-    int max_remove;
     int i;
-    int j;
-    static int ndir = 0;
-    double f;
+    SwapDir *SD;
     static time_t last_warn_time = 0;
-#if !HEAP_REPLACEMENT
-    SwapDir *sd;
-#else
-    heap_key age;
-    heap_key min_age = 0.0;
-    link_list *locked_entries = NULL;
-#if HEAP_REPLACEMENT_DEBUG
-    if (!verify_heap_property(store_heap)) {
-       debug(20, 1) ("Heap property violated!\n");
-    }
-#endif
-#endif
-    /* We can't delete objects while rebuilding swap */
-    if (store_dirs_rebuilding) {
-       eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
-       return;
-    } else {
-       f = (double) (store_swap_size - store_swap_low) / (store_swap_high - store_swap_low);
-       f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;
-       max_scan = (int) (f * 400.0 + 100.0);
-       if ((max_remove = stat5minClientRequests()) < 10)
-           max_remove = 10;
-       eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0 - f, 1);
-    }
-    debug(20, 3) ("storeMaintainSwapSpace: f=%f, max_scan=%d, max_remove=%d\n",
-       f, max_scan, max_remove);
-#if HEAP_REPLACEMENT
-    while (heap_nodes(store_heap) > 0) {
-       if (store_swap_size < store_swap_low)
-           break;
-       if (expired >= max_remove)
-           break;
-       if (scanned >= max_scan)
-           break;
-       age = heap_peepminkey(store_heap);
-       e = heap_extractmin(store_heap);
-       e->node = NULL;         /* no longer in the heap */
-       scanned++;
-       if (storeEntryLocked(e)) {
-           /*
-            * Entry is in use ... put it in a linked list to ignore it.
-            */
-           if (!EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-               /*
-                * If this was a "SPECIAL" do not add it back into the heap.
-                * It will always be "SPECIAL" and therefore never removed.
-                */
-               debug(20, 4) ("storeMaintainSwapSpace: locked url %s\n",
-                   (e->mem_obj && e->mem_obj->url) ? e->mem_obj->url : storeKeyText(e->key));
-               linklistPush(&locked_entries, e);
-           }
-           locked++;
-           continue;
-       } else if (storeCheckExpired(e)) {
-           /*
-            * Note: This will not check the reference age ifdef
-            * HEAP_REPLACEMENT, but it does some other useful
-            * checks...
-            */
-           expired++;
-           debug(20, 3) ("Released store object age %f size %d refs %d key %s\n",
-               age, e->swap_file_sz, e->refcount, storeKeyText(e->key));
-           min_age = age;
-           storeRelease(e);
-       } else {
-           /*
-            * Did not expire the object so we need to add it back
-            * into the heap!
-            */
-           debug(20, 5) ("storeMaintainSwapSpace: non-expired %s\n",
-               storeKeyText(e->key));
-           linklistPush(&locked_entries, e);
-           continue;
-       }
-       if (store_swap_size < store_swap_low)
-           break;
-       else if (expired >= max_remove)
-           break;
-       else if (scanned >= max_scan)
-           break;
-    }
-    /*
-     * Bump the heap age factor.
-     */
-    if (min_age > 0.0)
-       store_heap->age = min_age;
-    /*
-     * Reinsert all bumped locked entries back into heap...
-     */
-    while ((e = linklistShift(&locked_entries)))
-       e->node = heap_insert(store_heap, e);
-#else
+
+    PROF_start(storeMaintainSwapSpace);
+    /* walk each fs */
     for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-       sd = &Config.cacheSwap.swapDirs[i];
-       sd->lru_walker = sd->lru_list.tail;
+       /* call the maintain function .. */
+       SD = INDEXSD(i);
+       /* XXX FixMe: This should be done "in parallell" on the different
+        * cache_dirs, not one at a time.
+        */
+       SD->maintainfs();
     }
-    do {
-       j = 0;
-       for (i = 0; i < Config.cacheSwap.n_configured; i++) {
-           if (ndir >= Config.cacheSwap.n_configured)
-               ndir = ndir % Config.cacheSwap.n_configured;
-           sd = &Config.cacheSwap.swapDirs[ndir++];
-           if (sd->cur_size < sd->high_size)
-               continue;
-           if (NULL == sd->lru_walker)
-               continue;
-           e = sd->lru_walker->data;
-           sd->lru_walker = sd->lru_walker->prev;
-           j++;
-           scanned++;
-           sd->scanned++;
-           if (storeEntryLocked(e)) {
-               /*
-                * If there is a locked entry at the tail of the LRU list,
-                * move it to the beginning to get it out of the way.
-                * Theoretically, we might have all locked objects at the
-                * tail, and then we'll never remove anything here and the
-                * LRU age will go to zero.
-                */
-               if (memInUse(MEM_STOREENTRY) > max_scan) {
-                   storeDirLRUDelete(e);
-                   if (!EBIT_TEST(e->flags, ENTRY_SPECIAL))
-                       storeDirLRUAdd(e);
-               }
-               locked++;
-           } else if (storeCheckExpired(e)) {
-               expired++;
-               sd->removals++;
-               storeRelease(e);
-           }
-           if (expired >= max_remove)
-               break;
-           if (scanned >= max_scan)
-               break;
+    if (store_swap_size > Config.Swap.maxSize) {
+       if (squid_curtime - last_warn_time > 10) {
+           debug(20, 0) ("WARNING: Disk space over limit: %ld KB > %ld KB\n",
+               (long int) store_swap_size, (long int) Config.Swap.maxSize);
+           last_warn_time = squid_curtime;
        }
-    } while (j > 0 && expired < max_remove && scanned < max_scan);
-#endif
-    debug(20, (expired ? 2 : 3)) ("storeMaintainSwapSpace: scanned %d/%d removed %d/%d locked %d f=%.03f\n",
-       scanned, max_scan, expired, max_remove, locked, f);
-    debug(20, 3) ("storeMaintainSwapSpace stats:\n");
-    debug(20, 3) ("  %6d objects\n", memInUse(MEM_STOREENTRY));
-    debug(20, 3) ("  %6d were scanned\n", scanned);
-    debug(20, 3) ("  %6d were locked\n", locked);
-    debug(20, 3) ("  %6d were expired\n", expired);
-    if (store_swap_size < Config.Swap.maxSize)
-       return;
-    if (squid_curtime - last_warn_time < 10)
-       return;
-    debug(20, 0) ("WARNING: Disk space over limit: %d KB > %d KB\n",
-       store_swap_size, Config.Swap.maxSize);
-    last_warn_time = squid_curtime;
+    }
+    /* Reregister a maintain event .. */
+    eventAdd("MaintainSwapSpace", storeMaintainSwapSpace, NULL, 1.0, 1);
+    PROF_stop(storeMaintainSwapSpace);
 }
 
 
 /* release an object from a cache */
-/* return number of objects released. */
 void
 storeRelease(StoreEntry * e)
 {
-    debug(20, 3) ("storeRelease: Releasing: '%s'\n", storeKeyText(e->key));
+    PROF_start(storeRelease);
+    debug(20, 3) ("storeRelease: Releasing: '%s'\n", e->getMD5Text());
     /* If, for any reason we can't discard this object because of an
      * outstanding request, mark it for pending release */
     if (storeEntryLocked(e)) {
        storeExpireNow(e);
        debug(20, 3) ("storeRelease: Only setting RELEASE_REQUEST bit\n");
        storeReleaseRequest(e);
+       PROF_stop(storeRelease);
        return;
     }
-    if (store_dirs_rebuilding && e->swap_file_number > -1) {
+    if (store_dirs_rebuilding && e->swap_filen > -1) {
        storeSetPrivateKey(e);
-       if (e->mem_obj) {
-           storeSetMemStatus(e, NOT_IN_MEMORY);
+       if (e->mem_obj)
            destroy_MemObject(e);
+       if (e->swap_filen > -1) {
+           /*
+            * Fake a call to storeLockObject().  When rebuilding is done,
+            * we'll just call storeUnlockObject() on these.
+            */
+           e->lock_count++;
+           EBIT_SET(e->flags, RELEASE_REQUEST);
+           stackPush(&LateReleaseStack, e);
+           PROF_stop(storeRelease);
+           return;
+       } else {
+           destroyStoreEntry(e);
        }
-       /*
-        * Fake a call to storeLockObject().  When rebuilding is done,
-        * we'll just call storeUnlockObject() on these.
-        */
-       e->lock_count++;
-       EBIT_SET(e->flags, RELEASE_REQUEST);
-       stackPush(&LateReleaseStack, e);
-       return;
     }
     storeLog(STORE_LOG_RELEASE, e);
-    if (e->swap_file_number > -1) {
-       storeUnlink(e->swap_file_number);
+    if (e->swap_filen > -1) {
+       storeUnlink(e);
        if (e->swap_status == SWAPOUT_DONE)
            if (EBIT_TEST(e->flags, ENTRY_VALIDATED))
-               storeDirUpdateSwapSize(e->swap_file_number, e->swap_file_sz, -1);
+               storeDirUpdateSwapSize(INDEXSD(e->swap_dirn), e->swap_file_sz, -1);
        if (!EBIT_TEST(e->flags, KEY_PRIVATE))
            storeDirSwapLog(e, SWAP_LOG_DEL);
+#if 0
+       /* From 2.4. I think we do this in storeUnlink? */
        storeSwapFileNumberSet(e, -1);
+#endif
     }
     storeSetMemStatus(e, NOT_IN_MEMORY);
-    destroy_StoreEntry(e);
+    destroyStoreEntry(e);
+    PROF_stop(storeRelease);
 }
 
 static void
@@ -998,7 +1024,7 @@ storeLateRelease(void *unused)
        return;
     }
     for (i = 0; i < 10; i++) {
-       e = stackPop(&LateReleaseStack);
+       e = static_cast<StoreEntry*>(stackPop(&LateReleaseStack));
        if (e == NULL) {
            /* done! */
            debug(20, 1) ("storeLateRelease: released %d objects\n", n);
@@ -1011,7 +1037,7 @@ storeLateRelease(void *unused)
 }
 
 /* return 1 if a store entry is locked */
-static int
+int
 storeEntryLocked(const StoreEntry * e)
 {
     if (e->lock_count)
@@ -1029,71 +1055,67 @@ storeEntryLocked(const StoreEntry * e)
     return 0;
 }
 
-static int
-storeEntryValidLength(const StoreEntry * e)
+bool
+StoreEntry::validLength() const
 {
     int diff;
     const HttpReply *reply;
-    assert(e->mem_obj != NULL);
-    reply = e->mem_obj->reply;
-    debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", storeKeyText(e->key));
+    assert(mem_obj != NULL);
+    reply = getReply();
+    debug(20, 3) ("storeEntryValidLength: Checking '%s'\n", getMD5Text());
     debug(20, 5) ("storeEntryValidLength:     object_len = %d\n",
-       objectLen(e));
+       objectLen(this));
     debug(20, 5) ("storeEntryValidLength:         hdr_sz = %d\n",
        reply->hdr_sz);
     debug(20, 5) ("storeEntryValidLength: content_length = %d\n",
        reply->content_length);
     if (reply->content_length < 0) {
        debug(20, 5) ("storeEntryValidLength: Unspecified content length: %s\n",
-           storeKeyText(e->key));
+           getMD5Text());
        return 1;
     }
     if (reply->hdr_sz == 0) {
        debug(20, 5) ("storeEntryValidLength: Zero header size: %s\n",
-           storeKeyText(e->key));
+           getMD5Text());
        return 1;
     }
-    if (e->mem_obj->method == METHOD_HEAD) {
+    if (mem_obj->method == METHOD_HEAD) {
        debug(20, 5) ("storeEntryValidLength: HEAD request: %s\n",
-           storeKeyText(e->key));
+           getMD5Text());
        return 1;
     }
     if (reply->sline.status == HTTP_NOT_MODIFIED)
        return 1;
     if (reply->sline.status == HTTP_NO_CONTENT)
        return 1;
-    diff = reply->hdr_sz + reply->content_length - objectLen(e);
+    diff = reply->hdr_sz + reply->content_length - objectLen(this);
     if (diff == 0)
        return 1;
     debug(20, 3) ("storeEntryValidLength: %d bytes too %s; '%s'\n",
        diff < 0 ? -diff : diff,
        diff < 0 ? "big" : "small",
-       storeKeyText(e->key));
+       getMD5Text());
     return 0;
 }
 
 static void
 storeInitHashValues(void)
 {
-    int i;
+    long int i;
     /* Calculate size of hash table (maximum currently 64k buckets).  */
     i = Config.Swap.maxSize / Config.Store.avgObjectSize;
-    debug(20, 1) ("Swap maxSize %d KB, estimated %d objects\n",
-       Config.Swap.maxSize, i);
+    debug(20, 1) ("Swap maxSize %ld KB, estimated %ld objects\n",
+       (long int) Config.Swap.maxSize, i);
     i /= Config.Store.objectsPerBucket;
-    debug(20, 1) ("Target number of buckets: %d\n", i);
+    debug(20, 1) ("Target number of buckets: %ld\n", i);
     /* ideally the full scan period should be configurable, for the
      * moment it remains at approximately 24 hours.  */
     store_hash_buckets = storeKeyHashBuckets(i);
     debug(20, 1) ("Using %d Store buckets\n", store_hash_buckets);
-    debug(20, 1) ("Max Mem  size: %d KB\n", Config.memMaxSize >> 10);
-    debug(20, 1) ("Max Swap size: %d KB\n", Config.Swap.maxSize);
+    debug(20, 1) ("Max Mem  size: %ld KB\n", (long int) Config.memMaxSize >> 10);
+    debug(20, 1) ("Max Swap size: %ld KB\n", (long int) Config.Swap.maxSize);
 }
 
-#if HEAP_REPLACEMENT
-#include "store_heap_replacement.c"
-#endif
-
 void
 storeInit(void)
 {
@@ -1101,40 +1123,9 @@ storeInit(void)
     storeInitHashValues();
     store_table = hash_create(storeKeyHashCmp,
        store_hash_buckets, storeKeyHashHash);
+    mem_policy = createRemovalPolicy(Config.memPolicy);
     storeDigestInit();
     storeLogOpen();
-#if HEAP_REPLACEMENT
-    /*
-     * Create new heaps with cache replacement policies attached to them.
-     * The cache replacement policy is specified as either GDSF or LFUDA in
-     * the squid.conf configuration file.  Note that the replacement policy
-     * applies only to the disk replacement algorithm.  Memory replacement
-     * always uses GDSF since we want to maximize object hit rate.
-     */
-    inmem_heap = new_heap(1000, HeapKeyGen_StoreEntry_GDSF);
-    if (Config.replPolicy) {
-       if (tolower(Config.replPolicy[0]) == 'g') {
-           debug(20, 1) ("Using GDSF disk replacement policy\n");
-           store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-       } else if (tolower(Config.replPolicy[0]) == 'l') {
-           if (tolower(Config.replPolicy[1]) == 'f') {
-               debug(20, 1) ("Using LFUDA disk replacement policy\n");
-               store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LFUDA);
-           } else if (tolower(Config.replPolicy[1]) == 'r') {
-               debug(20, 1) ("Using LRU heap disk replacement policy\n");
-               store_heap = new_heap(10000, HeapKeyGen_StoreEntry_LRU);
-           }
-       } else {
-           debug(20, 1) ("Unrecognized replacement_policy; using GDSF\n");
-           store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-       }
-    } else {
-       debug(20, 1) ("Using default disk replacement policy (GDSF)\n");
-       store_heap = new_heap(10000, HeapKeyGen_StoreEntry_GDSF);
-    }
-#else
-    inmem_list.head = inmem_list.tail = NULL;
-#endif
     stackInit(&LateReleaseStack);
     eventAdd("storeLateRelease", storeLateRelease, NULL, 1.0, 1);
     storeDirInit();
@@ -1145,6 +1136,9 @@ storeInit(void)
     cachemgrRegister("store_check_cachable_stats",
        "storeCheckCachable() Stats",
        storeCheckCachableStats, 0, 1);
+    cachemgrRegister("store_io",
+       "Store IO Interface Stats",
+       storeIOStats, 0, 1);
 }
 
 void
@@ -1168,48 +1162,18 @@ storeKeepInMemory(const StoreEntry * e)
     return mem->inmem_lo == 0;
 }
 
-static int
-storeCheckExpired(const StoreEntry * e)
+int
+storeCheckNegativeHit(StoreEntry * e)
 {
-    if (storeEntryLocked(e))
+    if (!EBIT_TEST(e->flags, ENTRY_NEGCACHED))
+       return 0;
+    if (e->expires <= squid_curtime)
+       return 0;
+    if (e->store_status != STORE_OK)
        return 0;
-    if (EBIT_TEST(e->flags, RELEASE_REQUEST))
-       return 1;
-    if (EBIT_TEST(e->flags, ENTRY_NEGCACHED) && squid_curtime >= e->expires)
-       return 1;
     return 1;
 }
 
-#if !HEAP_REPLACEMENT
-/*
- * storeExpiredReferenceAge
- *
- * The LRU age is scaled exponentially between 1 minute and
- * Config.referenceAge , when store_swap_low < store_swap_size <
- * store_swap_high.  This keeps store_swap_size within the low and high
- * water marks.  If the cache is very busy then store_swap_size stays
- * closer to the low water mark, if it is not busy, then it will stay
- * near the high water mark.  The LRU age value can be examined on the
- * cachemgr 'info' page.
- */
-time_t
-storeExpiredReferenceAge(void)
-{
-    double x;
-    double z;
-    time_t age;
-    x = (double) (store_swap_high - store_swap_size) / (store_swap_high - store_swap_low);
-    x = x < 0.0 ? 0.0 : x > 1.0 ? 1.0 : x;
-    z = pow((double) (Config.referenceAge / 60), x);
-    age = (time_t) (z * 60.0);
-    if (age < 60)
-       age = 60;
-    else if (age > 31536000)
-       age = 31536000;
-    return age;
-}
-#endif
-
 void
 storeNegativeCache(StoreEntry * e)
 {
@@ -1220,7 +1184,7 @@ storeNegativeCache(StoreEntry * e)
 void
 storeFreeMemory(void)
 {
-    hashFreeItems(store_table, destroy_StoreEntry);
+    hashFreeItems(store_table, destroyStoreEntry);
     hashFreeMemory(store_table);
     store_table = NULL;
 #if USE_CACHE_DIGESTS
@@ -1254,11 +1218,29 @@ storeEntryValidToSend(StoreEntry * e)
 void
 storeTimestampsSet(StoreEntry * entry)
 {
-    const HttpReply *reply = entry->mem_obj->reply;
+    const HttpReply *reply = entry->getReply();
     time_t served_date = reply->date;
+    int age = httpHeaderGetInt(&reply->header, HDR_AGE);
+    /*
+     * The timestamp calculations below tries to mimic the properties
+     * of the age calculation in RFC2616 section 13.2.3. The implementaion
+     * isn't complete, and the most notable exception from the RFC is that
+     * this does not account for response_delay, but it probably does
+     * not matter much as this is calculated immediately when the headers
+     * are received, not when the whole response has been received.
+     */
     /* make sure that 0 <= served_date <= squid_curtime */
     if (served_date < 0 || served_date > squid_curtime)
        served_date = squid_curtime;
+    /*
+     * Compensate with Age header if origin server clock is ahead
+     * of us and there is a cache in between us and the origin
+     * server.  But DONT compensate if the age value is larger than
+     * squid_curtime because it results in a negative served_date.
+     */
+    if (age > squid_curtime - served_date)
+       if (squid_curtime > age)
+           served_date = squid_curtime - age;
     entry->expires = reply->expires;
     entry->lastmod = reply->last_modified;
     entry->timestamp = served_date;
@@ -1282,39 +1264,10 @@ storeUnregisterAbort(StoreEntry * e)
     mem->abort.callback = NULL;
 }
 
-void
-storeMemObjectDump(MemObject * mem)
-{
-    debug(20, 1) ("MemObject->data.head: %p\n",
-       mem->data_hdr.head);
-    debug(20, 1) ("MemObject->data.tail: %p\n",
-       mem->data_hdr.tail);
-    debug(20, 1) ("MemObject->data.origin_offset: %d\n",
-       mem->data_hdr.origin_offset);
-    debug(20, 1) ("MemObject->start_ping: %d.%06d\n",
-       (int) mem->start_ping.tv_sec,
-       (int) mem->start_ping.tv_usec);
-    debug(20, 1) ("MemObject->inmem_hi: %d\n",
-       (int) mem->inmem_hi);
-    debug(20, 1) ("MemObject->inmem_lo: %d\n",
-       (int) mem->inmem_lo);
-    debug(20, 1) ("MemObject->clients: %p\n",
-       mem->clients);
-    debug(20, 1) ("MemObject->nclients: %d\n",
-       mem->nclients);
-    debug(20, 1) ("MemObject->reply: %p\n",
-       mem->reply);
-    debug(20, 1) ("MemObject->request: %p\n",
-       mem->request);
-    debug(20, 1) ("MemObject->log_url: %p %s\n",
-       mem->log_url,
-       checkNullString(mem->log_url));
-}
-
 void
 storeEntryDump(const StoreEntry * e, int l)
 {
-    debug(20, l) ("StoreEntry->key: %s\n", storeKeyText(e->key));
+    debug(20, l) ("StoreEntry->key: %s\n", e->getMD5Text());
     debug(20, l) ("StoreEntry->next: %p\n", e->next);
     debug(20, l) ("StoreEntry->mem_obj: %p\n", e->mem_obj);
     debug(20, l) ("StoreEntry->timestamp: %d\n", (int) e->timestamp);
@@ -1324,7 +1277,8 @@ storeEntryDump(const StoreEntry * e, int l)
     debug(20, l) ("StoreEntry->swap_file_sz: %d\n", (int) e->swap_file_sz);
     debug(20, l) ("StoreEntry->refcount: %d\n", e->refcount);
     debug(20, l) ("StoreEntry->flags: %s\n", storeEntryFlags(e));
-    debug(20, l) ("StoreEntry->swap_file_number: %d\n", (int) e->swap_file_number);
+    debug(20, l) ("StoreEntry->swap_dirn: %d\n", (int) e->swap_dirn);
+    debug(20, l) ("StoreEntry->swap_filen: %d\n", (int) e->swap_filen);
     debug(20, l) ("StoreEntry->lock_count: %d\n", (int) e->lock_count);
     debug(20, l) ("StoreEntry->mem_status: %d\n", (int) e->mem_status);
     debug(20, l) ("StoreEntry->ping_status: %d\n", (int) e->ping_status);
@@ -1336,7 +1290,7 @@ storeEntryDump(const StoreEntry * e, int l)
  * NOTE, this function assumes only two mem states
  */
 void
-storeSetMemStatus(StoreEntry * e, int new_status)
+storeSetMemStatus(StoreEntry * e, mem_status_t new_status)
 {
     MemObject *mem = e->mem_obj;
     if (new_status == e->mem_status)
@@ -1344,35 +1298,24 @@ storeSetMemStatus(StoreEntry * e, int new_status)
     assert(mem != NULL);
     if (new_status == IN_MEMORY) {
        assert(mem->inmem_lo == 0);
-#if HEAP_REPLACEMENT
-       if (mem->node == NULL) {
-           if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
-               debug(20, 4) ("storeSetMemStatus: not inserting special %s\n",
-                   mem->url);
-           } else {
-               mem->node = heap_insert(inmem_heap, e);
-               debug(20, 4) ("storeSetMemStatus: inserted mem node %p\n",
-                   mem->node);
-           }
+       if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+           debug(20, 4) ("storeSetMemStatus: not inserting special %s into policy\n",
+               mem->url);
+       } else {
+           mem_policy->Add(mem_policy, e, &mem->repl);
+           debug(20, 4) ("storeSetMemStatus: inserted mem node %s\n",
+               mem->url);
        }
-#else
-       dlinkAdd(e, &mem->lru, &inmem_list);
-#endif
        hot_obj_count++;
     } else {
-#if HEAP_REPLACEMENT
-       /*
-        * It's being removed from the memory heap; is it already gone?
-        */
-       if (mem->node) {
-           heap_delete(inmem_heap, mem->node);
-           debug(20, 4) ("storeSetMemStatus: deleted mem node %p\n",
-               mem->node);
-           mem->node = NULL;
+       if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
+           debug(20, 4) ("storeSetMemStatus: special entry %s\n",
+               mem->url);
+       } else {
+           mem_policy->Remove(mem_policy, e, &mem->repl);
+           debug(20, 4) ("storeSetMemStatus: removed mem node %s\n",
+               mem->url);
        }
-#else
-       dlinkDelete(&mem->lru, &inmem_list);
-#endif
        hot_obj_count--;
     }
     e->mem_status = new_status;
@@ -1394,7 +1337,7 @@ storeCreateMemObject(StoreEntry * e, const char *url, const char *log_url)
 {
     if (e->mem_obj)
        return;
-    e->mem_obj = new_MemObject(url, log_url);
+    e->mem_obj = new MemObject(url, log_url);
 }
 
 /* this just sets DELAY_SENDING */
@@ -1410,10 +1353,9 @@ storeBufferFlush(StoreEntry * e)
 {
     EBIT_CLR(e->flags, DELAY_SENDING);
     InvokeHandlers(e);
-    storeSwapOut(e);
 }
 
-int
+ssize_t
 objectLen(const StoreEntry * e)
 {
     assert(e->mem_obj != NULL);
@@ -1424,44 +1366,117 @@ int
 contentLen(const StoreEntry * e)
 {
     assert(e->mem_obj != NULL);
-    assert(e->mem_obj->reply != NULL);
-    return e->mem_obj->object_sz - e->mem_obj->reply->hdr_sz;
+    assert(e->getReply() != NULL);
+    return objectLen(e) - e->getReply()->hdr_sz;
+
 }
 
-HttpReply *
-storeEntryReply(StoreEntry * e)
+HttpReply const *
+StoreEntry::getReply () const
 {
-    if (NULL == e)
-       return NULL;
-    if (NULL == e->mem_obj)
+    if (NULL == mem_obj)
        return NULL;
-    return e->mem_obj->reply;
+    return mem_obj->getReply();
 }
 
 void
 storeEntryReset(StoreEntry * e)
 {
     MemObject *mem = e->mem_obj;
+    assert (mem);
     debug(20, 3) ("storeEntryReset: %s\n", storeUrl(e));
-    assert(mem->swapout.sio == NULL);
-    stmemFree(&mem->data_hdr);
-    mem->inmem_hi = mem->inmem_lo = 0;
-    httpReplyDestroy(mem->reply);
-    mem->reply = httpReplyCreate();
+    mem->reset();
+    httpReplyReset((HttpReply *)e->getReply());
     e->expires = e->lastmod = e->timestamp = -1;
 }
 
-#if HEAP_REPLACEMENT
+/*
+ * storeFsInit
+ *
+ * This routine calls the SETUP routine for each fs type.
+ * I don't know where the best place for this is, and I'm not going to shuffle
+ * around large chunks of code right now (that can be done once its working.)
+ */
 void
-storeHeapPositionUpdate(StoreEntry * e)
+storeFsInit(void)
 {
-    if (e->node)
-       heap_update(store_heap, e->node, e);
-    if (e->mem_obj && e->mem_obj->node)
-       heap_update(inmem_heap, e->mem_obj->node, e);
+    storeReplSetup();
+    storeFsSetup();
+}
+
+
+/*
+ * similar to above, but is called when a graceful shutdown is to occur
+ * of each fs module.
+ */
+void
+storeFsDone(void)
+{
+    int i = 0;
+
+    while (storefs_list[i].typestr != NULL) {
+       storefs_list[i].donefunc();
+       i++;
+    }
+}
+
+/*
+ * called to add another store fs module
+ * RBC: doesn't belong here. Move IT.
+ */
+void
+StoreEntry::FsAdd(const char *type, STSETUP * setup)
+{
+    int i;
+    /* find the number of currently known storefs types */
+    for (i = 0; storefs_list && storefs_list[i].typestr; i++) {
+       assert(strcmp(storefs_list[i].typestr, type) != 0);
+    }
+    /* add the new type */
+    storefs_list = static_cast<storefs_entry_t *>(xrealloc(storefs_list, (i + 2) * sizeof(storefs_entry_t)));
+    memset(&storefs_list[i + 1], 0, sizeof(storefs_entry_t));
+    storefs_list[i].typestr = type;
+    /* Call the FS to set up capabilities and initialize the FS driver */
+    setup(&storefs_list[i]);
+}
+
+/*
+ * called to add another store removal policy module
+ */
+void
+storeReplAdd(const char *type, REMOVALPOLICYCREATE * create)
+{
+    int i;
+    /* find the number of currently known repl types */
+    for (i = 0; storerepl_list && storerepl_list[i].typestr; i++) {
+       assert(strcmp(storerepl_list[i].typestr, type) != 0);
+    }
+    /* add the new type */
+    storerepl_list = static_cast<storerepl_entry_t *>(xrealloc(storerepl_list, (i + 2) * sizeof(storerepl_entry_t)));
+    memset(&storerepl_list[i + 1], 0, sizeof(storerepl_entry_t));
+    storerepl_list[i].typestr = type;
+    storerepl_list[i].create = create;
+}
+
+/*
+ * Create a removal policy instance
+ */
+RemovalPolicy *
+createRemovalPolicy(RemovalPolicySettings * settings)
+{
+    storerepl_entry_t *r;
+    for (r = storerepl_list; r && r->typestr; r++) {
+       if (strcmp(r->typestr, settings->type) == 0)
+           return r->create(settings->args);
+    }
+    debug(20, 1) ("ERROR: Unknown policy %s\n", settings->type);
+    debug(20, 1) ("ERROR: Be sure to have set cache_replacement_policy\n");
+    debug(20, 1) ("ERROR:   and memory_replacement_policy in squid.conf!\n");
+    fatalf("ERROR: Unknown policy %s\n", settings->type);
+    return NULL;               /* NOTREACHED */
 }
-#endif
 
+#if 0
 void
 storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
 {
@@ -1478,3 +1493,111 @@ storeSwapFileNumberSet(StoreEntry * e, sfileno filn)
        storeDirLRUAdd(e);
     }
 }
+#endif
+
+/* Replace a store entry with
+ * a new reply. This eats the reply.
+ */
+void
+storeEntryReplaceObject(StoreEntry * e, HttpReply * rep)
+{
+    MemObject * const mem = e->mem_obj;
+    HttpReply *myrep;
+    Packer p;
+    debug(20, 3) ("storeEntryReplaceObject: %s\n", storeUrl(e));
+    if (!mem) {
+       debug (20,0)("Attempt to replace object with no in-memory representation\n");
+       return;
+    }
+    /* TODO: check that there is at most 1 store client ? */
+    myrep = (HttpReply *)e->getReply(); /* we are allowed to do this */
+    /* move info to the mem_obj->reply */
+    httpReplyAbsorb(myrep, rep);
+
+    /* TODO: when we store headers serparately remove the header portion */
+    /* TODO: mark the length of the headers ? */
+    /* We ONLY want the headers */
+    packerToStoreInit(&p, e);
+    assert (e->isEmpty());
+    httpReplyPackHeadersInto(e->getReply(), &p);
+    myrep->hdr_sz = e->mem_obj->endOffset();
+    httpBodyPackInto(&e->getReply()->body, &p);
+    packerClean(&p);
+}
+
+char const *
+StoreEntry::getSerialisedMetaData()
+{
+    StoreMeta *tlv_list = storeSwapMetaBuild(this);
+    int swap_hdr_sz;
+    char *result = storeSwapMetaPack(tlv_list, &swap_hdr_sz);
+    storeSwapTLVFree(tlv_list);
+    assert (swap_hdr_sz >= 0);
+    mem_obj->swap_hdr_sz = (size_t) swap_hdr_sz;
+    return result;
+}
+
+bool
+StoreEntry::swapoutPossible()
+{
+    /* should we swap something out to disk? */
+    debug(20, 7) ("storeSwapOut: %s\n", storeUrl(this));
+    debug(20, 7) ("storeSwapOut: store_status = %s\n",
+       storeStatusStr[store_status]);
+    if (EBIT_TEST(flags, ENTRY_ABORTED)) {
+       assert(EBIT_TEST(flags, RELEASE_REQUEST));
+       storeSwapOutFileClose(this);
+       return false;
+    }
+    if (EBIT_TEST(flags, ENTRY_SPECIAL)) {
+       debug(20, 3) ("storeSwapOut: %s SPECIAL\n", storeUrl(this));
+       return false;
+    }
+    return true;
+}
+
+void
+StoreEntry::trimMemory()
+{
+    if (mem_obj->policyLowestOffsetToKeep() == 0)
+       /* Nothing to do */
+       return;
+    assert (mem_obj->policyLowestOffsetToKeep() > 0);
+    if (!storeSwapOutAble(this)) {
+       /*
+        * Its not swap-able, and we're about to delete a chunk,
+        * so we must make it PRIVATE.  This is tricky/ugly because
+        * for the most part, we treat swapable == cachable here.
+        */
+       storeReleaseRequest(this);
+       mem_obj->trimUnSwappable ();
+    } else {
+       mem_obj->trimSwappable ();
+    }
+}
+/* NullStoreEntry */
+
+NullStoreEntry NullStoreEntry::_instance;
+
+NullStoreEntry *
+NullStoreEntry::getInstance()
+{
+    return &_instance;
+}
+
+char const *
+NullStoreEntry::getMD5Text() const
+{
+    return "N/A";
+}
+
+char const *
+NullStoreEntry::getSerialisedMetaData()
+{
+    return NULL;
+}
+
+#ifndef _USE_INLINE_
+#include "Store.cci"
+#endif