//
// System.Web.Caching
//
// Author:
// Patrik Torstensson (Patrik.Torstensson@labs2.com)
//
// (C) Copyright Patrik Torstensson, 2001
//
namespace System.Web.Caching
{
///
/// Responsible for holding a cache entry in the linked list bucket.
///
public struct ExpiresEntry
{
public CacheEntry _objEntry;
public long _ticksExpires;
public int _intNext;
}
///
/// Holds cache entries that has a expiration in a bucket list.
///
public class ExpiresBucket
{
private static int MIN_ENTRIES = 16;
private byte _byteID;
private int _intSize;
private int _intCount;
private int _intNext;
private Cache _objManager;
private ExpiresEntry [] _arrEntries;
///
/// Constructs a new bucket.
///
/// Current bucket ID.
/// Cache manager reponsible for the item(s) in the expires bucket.
public ExpiresBucket(byte bucket, Cache objManager)
{
_objManager = objManager;
Initialize(bucket);
}
///
/// Initializes the expires bucket, creates a linked list of MIN_ENTRIES.
///
/// Bucket ID.
private void Initialize(byte bucket)
{
_byteID = bucket;
_intNext = 0;
_intCount = 0;
_arrEntries = new ExpiresEntry[MIN_ENTRIES];
_intSize = MIN_ENTRIES;
int intPos = 0;
do
{
_arrEntries[intPos]._intNext = intPos + 1;
_arrEntries[intPos]._ticksExpires = System.DateTime.MaxValue.Ticks;
intPos++;
} while (intPos < _intSize);
_arrEntries[_intSize - 1]._intNext = -1;
}
///
/// Expands the bucket linked array list.
///
private void Expand()
{
ExpiresEntry [] arrData;
int intPos = 0;
int intOldSize;
lock(this)
{
intOldSize = _intSize;
_intSize *= 2;
// Create a new array and copy the old data into the new array
arrData = new ExpiresEntry[_intSize];
do
{
arrData[intPos] = _arrEntries[intPos];
intPos++;
} while (intPos < intOldSize);
_intNext = intPos;
// Initialize the "new" positions.
do
{
arrData[intPos]._intNext = intPos + 1;
intPos++;
} while (intPos < _intSize);
arrData[_intSize - 1]._intNext = -1;
_arrEntries = arrData;
}
}
///
/// Adds a cache entry into the expires bucket.
///
/// Cache Entry object to be added.
public void Add(CacheEntry objEntry)
{
if (_intNext == -1)
{
Expand();
}
lock(this)
{
_arrEntries[_intNext]._ticksExpires = objEntry.Expires;
_arrEntries[_intNext]._objEntry = objEntry;
_intNext = _arrEntries[_intNext]._intNext;
_intCount++;
}
}
///
/// Removes a cache entry from the expires bucket.
///
/// Cache entry to be removed.
public void Remove(CacheEntry objEntry)
{
lock(this)
{
// Check if this is our bucket
if (objEntry.ExpiresIndex != _byteID) return;
if (objEntry.ExpiresIndex == System.Int32.MaxValue) return;
if (_arrEntries.Length < objEntry.ExpiresIndex) return;
_intCount--;
_arrEntries[objEntry.ExpiresIndex]._objEntry.ExpiresBucket = byte.MaxValue;
_arrEntries[objEntry.ExpiresIndex]._objEntry.ExpiresIndex = int.MaxValue;
_arrEntries[objEntry.ExpiresIndex]._objEntry = null;
_intNext = _arrEntries[objEntry.ExpiresIndex]._intNext;
}
}
///
/// Updates a cache entry in the expires bucket, this is called during a hit of an item if the
/// cache item has a sliding expiration. The function is responsible for updating the cache
/// entry.
///
/// Cache entry to update.
/// New expiration value for the cache entry.
public void Update(CacheEntry objEntry, long ticksExpires)
{
lock(this)
{
// Check if this is our bucket
if (objEntry.ExpiresIndex != _byteID) return;
if (objEntry.ExpiresIndex == System.Int32.MaxValue) return;
if (_arrEntries.Length < objEntry.ExpiresIndex) return;
_arrEntries[objEntry.ExpiresIndex]._ticksExpires = ticksExpires;
_arrEntries[objEntry.ExpiresIndex]._objEntry.Expires = ticksExpires;
}
}
///
/// Flushes all cache entries that has expired and removes them from the cache manager.
///
public void FlushExpiredItems()
{
ExpiresEntry objEntry;
CacheEntry [] arrCacheEntries;
int intCachePos;
int intPos;
long ticksNow;
ticksNow = System.DateTime.Now.Ticks;
intCachePos = 0;
// Lookup all items that needs to be removed, this is done in a two part
// operation to minimize the locking time.
lock (this)
{
arrCacheEntries = new CacheEntry[_intSize];
intPos = 0;
do
{
objEntry = _arrEntries[intPos];
if (objEntry._objEntry != null)
{
if (objEntry._ticksExpires < ticksNow)
{
arrCacheEntries[intCachePos++] = objEntry._objEntry;
objEntry._objEntry.ExpiresBucket = byte.MaxValue;
objEntry._objEntry.ExpiresIndex = int.MaxValue;
objEntry._objEntry = null;
_intNext = objEntry._intNext;
}
}
intPos++;
} while (intPos < _intSize);
}
// If we have any entries to remove, go ahead and call the cache manager remove.
if (intCachePos > 0)
{
intPos = 0;
do
{
_objManager.Remove(arrCacheEntries[intPos].Key, CacheItemRemovedReason.Expired);
intPos++;
} while (intPos < intCachePos);
}
}
///
/// Returns the current size of the expires bucket.
///
public int Size
{
get
{
return _arrEntries.Length;
}
}
///
/// Returns number of items in the bucket.
///
public int Count
{
get
{
return _intCount;
}
}
}
}