nsresult
nsMemoryCacheDevice::DeactivateEntry(nsCacheEntry * entry)
{
    CACHE_LOG_DEBUG(("nsMemoryCacheDevice::DeactivateEntry for entry 0x%p\n",
                     entry));
    if (entry->IsDoomed()) {
#ifdef DEBUG
        // XXX verify we've removed it from mMemCacheEntries & eviction list
#endif
        delete entry;
        CACHE_LOG_DEBUG(("deleted doomed entry 0x%p\n", entry));
        return NS_OK;
    }

#ifdef DEBUG
    nsCacheEntry * ourEntry = mMemCacheEntries.GetEntry(entry->Key());
    NS_ASSERTION(ourEntry, "DeactivateEntry called for an entry we don't have!");
    NS_ASSERTION(entry == ourEntry, "entry doesn't match ourEntry");
    if (ourEntry != entry)
        return NS_ERROR_INVALID_POINTER;
#endif

    mInactiveSize += entry->DataSize();
    EvictEntriesIfNecessary();

    return NS_OK;
}
NS_INTERFACE_MAP_END_THREADSAFE

nsresult nsCacheEntryDescriptor::
nsInputStreamWrapper::LazyInit()
{
    // Check if we have the descriptor. If not we can't even grab the cache
    // lock since it is not ensured that the cache service still exists.
    if (!mDescriptor)
        return NS_ERROR_NOT_AVAILABLE;

    nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT));

    nsCacheAccessMode mode;
    nsresult rv = mDescriptor->GetAccessGranted(&mode);
    if (NS_FAILED(rv)) return rv;

    NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);

    nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
    if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;

    rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
                                                 mStartOffset,
                                                 getter_AddRefs(mInput));

    CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
                      "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
                      mDescriptor, this, mInput.get(), int(rv)));

    if (NS_FAILED(rv)) return rv;

    mInitialized = true;
    return NS_OK;
}
nsresult nsCacheEntryDescriptor::
nsInputStreamWrapper::LazyInit()
{
    nsCacheServiceAutoLock lock(LOCK_TELEM(NSINPUTSTREAMWRAPPER_LAZYINIT));

    nsCacheAccessMode mode;
    nsresult rv = mDescriptor->GetAccessGranted(&mode);
    if (NS_FAILED(rv)) return rv;

    NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);

    nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
    if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;

    rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
                                                 mStartOffset,
                                                 getter_AddRefs(mInput));

    CACHE_LOG_DEBUG(("nsInputStreamWrapper::LazyInit "
                      "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
                      mDescriptor, this, mInput.get(), int(rv)));

    if (NS_FAILED(rv)) return rv;

    mInitialized = true;
    return NS_OK;
}
bool
nsMemoryCacheDevice::EntryIsTooBig(int64_t entrySize)
{
    CACHE_LOG_DEBUG(("nsMemoryCacheDevice::EntryIsTooBig "
                     "[size=%d max=%d soft=%d]\n",
                     entrySize, mMaxEntrySize, mSoftLimit));
    if (mMaxEntrySize == -1)
        return entrySize > mSoftLimit;
    else
        return (entrySize > mSoftLimit || entrySize > mMaxEntrySize);
}
void
nsMemoryCacheDevice::DoomEntry(nsCacheEntry * entry)
{
#ifdef DEBUG
    // debug code to verify we have entry
    nsCacheEntry * hashEntry = mMemCacheEntries.GetEntry(entry->Key());
    if (!hashEntry)               NS_WARNING("no entry for key");
    else if (entry != hashEntry)  NS_WARNING("entry != hashEntry");
#endif
    CACHE_LOG_DEBUG(("Dooming entry 0x%p in memory cache\n", entry));
    EvictEntry(entry, DO_NOT_DELETE_ENTRY);
}
nsresult nsCacheEntryDescriptor::
nsInputStreamWrapper::Read_Locked(char *buf, uint32_t count, uint32_t *countRead)
{
    nsresult rv = EnsureInit();
    if (NS_SUCCEEDED(rv))
        rv = mInput->Read(buf, count, countRead);

    CACHE_LOG_DEBUG(("nsInputStreamWrapper::Read "
                      "[entry=%p, wrapper=%p, mInput=%p, rv=%d]",
                      mDescriptor, this, mInput.get(), rv));

    return rv;
}
void
nsMemoryCacheDevice::EvictEntriesIfNecessary(void)
{
    nsCacheEntry * entry;
    nsCacheEntry * maxEntry;
    CACHE_LOG_DEBUG(("EvictEntriesIfNecessary.  mTotalSize: %d, mHardLimit: %d,"
                     "mInactiveSize: %d, mSoftLimit: %d\n",
                     mTotalSize, mHardLimit, mInactiveSize, mSoftLimit));
    
    if ((mTotalSize < mHardLimit) && (mInactiveSize < mSoftLimit))
        return;

    uint32_t now = SecondsFromPRTime(PR_Now());
    uint64_t entryCost = 0;
    uint64_t maxCost = 0;
    do {
        // LRU-SP eviction selection: Check the head of each segment (each
        // eviction list, kept in LRU order) and select the maximal-cost
        // entry for eviction. Cost is time-since-accessed * size / nref.
        maxEntry = 0;
        for (int i = kQueueCount - 1; i >= 0; --i) {
            entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]);

            // If the head of a list is in use, check the next available entry
            while ((entry != &mEvictionList[i]) &&
                   (entry->IsInUse())) {
                entry = (nsCacheEntry *)PR_NEXT_LINK(entry);
            }

            if (entry != &mEvictionList[i]) {
                entryCost = (uint64_t)
                    (now - entry->LastFetched()) * entry->DataSize() / 
                    std::max(1, entry->FetchCount());
                if (!maxEntry || (entryCost > maxCost)) {
                    maxEntry = entry;
                    maxCost = entryCost;
                }
            }
        }
        if (maxEntry) {
            EvictEntry(maxEntry, DELETE_ENTRY);
        } else {
            break;
        }
    }
    while ((mTotalSize >= mHardLimit) || (mInactiveSize >= mSoftLimit));
}
void
nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry)
{
    CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n",
                     entry, deleteEntry));
    // remove entry from our hashtable
    mMemCacheEntries.RemoveEntry(entry);
    
    // remove entry from the eviction list
    PR_REMOVE_AND_INIT_LINK(entry);
    
    // update statistics
    int32_t memoryRecovered = (int32_t)entry->DataSize();
    mTotalSize    -= memoryRecovered;
    if (!entry->IsDoomed())
        mInactiveSize -= memoryRecovered;
    --mEntryCount;
    
    if (deleteEntry)  delete entry;
}
nsresult
nsCacheEntry::CreateDescriptor(nsCacheRequest *           request,
                               nsCacheAccessMode          accessGranted,
                               nsICacheEntryDescriptor ** result)
{
    NS_ENSURE_ARG_POINTER(request && result);

    nsCacheEntryDescriptor * descriptor =
        new nsCacheEntryDescriptor(this, accessGranted);

    // XXX check request is on q
    PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success

    if (descriptor == nsnull)
        return NS_ERROR_OUT_OF_MEMORY;

    PR_APPEND_LINK(descriptor, &mDescriptorQ);

    CACHE_LOG_DEBUG(("  descriptor %p created for request %p on entry %p\n",
                    descriptor, request, this));

    NS_ADDREF(*result = descriptor);
    return NS_OK;
}