diff --git a/OpenSim/Framework/CnmMemoryCache.cs b/OpenSim/Framework/CnmMemoryCache.cs index 6ca71ecbd8..8c25da0a1e 100644 --- a/OpenSim/Framework/CnmMemoryCache.cs +++ b/OpenSim/Framework/CnmMemoryCache.cs @@ -1,1852 +1,1852 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// -// -// -// -// -// -------------------------------------------------------------------------------------------------------------------- - -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; - - /// - /// 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 allowed total size of elements. - /// - private long m_maxElementSize; - - /// - /// 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 - IGeneration 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( IEnumerator> 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 - { - /// - /// Value indicating whether generation was accessed since last time check. - /// - private bool m_accessedSinceLastTimeCheck; - - /// - /// 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; - - /// - /// Generation's expiration time. - /// - private DateTime m_expirationTime1; - - /// - /// 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; - - /// - /// Size of data stored to generation. - /// - private long m_size; - - /// - /// 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; - int 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 { return m_accessedSinceLastTimeCheck; } - - set { m_accessedSinceLastTimeCheck = value; } - } - - /// - /// 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 { return m_expirationTime1; } - - set { m_expirationTime1 = value; } - } - - /// - /// Gets or sets size of data stored to generation. - /// - public long Size - { - get { return m_size; } - - private set { m_size = value; } - } - - /// - /// 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( int 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; - int 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; - int 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; - int 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; - - 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 m_maxElementSize; } - - private set { m_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; - - if( !IsTimeLimited ) - return; - - DateTime 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" ); - - int 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( TKey key in keys ) - { - if( key == null ) - continue; - - int 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; - - int 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" ); - - int 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 - } -} +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +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; + + /// + /// 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 allowed total size of elements. + /// + private long m_maxElementSize; + + /// + /// 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 + IGeneration 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( IEnumerator> 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 + { + /// + /// Value indicating whether generation was accessed since last time check. + /// + private bool m_accessedSinceLastTimeCheck; + + /// + /// 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; + + /// + /// Generation's expiration time. + /// + private DateTime m_expirationTime1; + + /// + /// 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; + + /// + /// Size of data stored to generation. + /// + private long m_size; + + /// + /// 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; + int 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 { return m_accessedSinceLastTimeCheck; } + + set { m_accessedSinceLastTimeCheck = value; } + } + + /// + /// 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 { return m_expirationTime1; } + + set { m_expirationTime1 = value; } + } + + /// + /// Gets or sets size of data stored to generation. + /// + public long Size + { + get { return m_size; } + + private set { m_size = value; } + } + + /// + /// 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( int 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; + int 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; + int 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; + int 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; + + 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 m_maxElementSize; } + + private set { m_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; + + if( !IsTimeLimited ) + return; + + DateTime 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" ); + + int 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( TKey key in keys ) + { + if( key == null ) + continue; + + int 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; + + int 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" ); + + int 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 + } +} diff --git a/OpenSim/Framework/CnmSynchronizedCache.cs b/OpenSim/Framework/CnmSynchronizedCache.cs index 418a095e5d..bf588ceec2 100644 --- a/OpenSim/Framework/CnmSynchronizedCache.cs +++ b/OpenSim/Framework/CnmSynchronizedCache.cs @@ -1,746 +1,746 @@ -/* - * 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.Threading; - -namespace OpenSim.Framework -{ - /// - /// Synchronized Cenome cache wrapper. - /// - /// - /// The type of keys in the cache. - /// - /// - /// The type of values in the cache. - /// - /// - /// - /// Enumerator will block other threads, until enumerator's method is called. - /// "foreach" statement is automatically calling it. - /// - /// - public class CnmSynchronizedCache : ICnmCache - { - /// - /// The cache object. - /// - private readonly ICnmCache m_cache; - - /// - /// Synchronization root. - /// - private readonly object m_syncRoot; - - /// - /// Initializes a new instance of the class. - /// Initializes a new instance of the class. - /// - /// - /// The cache. - /// - private CnmSynchronizedCache( ICnmCache cache ) - { - m_cache = cache; - m_syncRoot = m_cache.SyncRoot; - } - - /// - /// Returns a wrapper that is synchronized (thread safe). - /// - /// - /// The to synchronize. - /// - /// - /// A wrapper that is synchronized (thread safe). - /// - /// - /// is null. - /// - public static ICnmCache Synchronized( ICnmCache cache ) - { - if( cache == null ) - throw new ArgumentNullException( "cache" ); - return cache.IsSynchronized ? cache : new CnmSynchronizedCache( cache ); - } - - #region Nested type: SynchronizedEnumerator - - /// - /// Synchronized enumerator. - /// - private class SynchronizedEnumerator : IEnumerator> - { - /// - /// Enumerator that is being synchronized. - /// - private readonly IEnumerator> m_enumerator; - - /// - /// Synchronization root. - /// - private object m_syncRoot; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The enumerator that is being synchronized. - /// - /// - /// The sync root. - /// - public SynchronizedEnumerator( IEnumerator> enumerator, object syncRoot ) - { - m_syncRoot = syncRoot; - m_enumerator = enumerator; - Monitor.Enter( m_syncRoot ); - } - - /// - /// Finalizes an instance of the class. - /// - ~SynchronizedEnumerator() - { - Dispose(); - } - - #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 { return m_enumerator.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; } - } - - /// - /// Releases synchronization lock. - /// - public void Dispose() - { - if( m_syncRoot != null ) - { - Monitor.Exit( m_syncRoot ); - m_syncRoot = null; - } - - m_enumerator.Dispose(); - GC.SuppressFinalize( this ); - } - - /// - /// 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() - { - return m_enumerator.MoveNext(); - } - - /// - /// 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. - /// - public void Reset() - { - m_enumerator.Reset(); - } - - #endregion - } - - #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 - { - lock( m_syncRoot ) - { - return m_cache.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 - { - lock( m_syncRoot ) - { - return m_cache.ExpirationTime; - } - } - - set - { - lock( m_syncRoot ) - { - m_cache.ExpirationTime = value; - } - } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.IsCountLimited; - } - } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.IsSizeLimited; - } - } - } - - /// - /// 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 true; } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.IsTimeLimited; - } - } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.MaxCount; - } - } - - set - { - lock( m_syncRoot ) - { - m_cache.MaxCount = value; - } - } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.MaxElementSize; - } - } - } - - /// - /// 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. - /// - /// - /// value is less than 0. - /// - /// - /// - public long MaxSize - { - get - { - lock( m_syncRoot ) - { - return m_cache.MaxSize; - } - } - - set - { - lock( m_syncRoot ) - { - m_cache.MaxSize = value; - } - } - } - - /// - /// 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 - { - lock( m_syncRoot ) - { - return m_cache.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() - { - lock( m_syncRoot ) - { - m_cache.Clear(); - } - } - - /// - /// Returns an enumerator that iterates through the elements stored to . - /// - /// - /// A that can be used to iterate through the collection. - /// - /// 1 - public IEnumerator> GetEnumerator() - { - lock( m_syncRoot ) - { - return new SynchronizedEnumerator( m_cache.GetEnumerator(), m_syncRoot ); - } - } - - /// - /// 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() - { - lock( m_syncRoot ) - { - m_cache.PurgeExpired(); - } - } - - /// - /// Removes element associated with from the . - /// - /// - /// The key that is associated with element to remove from the . - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - public void Remove( TKey key ) - { - lock( m_syncRoot ) - { - m_cache.Remove( key ); - } - } - - /// - /// 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 ) - { - lock( m_syncRoot ) - { - m_cache.RemoveRange( keys ); - } - } - - /// - /// 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 ) - { - lock( m_syncRoot ) - { - return m_cache.Set( key, value, size ); - } - } - - /// - /// 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 ) - { - lock( m_syncRoot ) - { - return m_cache.TryGetValue( key, out value ); - } - } - - /// - /// Returns an enumerator that iterates through the elements stored to . - /// - /// - /// A that can be used to iterate through the collection. - /// - /// 1 - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } -} +/* + * 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.Threading; + +namespace OpenSim.Framework +{ + /// + /// Synchronized Cenome cache wrapper. + /// + /// + /// The type of keys in the cache. + /// + /// + /// The type of values in the cache. + /// + /// + /// + /// Enumerator will block other threads, until enumerator's method is called. + /// "foreach" statement is automatically calling it. + /// + /// + public class CnmSynchronizedCache : ICnmCache + { + /// + /// The cache object. + /// + private readonly ICnmCache m_cache; + + /// + /// Synchronization root. + /// + private readonly object m_syncRoot; + + /// + /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + /// + /// The cache. + /// + private CnmSynchronizedCache( ICnmCache cache ) + { + m_cache = cache; + m_syncRoot = m_cache.SyncRoot; + } + + /// + /// Returns a wrapper that is synchronized (thread safe). + /// + /// + /// The to synchronize. + /// + /// + /// A wrapper that is synchronized (thread safe). + /// + /// + /// is null. + /// + public static ICnmCache Synchronized( ICnmCache cache ) + { + if( cache == null ) + throw new ArgumentNullException( "cache" ); + return cache.IsSynchronized ? cache : new CnmSynchronizedCache( cache ); + } + + #region Nested type: SynchronizedEnumerator + + /// + /// Synchronized enumerator. + /// + private class SynchronizedEnumerator : IEnumerator> + { + /// + /// Enumerator that is being synchronized. + /// + private readonly IEnumerator> m_enumerator; + + /// + /// Synchronization root. + /// + private object m_syncRoot; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The enumerator that is being synchronized. + /// + /// + /// The sync root. + /// + public SynchronizedEnumerator( IEnumerator> enumerator, object syncRoot ) + { + m_syncRoot = syncRoot; + m_enumerator = enumerator; + Monitor.Enter( m_syncRoot ); + } + + /// + /// Finalizes an instance of the class. + /// + ~SynchronizedEnumerator() + { + Dispose(); + } + + #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 { return m_enumerator.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; } + } + + /// + /// Releases synchronization lock. + /// + public void Dispose() + { + if( m_syncRoot != null ) + { + Monitor.Exit( m_syncRoot ); + m_syncRoot = null; + } + + m_enumerator.Dispose(); + GC.SuppressFinalize( this ); + } + + /// + /// 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() + { + return m_enumerator.MoveNext(); + } + + /// + /// 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. + /// + public void Reset() + { + m_enumerator.Reset(); + } + + #endregion + } + + #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 + { + lock( m_syncRoot ) + { + return m_cache.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 + { + lock( m_syncRoot ) + { + return m_cache.ExpirationTime; + } + } + + set + { + lock( m_syncRoot ) + { + m_cache.ExpirationTime = value; + } + } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.IsCountLimited; + } + } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.IsSizeLimited; + } + } + } + + /// + /// 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 true; } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.IsTimeLimited; + } + } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.MaxCount; + } + } + + set + { + lock( m_syncRoot ) + { + m_cache.MaxCount = value; + } + } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.MaxElementSize; + } + } + } + + /// + /// 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. + /// + /// + /// value is less than 0. + /// + /// + /// + public long MaxSize + { + get + { + lock( m_syncRoot ) + { + return m_cache.MaxSize; + } + } + + set + { + lock( m_syncRoot ) + { + m_cache.MaxSize = value; + } + } + } + + /// + /// 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 + { + lock( m_syncRoot ) + { + return m_cache.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() + { + lock( m_syncRoot ) + { + m_cache.Clear(); + } + } + + /// + /// Returns an enumerator that iterates through the elements stored to . + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator> GetEnumerator() + { + lock( m_syncRoot ) + { + return new SynchronizedEnumerator( m_cache.GetEnumerator(), m_syncRoot ); + } + } + + /// + /// 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() + { + lock( m_syncRoot ) + { + m_cache.PurgeExpired(); + } + } + + /// + /// Removes element associated with from the . + /// + /// + /// The key that is associated with element to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public void Remove( TKey key ) + { + lock( m_syncRoot ) + { + m_cache.Remove( key ); + } + } + + /// + /// 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 ) + { + lock( m_syncRoot ) + { + m_cache.RemoveRange( keys ); + } + } + + /// + /// 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 ) + { + lock( m_syncRoot ) + { + return m_cache.Set( key, value, size ); + } + } + + /// + /// 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 ) + { + lock( m_syncRoot ) + { + return m_cache.TryGetValue( key, out value ); + } + } + + /// + /// Returns an enumerator that iterates through the elements stored to . + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/ICnmCache.cs b/OpenSim/Framework/ICnmCache.cs index cba8a7f0f9..2f62189594 100644 --- a/OpenSim/Framework/ICnmCache.cs +++ b/OpenSim/Framework/ICnmCache.cs @@ -1,441 +1,441 @@ -/* - * 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.Generic; - -namespace OpenSim.Framework -{ - /// - /// Represent generic cache to store key/value pairs (elements) limited by time, size and count of elements. - /// - /// - /// The type of keys in the cache. - /// - /// - /// The type of values in the cache. - /// - /// - /// - /// Cache store limitations: - /// - /// - /// - /// Limitation - /// Description - /// - /// - /// Time - /// - /// Element that is not accessed through or in last are - /// removed from the cache automatically. Depending on implementation of the cache some of elements may stay longer in cache. - /// returns , if cache is limited by time. - /// - /// - /// - /// Count - /// - /// When adding an new element to cache that already have of elements, cache will remove less recently - /// used element(s) from the cache, until element fits to cache. - /// returns , if cache is limiting element count. - /// - /// - /// - /// Size - /// - /// - /// When adding an new element to cache that already have of elements, cache will remove less recently - /// used element(s) from the cache, until element fits to cache. - /// returns , if cache is limiting total size of elements. - /// Normally size is bytes used by element in the cache. But it can be any other suitable unit of measure. - /// - /// - /// - /// - /// - public interface ICnmCache : IEnumerable> - { - /// - /// 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. - /// - /// - /// - /// - /// - /// - int Count { get; } - - /// - /// 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 . - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - TimeSpan ExpirationTime { get; set; } - - /// - /// 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. - /// - /// - /// - /// - bool IsSynchronized { get; } - - /// - /// 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. - /// - /// - /// - /// - /// - /// - bool IsCountLimited { get; } - - /// - /// 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. - /// - /// - /// - /// - /// - /// - /// - bool IsSizeLimited { get; } - - /// - /// 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. - /// - /// - /// - /// - /// - bool IsTimeLimited { get; } - - /// - /// 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. - /// - /// - int MaxCount { get; set; } - - /// - /// Gets maximal allowed element size. - /// - /// - /// Maximal allowed element size. - /// - /// - /// - /// If element's size is larger than , then element is - /// not added to the . - /// - /// - /// - /// - /// - /// - long MaxElementSize { get; } - - /// - /// 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. - /// - /// - /// value is less than 0. - /// - /// - /// - long MaxSize { get; set; } - - /// - /// 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. - /// - /// - /// - /// - /// - /// - /// - long Size { get; } - - /// - /// 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 - /// . - /// - /// - /// - /// - object SyncRoot { get; } - - /// - /// Removes all elements from the . - /// - /// - /// - /// - /// - /// - void Clear(); - - /// - /// 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - void PurgeExpired(); - - /// - /// Removes element associated with from the . - /// - /// - /// The key that is associated with element to remove from the . - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - void Remove( TKey key ); - - /// - /// Removes elements that are associated with one of from the . - /// - /// - /// The keys that are associated with elements to remove from the . - /// - /// - /// is . - /// - /// - /// - /// - /// - /// - void RemoveRange( IEnumerable keys ); - - /// - /// 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. - /// - /// - /// - /// - /// - /// - /// - /// - /// - bool Set( TKey key, TValue value, long size ); - - /// - /// 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 . - /// - /// - /// - /// - /// - /// - bool TryGetValue( TKey key, out TValue value ); - } -} +/* + * 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.Generic; + +namespace OpenSim.Framework +{ + /// + /// Represent generic cache to store key/value pairs (elements) limited by time, size and count of elements. + /// + /// + /// The type of keys in the cache. + /// + /// + /// The type of values in the cache. + /// + /// + /// + /// Cache store limitations: + /// + /// + /// + /// Limitation + /// Description + /// + /// + /// Time + /// + /// Element that is not accessed through or in last are + /// removed from the cache automatically. Depending on implementation of the cache some of elements may stay longer in cache. + /// returns , if cache is limited by time. + /// + /// + /// + /// Count + /// + /// When adding an new element to cache that already have of elements, cache will remove less recently + /// used element(s) from the cache, until element fits to cache. + /// returns , if cache is limiting element count. + /// + /// + /// + /// Size + /// + /// + /// When adding an new element to cache that already have of elements, cache will remove less recently + /// used element(s) from the cache, until element fits to cache. + /// returns , if cache is limiting total size of elements. + /// Normally size is bytes used by element in the cache. But it can be any other suitable unit of measure. + /// + /// + /// + /// + /// + public interface ICnmCache : IEnumerable> + { + /// + /// 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. + /// + /// + /// + /// + /// + /// + int Count { get; } + + /// + /// 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 . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + TimeSpan ExpirationTime { get; set; } + + /// + /// 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. + /// + /// + /// + /// + bool IsSynchronized { get; } + + /// + /// 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. + /// + /// + /// + /// + /// + /// + bool IsCountLimited { get; } + + /// + /// 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. + /// + /// + /// + /// + /// + /// + /// + bool IsSizeLimited { get; } + + /// + /// 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. + /// + /// + /// + /// + /// + bool IsTimeLimited { get; } + + /// + /// 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. + /// + /// + int MaxCount { get; set; } + + /// + /// Gets maximal allowed element size. + /// + /// + /// Maximal allowed element size. + /// + /// + /// + /// If element's size is larger than , then element is + /// not added to the . + /// + /// + /// + /// + /// + /// + long MaxElementSize { get; } + + /// + /// 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. + /// + /// + /// value is less than 0. + /// + /// + /// + long MaxSize { get; set; } + + /// + /// 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. + /// + /// + /// + /// + /// + /// + /// + long Size { get; } + + /// + /// 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 + /// . + /// + /// + /// + /// + object SyncRoot { get; } + + /// + /// Removes all elements from the . + /// + /// + /// + /// + /// + /// + void Clear(); + + /// + /// 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + void PurgeExpired(); + + /// + /// Removes element associated with from the . + /// + /// + /// The key that is associated with element to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + void Remove( TKey key ); + + /// + /// Removes elements that are associated with one of from the . + /// + /// + /// The keys that are associated with elements to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + void RemoveRange( IEnumerable keys ); + + /// + /// 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. + /// + /// + /// + /// + /// + /// + /// + /// + /// + bool Set( TKey key, TValue value, long size ); + + /// + /// 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 . + /// + /// + /// + /// + /// + /// + bool TryGetValue( TKey key, out TValue value ); + } +} diff --git a/OpenSim/Framework/PrimeNumberHelper.cs b/OpenSim/Framework/PrimeNumberHelper.cs index e4bf615d4e..f533f4a867 100644 --- a/OpenSim/Framework/PrimeNumberHelper.cs +++ b/OpenSim/Framework/PrimeNumberHelper.cs @@ -1,98 +1,98 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// -// -// -// -// -// -// -------------------------------------------------------------------------------------------------------------------- - -using System; - -namespace OpenSim.Framework -{ - /// - /// Utility class that is used to find small prime numbers and test is number prime number. - /// - public static class PrimeNumberHelper - { - /// - /// Precalculated prime numbers. - /// - private static readonly int[] Primes = new int[] - { - 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, - 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, - 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, - 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, - 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, - 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, - 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, - 5999471, 7199369 - }; - - /// - /// Get prime number that is equal or larger than . - /// - /// - /// Minimal returned prime number. - /// - /// - /// Primer number that is equal or larger than . If is too large, return -1. - /// - public static int GetPrime( int min ) - { - if( min <= 2 ) - return 2; - - if( Primes[ Primes.Length - 1 ] < min ) - { - for( int i = min | 1 ; i < 0x7FFFFFFF ; i += 2 ) - { - if( IsPrime( i ) ) - return i; - } - - return -1; - } - - for( int i = Primes.Length - 2 ; i >= 0 ; i-- ) - { - if( min == Primes[ i ] ) - return min; - - if( min > Primes[ i ] ) - return Primes[ i + 1 ]; - } - - return 2; - } - - /// - /// Just basic Sieve of Eratosthenes prime number test. - /// - /// - /// Number that is tested. - /// - /// - /// true, if is prime number; otherwise false. - /// - public static bool IsPrime( int candinate ) - { - if( (candinate & 1) == 0 ) - - // Even number - only prime if 2 - return candinate == 2; - - int upperBound = (int) Math.Sqrt( candinate ); - for( int i = 3 ; i < upperBound ; i += 2 ) - { - if( candinate % i == 0 ) - return false; - } - - return true; - } - } -} +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// +// +// +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; + +namespace OpenSim.Framework +{ + /// + /// Utility class that is used to find small prime numbers and test is number prime number. + /// + public static class PrimeNumberHelper + { + /// + /// Precalculated prime numbers. + /// + private static readonly int[] Primes = new int[] + { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, + 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, + 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, + 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, + 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, + 5999471, 7199369 + }; + + /// + /// Get prime number that is equal or larger than . + /// + /// + /// Minimal returned prime number. + /// + /// + /// Primer number that is equal or larger than . If is too large, return -1. + /// + public static int GetPrime( int min ) + { + if( min <= 2 ) + return 2; + + if( Primes[ Primes.Length - 1 ] < min ) + { + for( int i = min | 1 ; i < 0x7FFFFFFF ; i += 2 ) + { + if( IsPrime( i ) ) + return i; + } + + return -1; + } + + for( int i = Primes.Length - 2 ; i >= 0 ; i-- ) + { + if( min == Primes[ i ] ) + return min; + + if( min > Primes[ i ] ) + return Primes[ i + 1 ]; + } + + return 2; + } + + /// + /// Just basic Sieve of Eratosthenes prime number test. + /// + /// + /// Number that is tested. + /// + /// + /// true, if is prime number; otherwise false. + /// + public static bool IsPrime( int candinate ) + { + if( (candinate & 1) == 0 ) + + // Even number - only prime if 2 + return candinate == 2; + + int upperBound = (int) Math.Sqrt( candinate ); + for( int i = 3 ; i < upperBound ; i += 2 ) + { + if( candinate % i == 0 ) + return false; + } + + return true; + } + } +} diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs index 00a8143fc7..bbc9c9b32b 100644 --- a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs @@ -1,382 +1,382 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// -// -// -// -// -// -// -------------------------------------------------------------------------------------------------------------------- -using System; -using System.Reflection; -using log4net; -using Nini.Config; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; - -namespace OpenSim.Region.CoreModules.Asset -{ - /// - /// Cenome memory asset cache. - /// - /// - /// - /// Cache is enabled by setting "AssetCaching" configuration to value "CenomeMemoryAssetCache". - /// When cache is successfully enable log should have message - /// "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = XXX bytes, MaxCount = XXX, ExpirationTime = XXX)". - /// - /// - /// Cache's size is limited by two parameters: - /// maximal allowed size in bytes and maximal allowed asset count. When new asset - /// is added to cache that have achieved either size or count limitation, cache - /// will automatically remove less recently used assets from cache. Additionally - /// asset's lifetime is controlled by expiration time. - /// - /// - /// - /// - /// Configuration - /// Description - /// - /// - /// MaxSize - /// Maximal size of the cache in bytes. Default value: 128MB (134 217 728 bytes). - /// - /// - /// MaxCount - /// Maximal count of assets stored to cache. Default value: 4096 assets. - /// - /// - /// ExpirationTime - /// Asset's expiration time in minutes. Default value: 30 minutes. - /// - /// - /// - /// - /// - /// Enabling Cenome Asset Cache: - /// - /// [Modules] - /// AssetCaching = "CenomeMemoryAssetCache" - /// - /// Setting size and expiration time limitations: - /// - /// [AssetService] - /// ; 256 MB (default: 134217728) - /// MaxSize = 268435456 - /// ; How many assets it is possible to store cache (default: 4096) - /// MaxCount = 16384 - /// ; Expiration time - 1 hour (default: 30 minutes) - /// ExpirationTime = 60 - /// - /// - public class CenomeMemoryAssetCache : IImprovedAssetCache, ISharedRegionModule - { - /// - /// Cache's default maximal asset count. - /// - /// - /// - /// Assuming that average asset size is about 32768 bytes. - /// - /// - public const int DefaultMaxCount = 4096; - - /// - /// Default maximal size of the cache in bytes - /// - /// - /// - /// 128MB = 128 * 1024^2 = 134 217 728 bytes. - /// - /// - public const long DefaultMaxSize = 134217728; - - /// - /// Asset's default expiration time in the cache. - /// - public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 ); - - /// - /// Log manager instance. - /// - private static readonly ILog Log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType ); - - /// - /// Cache object. - /// - private ICnmCache m_cache; - - /// - /// Count of cache commands - /// - private int m_cachedCount; - - /// - /// How many gets before dumping statistics - /// - /// - /// If 0 or less, then disabled. - /// - private int m_debugEpoch; - - /// - /// Is Cenome asset cache enabled. - /// - private bool m_enabled; - - /// - /// Count of get requests - /// - private int m_getCount; - - /// - /// How many hits - /// - private int m_hitCount; - - /// - /// Initialize asset cache module with default parameters. - /// - public void Initialize() - { - Initialize( DefaultMaxSize, DefaultMaxCount, DefaultExpirationTime ); - } - - /// - /// Initialize asset cache module, with custom parameters. - /// - /// - /// Cache's maximal size in bytes. - /// - /// - /// Cache's maximal count of assets. - /// - /// - /// Asset's expiration time. - /// - public void Initialize( long maximalSize, int maximalCount, TimeSpan expirationTime ) - { - if( maximalSize <= 0 || maximalCount <= 0 ) - { - Log.Info( "[ASSET CACHE]: Cenome asset cache is not enabled." ); - m_enabled = false; - return; - } - - if( expirationTime <= TimeSpan.Zero ) - { - // Disable expiration time - expirationTime = TimeSpan.MaxValue; - } - - // Create cache and add synchronization wrapper over it - m_cache = - CnmSynchronizedCache.Synchronized( new CnmMemoryCache( - maximalSize, maximalCount, expirationTime ) ); - m_enabled = true; - Log.InfoFormat( - "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = {0} bytes, MaxCount = {1}, ExpirationTime = {2})", - maximalSize, - maximalCount, - expirationTime ); - } - - #region IImprovedAssetCache Members - - /// - /// Cache asset. - /// - /// - /// The asset that is being cached. - /// - public void Cache( AssetBase asset ) - { - long size = asset.Data != null ? asset.Data.Length : 1; - m_cache.Set( asset.ID, asset, size ); - m_cachedCount++; - } - - /// - /// Clear asset cache. - /// - public void Clear() - { - m_cache.Clear(); - } - - /// - /// Expire (remove) asset stored to cache. - /// - /// - /// The expired asset's id. - /// - public void Expire( string id ) - { - m_cache.Remove( id ); - } - - /// - /// Get asset stored - /// - /// - /// The asset's id. - /// - /// - /// Asset if it is found from cache; otherwise . - /// - /// - /// - /// Caller should always check that is return value . - /// Cache doesn't guarantee in any situation that asset is stored to it. - /// - /// - public AssetBase Get( string id ) - { - m_getCount++; - AssetBase assetBase; - if( m_cache.TryGetValue( id, out assetBase ) ) - m_hitCount++; - - if( m_getCount == m_debugEpoch ) - { - Log.InfoFormat( - "[ASSET CACHE]: Cached = {0}, Get = {1}, Hits = {2}%, Size = {3} bytes, Avg. A. Size = {4} bytes", - m_cachedCount, - m_getCount, - ((double) m_hitCount / m_getCount) * 100.0, - m_cache.Size, - m_cache.Size / m_cache.Count ); - m_getCount = 0; - m_hitCount = 0; - m_cachedCount = 0; - } - - return assetBase; - } - - #endregion - - #region ISharedRegionModule Members - - /// - /// Gets region module's name. - /// - public string Name - { - get { return "CenomeMemoryAssetCache"; } - } - - /// - /// New region is being added to server. - /// - /// - /// Region's scene. - /// - public void AddRegion( Scene scene ) - { - if( m_enabled ) - scene.RegisterModuleInterface( this ); - } - - /// - /// Close region module. - /// - public void Close() - { - m_enabled = false; - m_cache.Clear(); - m_cache = null; - } - - /// - /// Initialize region module. - /// - /// - /// Configuration source. - /// - public void Initialise( IConfigSource source ) - { - m_cache = null; - m_enabled = false; - - IConfig moduleConfig = source.Configs[ "Modules" ]; - if( moduleConfig == null ) - return; - - string name = moduleConfig.GetString( "AssetCaching" ); - Log.DebugFormat( "[XXX] name = {0} (this module's name: {1}", name, Name ); - - if( name != Name ) - return; - - // This module is used - long maxSize = DefaultMaxSize; - int maxCount = DefaultMaxCount; - TimeSpan expirationTime = DefaultExpirationTime; - - IConfig assetConfig = source.Configs[ "AssetCache" ]; - if( assetConfig != null ) - { - // Get optional configurations - maxSize = assetConfig.GetLong( "MaxSize", DefaultMaxSize ); - maxCount = assetConfig.GetInt( "MaxCount", DefaultMaxCount ); - expirationTime = - TimeSpan.FromMinutes( assetConfig.GetInt( "ExpirationTime", (int) DefaultExpirationTime.TotalMinutes ) ); - - // Debugging purposes only - m_debugEpoch = assetConfig.GetInt( "DebugEpoch", 0 ); - } - - Initialize( maxSize, maxCount, expirationTime ); - } - - /// - /// Initialization post handling. - /// - /// - /// - /// Modules can use this to initialize connection with other modules. - /// - /// - public void PostInitialise() - { - } - - /// - /// Region has been loaded. - /// - /// - /// Region's scene. - /// - /// - /// - /// This is needed for all module types. Modules will register - /// Interfaces with scene in AddScene, and will also need a means - /// to access interfaces registered by other modules. Without - /// this extra method, a module attempting to use another modules' - /// interface would be successful only depending on load order, - /// which can't be depended upon, or modules would need to resort - /// to ugly kludges to attempt to request interfaces when needed - /// and unnecessary caching logic repeated in all modules. - /// The extra function stub is just that much cleaner. - /// - /// - public void RegionLoaded( Scene scene ) - { - } - - /// - /// Region is being removed. - /// - /// - /// Region scene that is being removed. - /// - public void RemoveRegion( Scene scene ) - { - } - - #endregion - } -} +// -------------------------------------------------------------------------------------------------------------------- +// +// +// +// +// +// +// +// -------------------------------------------------------------------------------------------------------------------- +using System; +using System.Reflection; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Asset +{ + /// + /// Cenome memory asset cache. + /// + /// + /// + /// Cache is enabled by setting "AssetCaching" configuration to value "CenomeMemoryAssetCache". + /// When cache is successfully enable log should have message + /// "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = XXX bytes, MaxCount = XXX, ExpirationTime = XXX)". + /// + /// + /// Cache's size is limited by two parameters: + /// maximal allowed size in bytes and maximal allowed asset count. When new asset + /// is added to cache that have achieved either size or count limitation, cache + /// will automatically remove less recently used assets from cache. Additionally + /// asset's lifetime is controlled by expiration time. + /// + /// + /// + /// + /// Configuration + /// Description + /// + /// + /// MaxSize + /// Maximal size of the cache in bytes. Default value: 128MB (134 217 728 bytes). + /// + /// + /// MaxCount + /// Maximal count of assets stored to cache. Default value: 4096 assets. + /// + /// + /// ExpirationTime + /// Asset's expiration time in minutes. Default value: 30 minutes. + /// + /// + /// + /// + /// + /// Enabling Cenome Asset Cache: + /// + /// [Modules] + /// AssetCaching = "CenomeMemoryAssetCache" + /// + /// Setting size and expiration time limitations: + /// + /// [AssetService] + /// ; 256 MB (default: 134217728) + /// MaxSize = 268435456 + /// ; How many assets it is possible to store cache (default: 4096) + /// MaxCount = 16384 + /// ; Expiration time - 1 hour (default: 30 minutes) + /// ExpirationTime = 60 + /// + /// + public class CenomeMemoryAssetCache : IImprovedAssetCache, ISharedRegionModule + { + /// + /// Cache's default maximal asset count. + /// + /// + /// + /// Assuming that average asset size is about 32768 bytes. + /// + /// + public const int DefaultMaxCount = 4096; + + /// + /// Default maximal size of the cache in bytes + /// + /// + /// + /// 128MB = 128 * 1024^2 = 134 217 728 bytes. + /// + /// + public const long DefaultMaxSize = 134217728; + + /// + /// Asset's default expiration time in the cache. + /// + public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes( 30.0 ); + + /// + /// Log manager instance. + /// + private static readonly ILog Log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType ); + + /// + /// Cache object. + /// + private ICnmCache m_cache; + + /// + /// Count of cache commands + /// + private int m_cachedCount; + + /// + /// How many gets before dumping statistics + /// + /// + /// If 0 or less, then disabled. + /// + private int m_debugEpoch; + + /// + /// Is Cenome asset cache enabled. + /// + private bool m_enabled; + + /// + /// Count of get requests + /// + private int m_getCount; + + /// + /// How many hits + /// + private int m_hitCount; + + /// + /// Initialize asset cache module with default parameters. + /// + public void Initialize() + { + Initialize( DefaultMaxSize, DefaultMaxCount, DefaultExpirationTime ); + } + + /// + /// Initialize asset cache module, with custom parameters. + /// + /// + /// Cache's maximal size in bytes. + /// + /// + /// Cache's maximal count of assets. + /// + /// + /// Asset's expiration time. + /// + public void Initialize( long maximalSize, int maximalCount, TimeSpan expirationTime ) + { + if( maximalSize <= 0 || maximalCount <= 0 ) + { + Log.Info( "[ASSET CACHE]: Cenome asset cache is not enabled." ); + m_enabled = false; + return; + } + + if( expirationTime <= TimeSpan.Zero ) + { + // Disable expiration time + expirationTime = TimeSpan.MaxValue; + } + + // Create cache and add synchronization wrapper over it + m_cache = + CnmSynchronizedCache.Synchronized( new CnmMemoryCache( + maximalSize, maximalCount, expirationTime ) ); + m_enabled = true; + Log.InfoFormat( + "[ASSET CACHE]: Cenome asset cache enabled (MaxSize = {0} bytes, MaxCount = {1}, ExpirationTime = {2})", + maximalSize, + maximalCount, + expirationTime ); + } + + #region IImprovedAssetCache Members + + /// + /// Cache asset. + /// + /// + /// The asset that is being cached. + /// + public void Cache( AssetBase asset ) + { + long size = asset.Data != null ? asset.Data.Length : 1; + m_cache.Set( asset.ID, asset, size ); + m_cachedCount++; + } + + /// + /// Clear asset cache. + /// + public void Clear() + { + m_cache.Clear(); + } + + /// + /// Expire (remove) asset stored to cache. + /// + /// + /// The expired asset's id. + /// + public void Expire( string id ) + { + m_cache.Remove( id ); + } + + /// + /// Get asset stored + /// + /// + /// The asset's id. + /// + /// + /// Asset if it is found from cache; otherwise . + /// + /// + /// + /// Caller should always check that is return value . + /// Cache doesn't guarantee in any situation that asset is stored to it. + /// + /// + public AssetBase Get( string id ) + { + m_getCount++; + AssetBase assetBase; + if( m_cache.TryGetValue( id, out assetBase ) ) + m_hitCount++; + + if( m_getCount == m_debugEpoch ) + { + Log.InfoFormat( + "[ASSET CACHE]: Cached = {0}, Get = {1}, Hits = {2}%, Size = {3} bytes, Avg. A. Size = {4} bytes", + m_cachedCount, + m_getCount, + ((double) m_hitCount / m_getCount) * 100.0, + m_cache.Size, + m_cache.Size / m_cache.Count ); + m_getCount = 0; + m_hitCount = 0; + m_cachedCount = 0; + } + + return assetBase; + } + + #endregion + + #region ISharedRegionModule Members + + /// + /// Gets region module's name. + /// + public string Name + { + get { return "CenomeMemoryAssetCache"; } + } + + /// + /// New region is being added to server. + /// + /// + /// Region's scene. + /// + public void AddRegion( Scene scene ) + { + if( m_enabled ) + scene.RegisterModuleInterface( this ); + } + + /// + /// Close region module. + /// + public void Close() + { + m_enabled = false; + m_cache.Clear(); + m_cache = null; + } + + /// + /// Initialize region module. + /// + /// + /// Configuration source. + /// + public void Initialise( IConfigSource source ) + { + m_cache = null; + m_enabled = false; + + IConfig moduleConfig = source.Configs[ "Modules" ]; + if( moduleConfig == null ) + return; + + string name = moduleConfig.GetString( "AssetCaching" ); + Log.DebugFormat( "[XXX] name = {0} (this module's name: {1}", name, Name ); + + if( name != Name ) + return; + + // This module is used + long maxSize = DefaultMaxSize; + int maxCount = DefaultMaxCount; + TimeSpan expirationTime = DefaultExpirationTime; + + IConfig assetConfig = source.Configs[ "AssetCache" ]; + if( assetConfig != null ) + { + // Get optional configurations + maxSize = assetConfig.GetLong( "MaxSize", DefaultMaxSize ); + maxCount = assetConfig.GetInt( "MaxCount", DefaultMaxCount ); + expirationTime = + TimeSpan.FromMinutes( assetConfig.GetInt( "ExpirationTime", (int) DefaultExpirationTime.TotalMinutes ) ); + + // Debugging purposes only + m_debugEpoch = assetConfig.GetInt( "DebugEpoch", 0 ); + } + + Initialize( maxSize, maxCount, expirationTime ); + } + + /// + /// Initialization post handling. + /// + /// + /// + /// Modules can use this to initialize connection with other modules. + /// + /// + public void PostInitialise() + { + } + + /// + /// Region has been loaded. + /// + /// + /// Region's scene. + /// + /// + /// + /// This is needed for all module types. Modules will register + /// Interfaces with scene in AddScene, and will also need a means + /// to access interfaces registered by other modules. Without + /// this extra method, a module attempting to use another modules' + /// interface would be successful only depending on load order, + /// which can't be depended upon, or modules would need to resort + /// to ugly kludges to attempt to request interfaces when needed + /// and unnecessary caching logic repeated in all modules. + /// The extra function stub is just that much cleaner. + /// + /// + public void RegionLoaded( Scene scene ) + { + } + + /// + /// Region is being removed. + /// + /// + /// Region scene that is being removed. + /// + public void RemoveRegion( Scene scene ) + { + } + + #endregion + } +}