From 528db433264053d2a0fca095aaa25db48c816eac Mon Sep 17 00:00:00 2001 From: Dahlia Trimble Date: Wed, 3 Jun 2009 02:03:11 +0000 Subject: revert r9765 due to too many errors on some compilers. Affects Mantis #3759 --- OpenSim/Framework/CnmMemoryCache.cs | 1836 ----------------------------------- 1 file changed, 1836 deletions(-) delete mode 100644 OpenSim/Framework/CnmMemoryCache.cs (limited to 'OpenSim/Framework/CnmMemoryCache.cs') diff --git a/OpenSim/Framework/CnmMemoryCache.cs b/OpenSim/Framework/CnmMemoryCache.cs deleted file mode 100644 index 80b7283..0000000 --- a/OpenSim/Framework/CnmMemoryCache.cs +++ /dev/null @@ -1,1836 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE 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. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; - -namespace OpenSim.Framework -{ - /// - /// Cenome memory based cache to store key/value pairs (elements) limited time and/or limited size. - /// - /// - /// The type of keys in the cache. - /// - /// - /// The type of values in the dictionary. - /// - /// - /// - /// Cenome memory cache stores elements to hash table generations. When new element is being added to cache, and new size would exceed - /// maximal allowed size or maximal amount of allowed element count, then elements in oldest generation are deleted. Last access time - /// is also tracked in generation level - thus it is possible that some elements are staying in cache far beyond their expiration time. - /// If elements in older generations are accessed through method, they are moved to newest generation. - /// - /// - public class CnmMemoryCache : ICnmCache - { - /// - /// Default maximal count. - /// - /// - public const int DefaultMaxCount = 4096; - - /// - /// Default maximal size. - /// - /// - /// - /// 128MB = 128 * 1024^2 = 134 217 728 bytes. - /// - /// - /// - public const long DefaultMaxSize = 134217728; - - public long _MaxElementSize; - - /// - /// How many operations between time checks. - /// - private const int DefaultOperationsBetweenTimeChecks = 40; - - /// - /// Default expiration time. - /// - /// - /// - /// 30 minutes. - /// - /// - public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 ); - - /// - /// Minimal allowed expiration time. - /// - /// - /// - /// 5 minutes. - /// - /// - public static readonly TimeSpan MinExpirationTime = TimeSpan.FromSeconds( 10.0 ); - - /// - /// Comparer used to compare element keys. - /// - /// - /// Comparer is initialized by constructor. - /// - /// - public readonly IEqualityComparer Comparer; - - /// - /// Expiration time. - /// - private TimeSpan m_expirationTime = DefaultExpirationTime; - - /// - /// Generation bucket count. - /// - private int m_generationBucketCount; - - /// - /// Generation entry count. - /// - private int m_generationElementCount; - - /// - /// Generation max size. - /// - private long m_generationMaxSize; - - /// - /// Maximal allowed count of elements. - /// - private int m_maxCount; - - /// - /// Maximal size. - /// - private long m_maxSize; - - /// - /// New generation. - /// - private IGeneration m_newGeneration; - - /// - /// Old generation. - /// - private IGeneration m_oldGeneration; - - /// - /// Operations between time check. - /// - private int m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks; - - /// - /// Synchronization root object, should always be private and exists always - /// - private readonly object m_syncRoot = new object(); - - /// - /// Version of cache. - /// - /// - /// - /// Updated every time when cache has been changed (element removed, expired, added, replaced). - /// - /// - private int m_version; - - /// - /// Initializes a new instance of the class. - /// - public CnmMemoryCache() - : this( DefaultMaxSize ) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Maximal cache size. - /// - public CnmMemoryCache( long maximalSize ) - : this( maximalSize, DefaultMaxCount ) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Maximal cache size. - /// - /// - /// Maximal element count. - /// - public CnmMemoryCache( long maximalSize, int maximalCount ) - : this( maximalSize, maximalCount, DefaultExpirationTime ) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Maximal cache size. - /// - /// - /// Maximal element count. - /// - /// - /// Elements expiration time. - /// - public CnmMemoryCache( long maximalSize, int maximalCount, TimeSpan expirationTime ) - : this( maximalSize, maximalCount, expirationTime, EqualityComparer.Default ) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Maximal cache size. - /// - /// - /// Maximal element count. - /// - /// - /// Elements expiration time. - /// - /// - /// Comparer used for comparing elements. - /// - /// - /// is reference. - /// - public CnmMemoryCache( - long maximalSize, - int maximalCount, - TimeSpan expirationTime, - IEqualityComparer comparer ) - { - if( comparer == null ) - throw new ArgumentNullException( "comparer" ); - - if( expirationTime < MinExpirationTime ) - expirationTime = MinExpirationTime; - if( maximalCount < 8 ) - maximalCount = 8; - if( maximalSize < 8 ) - maximalSize = 8; - if( maximalCount > maximalSize ) - maximalCount = (int) maximalSize; - - Comparer = comparer; - m_expirationTime = expirationTime; - m_maxSize = maximalSize; - m_maxCount = maximalCount; - - Initialize(); - } - - /// - /// Add element to new generation. - /// - /// - /// The bucket index. - /// - /// - /// The element's key. - /// - /// - /// The element's value. - /// - /// - /// The element's size. - /// - protected virtual void AddToNewGeneration( int bucketIndex, TKey key, TValue value, long size ) - { - // Add to newest generation - if( !m_newGeneration.Set( bucketIndex, key, value, size ) ) - { - // Failed to add new generation - RecycleGenerations(); - m_newGeneration.Set( bucketIndex, key, value, size ); - } - - m_version++; - } - - /// - /// - /// Get keys bucket index. - /// - /// - /// - /// - /// Key which bucket index is being retrieved. - /// - /// - /// - /// - /// Bucket index. - /// - /// - /// - /// - /// Method uses to calculate hash code. - /// - /// - /// Bucket index is remainder when element key's hash value is divided by bucket count. - /// - /// - /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2. - /// - /// - protected virtual int GetBucketIndex( TKey key ) - { - return (Comparer.GetHashCode( key ) & 0x7FFFFFFF) % m_generationBucketCount; - } - - /// - /// Purge generation from the cache. - /// - /// - /// The generation that is purged. - /// - protected virtual void PurgeGeneration( IGeneration generation ) - { - generation.Clear(); - m_version++; - } - - /// - /// check expired. - /// - private void CheckExpired() - { - // Do this only one in every m_operationsBetweenTimeChecks - // Fetching time is using several millisecons - it is better not to do all time. - m_operationsBetweenTimeChecks--; - if( m_operationsBetweenTimeChecks <= 0 ) - PurgeExpired(); - } - - /// - /// Initialize cache. - /// - private void Initialize() - { - m_version++; - - m_generationMaxSize = MaxSize / 2; - MaxElementSize = MaxSize / 8; - m_generationElementCount = MaxCount / 2; - - // Buckets need to be prime number to get better spread of hash values - m_generationBucketCount = PrimeNumberHelper.GetPrime( m_generationElementCount ); - - m_newGeneration = new HashGeneration( this ); - m_oldGeneration = new HashGeneration( this ); - m_oldGeneration.MakeOld(); - } - - /// - /// Recycle generations. - /// - private void RecycleGenerations() - { - // Rotate old generation to new generation, new generation to old generation - var temp = m_newGeneration; - m_newGeneration = m_oldGeneration; - m_newGeneration.Clear(); - m_oldGeneration = temp; - m_oldGeneration.MakeOld(); - } - - #region Nested type: Enumerator - - /// - /// Key and value pair enumerator. - /// - private class Enumerator : IEnumerator> - { - /// - /// Current enumerator. - /// - private int m_currentEnumerator = -1; - - /// - /// Enumerators to different generations. - /// - private readonly IEnumerator>[] m_generationEnumerators = - new IEnumerator>[2]; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The cache. - /// - public Enumerator( CnmMemoryCache cache ) - { - m_generationEnumerators[ 0 ] = cache.m_newGeneration.GetEnumerator(); - m_generationEnumerators[ 1 ] = cache.m_oldGeneration.GetEnumerator(); - } - - #region IEnumerator> Members - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// - /// The element in the collection at the current position of the enumerator. - /// - /// - /// The enumerator has reach end of collection or is not called. - /// - public KeyValuePair Current - { - get - { - if( m_currentEnumerator == -1 || m_currentEnumerator >= m_generationEnumerators.Length ) - throw new InvalidOperationException(); - - return m_generationEnumerators[ m_currentEnumerator ].Current; - } - } - - /// - /// Gets the current element in the collection. - /// - /// - /// The current element in the collection. - /// - /// - /// The enumerator is positioned before the first element of the collection or after the last element. - /// 2 - object IEnumerator.Current - { - get { return Current; } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// 2 - public void Dispose() - { - } - - /// - /// Advances the enumerator to the next element of the collection. - /// - /// - /// if the enumerator was successfully advanced to the next element; if the enumerator has passed the end of the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - /// 2 - public bool MoveNext() - { - if( m_currentEnumerator == -1 ) - m_currentEnumerator = 0; - - while( m_currentEnumerator < m_generationEnumerators.Length ) - { - if( m_generationEnumerators[ m_currentEnumerator ].MoveNext() ) - return true; - - m_currentEnumerator++; - } - - return false; - } - - /// - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - /// 2 - public void Reset() - { - foreach( var enumerator in m_generationEnumerators ) - { - enumerator.Reset(); - } - - m_currentEnumerator = -1; - } - - #endregion - } - - #endregion - - #region Nested type: HashGeneration - - /// - /// Hash generation class - /// - /// - /// - /// Current implementation is based to separated chaining with move-to-front heuristics. Hash generations have fixed - /// amount of buckets and it is never rehashed. - /// - /// - /// Read more about hash tables from Wiki article. - /// - /// - /// - private class HashGeneration : IGeneration - { - /// - /// Index of first element's in element chain. - /// - /// - /// -1 if there is no element in bucket; otherwise first element's index in the element chain. - /// - /// - /// Bucket index is remainder when element key's hash value is divided by bucket count. - /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2. - /// - private readonly int[] m_buckets; - - /// - /// Cache object. - /// - private readonly CnmMemoryCache m_cache; - - /// - /// Generation's element array. - /// - /// - private readonly Element[] m_elements; - - /// - /// Index to first free element. - /// - private int m_firstFreeElement; - - /// - /// Free element count. - /// - /// - /// When generation is cleared or constructed, this is NOT set to element count. - /// This is only tracking elements that are removed and are currently free. - /// - private int m_freeCount; - - /// - /// Is this generation "new generation". - /// - private bool m_newGeneration; - - /// - /// Next unused entry. - /// - private int m_nextUnusedElement; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The cache. - /// - public HashGeneration( CnmMemoryCache cache ) - { - m_cache = cache; - m_elements = new Element[m_cache.m_generationElementCount]; - m_buckets = new int[m_cache.m_generationBucketCount]; - Clear(); - } - - /// - /// Find element's index - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// Move element to front of elements. - /// - /// - /// The previous element's index. - /// - /// - /// Element's index, if found from the generation; -1 otherwise (if element is not found the generation). - /// - private int FindElementIndex( int bucketIndex, TKey key, bool moveToFront, out int previousIndex ) - { - previousIndex = -1; - var elementIndex = m_buckets[ bucketIndex ]; - while( elementIndex >= 0 ) - { - if( m_cache.Comparer.Equals( key, m_elements[ elementIndex ].Key ) ) - { - // Found match - if( moveToFront && previousIndex >= 0 ) - { - // Move entry to front - m_elements[ previousIndex ].Next = m_elements[ elementIndex ].Next; - m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ]; - m_buckets[ bucketIndex ] = elementIndex; - previousIndex = 0; - } - - return elementIndex; - } - - previousIndex = elementIndex; - elementIndex = m_elements[ elementIndex ].Next; - } - - return -1; - } - - /// - /// Remove element front the generation. - /// - /// - /// The bucket index. - /// - /// - /// The element index. - /// - /// - /// The element's previous index. - /// - private void RemoveElement( int bucketIndex, int entryIndex, int previousIndex ) - { - if( previousIndex >= 0 ) - m_elements[ previousIndex ].Next = m_elements[ entryIndex ].Next; - else - m_buckets[ bucketIndex ] = m_elements[ entryIndex ].Next; - - Size -= m_elements[ entryIndex ].Size; - m_elements[ entryIndex ].Value = default(TValue); - m_elements[ entryIndex ].Key = default(TKey); - - // Add element to free elements list - m_elements[ entryIndex ].Next = m_firstFreeElement; - m_firstFreeElement = entryIndex; - m_freeCount++; - } - - #region Nested type: Element - - /// - /// Element that stores key, next element in chain, size and value. - /// - private struct Element - { - /// - /// Element's key. - /// - public TKey Key; - - /// - /// Next element in chain. - /// - /// - /// When element have value (something is stored to it), this is index of - /// next element with same bucket index. When element is free, this - /// is index of next element in free element's list. - /// - public int Next; - - /// - /// Size of element. - /// - /// - /// 0 if element is free; otherwise larger than 0. - /// - public long Size; - - /// - /// Element's value. - /// - /// - /// It is possible that this value is even when element - /// have value - element's value is then reference. - /// - public TValue Value; - - /// - /// Gets a value indicating whether element is free or have value. - /// - /// - /// when element is free; otherwise . - /// - public bool IsFree - { - get { return Size == 0; } - } - } - - #endregion - - #region Nested type: Enumerator - - /// - /// Key value pair enumerator for object. - /// - private class Enumerator : IEnumerator> - { - /// - /// Current element. - /// - private KeyValuePair m_current; - - /// - /// Current index. - /// - private int m_currentIndex; - - /// - /// Generation that is being enumerated. - /// - private readonly HashGeneration m_generation; - - /// - /// Cache version. - /// - /// - /// When cache is change, version number is changed. - /// - /// - private readonly int m_version; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The generation. - /// - public Enumerator( HashGeneration generation ) - { - m_generation = generation; - m_version = m_generation.m_cache.m_version; - } - - #region IEnumerator> Members - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// - /// The element in the collection at the current position of the enumerator. - /// - /// - /// The enumerator has reach end of collection or is not called. - /// - public KeyValuePair Current - { - get - { - if( m_currentIndex == 0 || m_currentIndex >= m_generation.Count ) - throw new InvalidOperationException(); - - return m_current; - } - } - - /// - /// Gets the current element in the collection. - /// - /// - /// The current element in the collection. - /// - /// - /// The enumerator is positioned before the first element of the collection or after the last element. - /// 2 - object IEnumerator.Current - { - get { return Current; } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// 2 - public void Dispose() - { - } - - /// - /// Advances the enumerator to the next element of the collection. - /// - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - public bool MoveNext() - { - if( m_version != m_generation.m_cache.m_version ) - throw new InvalidOperationException(); - - while( m_currentIndex < m_generation.Count ) - { - if( m_generation.m_elements[ m_currentIndex ].IsFree ) - { - m_currentIndex++; - continue; - } - - m_current = new KeyValuePair( m_generation.m_elements[ m_currentIndex ].Key, - m_generation.m_elements[ m_currentIndex ].Value ); - m_currentIndex++; - return true; - } - - m_current = new KeyValuePair(); - return false; - } - - /// - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// - /// - /// The collection was modified after the enumerator was created. - /// - /// 2 - public void Reset() - { - if( m_version != m_generation.m_cache.m_version ) - throw new InvalidOperationException(); - - m_currentIndex = 0; - } - - #endregion - } - - #endregion - - #region IGeneration Members - - /// - /// Gets or sets a value indicating whether generation was accessed since last time check. - /// - public bool AccessedSinceLastTimeCheck { get; set; } - - /// - /// Gets element count in generation. - /// - public int Count - { - get { return m_nextUnusedElement - m_freeCount; } - } - - /// - /// Gets or sets generation's expiration time. - /// - public DateTime ExpirationTime { get; set; } - - /// - /// Gets or sets size of data stored to generation. - /// - public long Size { get; private set; } - - /// - /// Clear all elements from the generation and make generation new again. - /// - /// - /// When generation is new, it is allowed to add new elements to it and - /// doesn't remove elements from it. - /// - /// - public void Clear() - { - for( var i = m_buckets.Length - 1 ; i >= 0 ; i-- ) - { - m_buckets[ i ] = -1; - } - - Array.Clear( m_elements, 0, m_elements.Length ); - Size = 0; - m_firstFreeElement = -1; - m_freeCount = 0; - m_nextUnusedElement = 0; - m_newGeneration = true; - ExpirationTime = DateTime.MaxValue; - } - - /// - /// Determines whether the contains an element with the specific key. - /// - /// - /// The bucket index for the to locate in . - /// - /// - /// The key to locate in the . - /// - /// - /// if the contains an element with the ; - /// otherwise . - /// - public bool Contains( int bucketIndex, TKey key ) - { - int previousIndex; - if( FindElementIndex( bucketIndex, key, true, out previousIndex ) == -1 ) - return false; - - AccessedSinceLastTimeCheck = true; - return true; - } - - /// - /// Returns an enumerator that iterates through the elements stored . - /// - /// - /// A that can be used to iterate through the . - /// - /// 1 - public IEnumerator> GetEnumerator() - { - return new Enumerator( this ); - } - - /// - /// Make from generation old generation. - /// - /// - /// When generation is old, hit removes element from the generation. - /// - /// - public void MakeOld() - { - m_newGeneration = false; - } - - /// - /// Remove element associated with the key from the generation. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// , if remove was successful; otherwise . - /// - public bool Remove( int bucketIndex, TKey key ) - { - int previousIndex; - var entryIndex = FindElementIndex( bucketIndex, key, false, out previousIndex ); - if( entryIndex != -1 ) - { - RemoveElement( bucketIndex, entryIndex, previousIndex ); - AccessedSinceLastTimeCheck = true; - return true; - } - - return false; - } - - /// - /// Set or add element to generation. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// The element's value. - /// - /// - /// The element's size. - /// - /// - /// , if setting or adding was successful; otherwise . - /// - /// - /// - /// If element was already existing in generation and new element size fits to collection limits, - /// then it's value is replaced with new one and size information is updated. If element didn't - /// exists in generation before, then generation must have empty space for a new element and - /// size must fit generation's limits, before element is added to generation. - /// - /// - public bool Set( int bucketIndex, TKey key, TValue value, long size ) - { - Debug.Assert( m_newGeneration, "It is possible to insert new elements only to newest generation." ); - Debug.Assert( size > 0, "New element size should be more than 0." ); - - int previousIndex; - var elementIndex = FindElementIndex( bucketIndex, key, true, out previousIndex ); - if( elementIndex == -1 ) - { - // New key - if( Size + size > m_cache.m_generationMaxSize || - (m_nextUnusedElement == m_cache.m_generationElementCount && m_freeCount == 0) ) - { - // Generation is full - return false; - } - - // Increase size of generation - Size += size; - - // Get first free entry and update free entry list - if( m_firstFreeElement != -1 ) - { - // There was entry that was removed - elementIndex = m_firstFreeElement; - m_firstFreeElement = m_elements[ elementIndex ].Next; - m_freeCount--; - } - else - { - // No entries removed so far - just take a last one - elementIndex = m_nextUnusedElement; - m_nextUnusedElement++; - } - - Debug.Assert( m_elements[ elementIndex ].IsFree, "Allocated element is not free." ); - - // Move new entry to front - m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ]; - m_buckets[ bucketIndex ] = elementIndex; - - // Set key and update count - m_elements[ elementIndex ].Key = key; - } - else - { - // Existing key - if( Size - m_elements[ elementIndex ].Size + size > m_cache.m_generationMaxSize ) - { - // Generation is full - // Remove existing element, because generation is going to be recycled to - // old generation and element is stored to new generation - RemoveElement( bucketIndex, elementIndex, previousIndex ); - return false; - } - - // Update generation's size - Size = Size - m_elements[ elementIndex ].Size + size; - } - - // Finally set value and size - m_elements[ elementIndex ].Value = value; - m_elements[ elementIndex ].Size = size; - - // Success - key was inserterted to generation - AccessedSinceLastTimeCheck = true; - return true; - } - - /// - /// Try to get element associated with key. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// The element's value. - /// - /// - /// The element's size. - /// - /// - /// , if element was successful retrieved; otherwise . - /// - /// - /// - /// If element is not found from generation then and - /// are set to default value (default(TValue) and 0). - /// - /// - public bool TryGetValue( int bucketIndex, TKey key, out TValue value, out long size ) - { - // Find entry index, - int previousIndex; - var elementIndex = FindElementIndex( bucketIndex, key, m_newGeneration, out previousIndex ); - if( elementIndex == -1 ) - { - value = default(TValue); - size = 0; - return false; - } - - value = m_elements[ elementIndex ].Value; - size = m_elements[ elementIndex ].Size; - - if( !m_newGeneration ) - { - // Old generation - remove element, because it is moved to new generation - RemoveElement( bucketIndex, elementIndex, previousIndex ); - } - - AccessedSinceLastTimeCheck = true; - return true; - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - /// 2 - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } - - #endregion - - #region Nested type: IGeneration - - /// - /// Cache element generation interface - /// - /// - /// - /// Generation can hold limited count of elements and limited size of data. - /// - /// - /// There are two kind generations: "new generation" and "old generation(s)". All new elements - /// are added to "new generation". - /// - /// - protected interface IGeneration : IEnumerable> - { - /// - /// Gets or sets a value indicating whether generation was accessed since last time check. - /// - bool AccessedSinceLastTimeCheck { get; set; } - - /// - /// Gets element count in generation. - /// - int Count { get; } - - /// - /// Gets or sets generation's expiration time. - /// - DateTime ExpirationTime { get; set; } - - /// - /// Gets size of data stored to generation. - /// - long Size { get; } - - /// - /// Clear all elements from the generation and make generation new again. - /// - /// - /// When generation is new, it is allowed to add new elements to it and - /// doesn't remove elements from it. - /// - /// - void Clear(); - - /// - /// Determines whether the contains an element with the specific key. - /// - /// - /// The bucket index for the to locate in . - /// - /// - /// The key to locate in the . - /// - /// - /// if the contains an element with the ; - /// otherwise . - /// - bool Contains( int bucketIndex, TKey key ); - - /// - /// Make from generation old generation. - /// - /// - /// When generation is old, hit removes element from the generation. - /// - /// - void MakeOld(); - - /// - /// Remove element associated with the key from the generation. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// , if remove was successful; otherwise . - /// - bool Remove( int bucketIndex, TKey key ); - - /// - /// Set or add element to generation. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// The element's value. - /// - /// - /// The element's size. - /// - /// - /// , if setting or adding was successful; otherwise . - /// - /// - /// - /// If element was already existing in generation and new element size fits to collection limits, - /// then it's value is replaced with new one and size information is updated. If element didn't - /// exists in generation before, then generation must have empty space for a new element and - /// size must fit generation's limits, before element is added to generation. - /// - /// - bool Set( int bucketIndex, TKey key, TValue value, long size ); - - /// - /// Try to get element associated with key. - /// - /// - /// The element's bucket index. - /// - /// - /// The element's key. - /// - /// - /// The element's value. - /// - /// - /// The element's size. - /// - /// - /// , if element was successful retrieved; otherwise . - /// - /// - /// - /// If element is not found from generation then and - /// are set to default value (default(TValue) and 0). - /// - /// - bool TryGetValue( int bucketIndex, TKey key, out TValue value, out long size ); - } - - #endregion - - #region ICnmCache Members - - /// - /// Gets current count of elements stored to . - /// - /// - /// - /// When adding an new element to that is limiting element count, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - /// - public int Count - { - get { return m_newGeneration.Count + m_oldGeneration.Count; } - } - - /// - /// Gets or sets elements expiration time. - /// - /// - /// Elements expiration time. - /// - /// - /// - /// When element has been stored in longer than - /// and it is not accessed through method or element's value is - /// not replaced by method, then it is automatically removed from the - /// . - /// - /// - /// It is possible that implementation removes element before it's expiration time, - /// because total size or count of elements stored to cache is larger than or . - /// - /// - /// It is also possible that element stays in cache longer than . - /// - /// - /// Calling try to remove all elements that are expired. - /// - /// - /// To disable time limit in cache, set to . - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public TimeSpan ExpirationTime - { - get { return m_expirationTime; } - - set - { - if( value < MinExpirationTime ) - value = MinExpirationTime; - - if( m_expirationTime == value ) - return; - - var now = DateTime.Now; - m_newGeneration.ExpirationTime = (m_newGeneration.ExpirationTime - m_expirationTime) + value; - m_oldGeneration.ExpirationTime = (m_oldGeneration.ExpirationTime - m_expirationTime) + value; - m_expirationTime = value; - - PurgeExpired(); - } - } - - /// - /// Gets a value indicating whether is limiting count of elements. - /// - /// - /// if the count of elements is limited; - /// otherwise, . - /// - /// - /// - /// When adding an new element to that is limiting element count, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - /// - public bool IsCountLimited - { - get { return true; } - } - - /// - /// Gets a value indicating whether is limiting size of elements. - /// - /// - /// if the total size of elements is limited; - /// otherwise, . - /// - /// - /// - /// When adding an new element to that is limiting total size of elements, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - /// - /// - public bool IsSizeLimited - { - get { return true; } - } - - /// - /// Gets a value indicating whether or not access to the is synchronized (thread safe). - /// - /// - /// if access to the is synchronized (thread safe); - /// otherwise, . - /// - /// - /// - /// To get synchronized (thread safe) access to object, use - /// in class - /// to retrieve synchronized wrapper for object. - /// - /// - /// - /// - public bool IsSynchronized - { - get { return false; } - } - - /// - /// Gets a value indicating whether elements stored to have limited inactivity time. - /// - /// - /// if the has a fixed total size of elements; - /// otherwise, . - /// - /// - /// If have limited inactivity time and element is not accessed through - /// or methods in , then element is automatically removed from - /// the cache. Depending on implementation of the , some of the elements may - /// stay longer in cache. - /// - /// - /// - /// - /// - public bool IsTimeLimited - { - get { return ExpirationTime != TimeSpan.MaxValue; } - } - - /// - /// Gets or sets maximal allowed count of elements that can be stored to . - /// - /// - /// , if is not limited by count of elements; - /// otherwise maximal allowed count of elements. - /// - /// - /// - /// When adding an new element to that is limiting element count, - /// will remove less recently used elements until it can fit an new element. - /// - /// - public int MaxCount - { - get { return m_maxCount; } - - set - { - if( value < 8 ) - value = 8; - if( m_maxCount == value ) - return; - - m_maxCount = value; - Initialize(); - } - } - - /// - /// Gets maximal allowed element size. - /// - /// - /// Maximal allowed element size. - /// - /// - /// - /// If element's size is larger than , then element is - /// not added to the . - /// - /// - /// - /// - /// - /// - public long MaxElementSize - { - get { return _MaxElementSize; } - - private set { _MaxElementSize = value; } - } - - /// - /// Gets or sets maximal allowed total size for elements stored to . - /// - /// - /// Maximal allowed total size for elements stored to . - /// - /// - /// - /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure. - /// - /// - /// When adding an new element to that is limiting total size of elements, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - public long MaxSize - { - get { return m_maxSize; } - - set - { - if( value < 8 ) - value = 8; - if( m_maxSize == value ) - return; - - m_maxSize = value; - Initialize(); - } - } - - /// - /// Gets total size of elements stored to . - /// - /// - /// Total size of elements stored to . - /// - /// - /// - /// Normally bytes, but can be any suitable unit of measure. - /// - /// - /// Element's size is given when element is added or replaced by method. - /// - /// - /// When adding an new element to that is limiting total size of elements, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - /// - /// - public long Size - { - get { return m_newGeneration.Size + m_oldGeneration.Size; } - } - - /// - /// Gets an object that can be used to synchronize access to the . - /// - /// - /// An object that can be used to synchronize access to the . - /// - /// - /// - /// To get synchronized (thread safe) access to , use - /// method to retrieve synchronized wrapper interface to - /// . - /// - /// - /// - /// - public object SyncRoot - { - get { return m_syncRoot; } - } - - /// - /// Removes all elements from the . - /// - /// - /// - /// - /// - /// - public void Clear() - { - m_newGeneration.Clear(); - m_oldGeneration.Clear(); - m_oldGeneration.MakeOld(); - m_version++; - } - - /// - /// Returns an enumerator that iterates through the elements stored to . - /// - /// - /// A that can be used to iterate through the collection. - /// - /// 1 - public IEnumerator> GetEnumerator() - { - return new Enumerator( this ); - } - - /// - /// Purge expired elements from the . - /// - /// - /// - /// Element becomes expired when last access time to it has been longer time than . - /// - /// - /// Depending on implementation, some of expired elements - /// may stay longer than in the cache. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void PurgeExpired() - { - m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks; - - var now = DateTime.Now; - if( m_newGeneration.AccessedSinceLastTimeCheck ) - { - // New generation has been accessed since last check - // Update it's expiration time. - m_newGeneration.ExpirationTime = now + ExpirationTime; - m_newGeneration.AccessedSinceLastTimeCheck = false; - } - else if( m_newGeneration.ExpirationTime < now ) - { - // New generation has been expired. - // --> also old generation must be expired. - PurgeGeneration( m_newGeneration ); - PurgeGeneration( m_oldGeneration ); - return; - } - - if( m_oldGeneration.ExpirationTime < now ) - PurgeGeneration( m_oldGeneration ); - } - - /// - /// Removes element associated with from the . - /// - /// - /// The key that is associated with element to remove from the . - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - public void Remove( TKey key ) - { - if( key == null ) - throw new ArgumentNullException( "key" ); - - var bucketIndex = GetBucketIndex( key ); - if( !m_newGeneration.Remove( bucketIndex, key ) ) - { - if( !m_oldGeneration.Remove( bucketIndex, key ) ) - { - CheckExpired(); - return; - } - } - - CheckExpired(); - m_version++; - } - - /// - /// Removes elements that are associated with one of from the . - /// - /// - /// The keys that are associated with elements to remove from the . - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - public void RemoveRange( IEnumerable keys ) - { - if( keys == null ) - throw new ArgumentNullException( "keys" ); - - foreach( var key in keys ) - { - if( key == null ) - continue; - - var bucketIndex = GetBucketIndex( key ); - if( !m_newGeneration.Remove( bucketIndex, key ) ) - m_oldGeneration.Remove( bucketIndex, key ); - } - - CheckExpired(); - m_version++; - } - - /// - /// Add or replace an element with the provided , and to - /// . - /// - /// - /// The object used as the key of the element. Can't be reference. - /// - /// - /// The object used as the value of the element to add or replace. is allowed. - /// - /// - /// The element's size. Normally bytes, but can be any suitable unit of measure. - /// - /// - /// if element has been added successfully to the ; - /// otherwise . - /// - /// - /// is . - /// - /// - /// The element's is less than 0. - /// - /// - /// - /// If element's is larger than , then element is - /// not added to the , however - possible older element is - /// removed from the . - /// - /// - /// When adding an new element to that is limiting total size of elements, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// When adding an new element to that is limiting element count, - /// will remove less recently used elements until it can fit an new element. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public bool Set( TKey key, TValue value, long size ) - { - if( key == null ) - throw new ArgumentNullException( "key" ); - - if( size < 0 ) - throw new ArgumentOutOfRangeException( "size", size, "Value's size can't be less than 0." ); - - if( size > MaxElementSize ) - { - // Entry size is too big to fit cache - ignore it - Remove( key ); - return false; - } - - if( size == 0 ) - size = 1; - - var bucketIndex = GetBucketIndex( key ); - m_oldGeneration.Remove( bucketIndex, key ); - AddToNewGeneration( bucketIndex, key, value, size ); - CheckExpired(); - - return true; - } - - /// - /// Gets the associated with the specified . - /// - /// - /// if the contains an element with - /// the specified key; otherwise, . - /// - /// - /// The key whose to get. - /// - /// - /// When this method returns, the value associated with the specified , - /// if the is found; otherwise, the - /// default value for the type of the parameter. This parameter is passed uninitialized. - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - public bool TryGetValue( TKey key, out TValue value ) - { - if( key == null ) - throw new ArgumentNullException( "key" ); - - var bucketIndex = GetBucketIndex( key ); - long size; - if( m_newGeneration.TryGetValue( bucketIndex, key, out value, out size ) ) - { - CheckExpired(); - return true; - } - - if( m_oldGeneration.TryGetValue( bucketIndex, key, out value, out size ) ) - { - // Move element to new generation - AddToNewGeneration( bucketIndex, key, value, size ); - CheckExpired(); - return true; - } - - CheckExpired(); - return false; - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - /// 2 - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } -} -- cgit v1.1