/// <summary> /// Returns the index of the entry to replace in a given set of the <see cref="Cache{TItem, UKey, VData}"/>; this could be an empty entry or an entry that should be replaced /// </summary> /// <param name="startIndex">The starting index of the set</param> /// <param name="endIndex">The ending index of the set</param> /// <returns>An index inclusively between <c>startIndex</c> and <c>endIndex</c> that should be replaced</returns> protected int GetReplacementIndex(int startIndex, int endIndex) { // readlock needed cacheLock.EnterReadLock(); try { // first check if we have an empty block to use int ans = GetFirstEmptyIndex(startIndex, endIndex); // if so use that for the put if (ans > -1) { return(ans); } // otherwise we'll need to use our IReplacementAlgorithm to figure out which block to use // Using ArraySegment wrapped in a ReadOnlyCollection we can referencially pass the set // we need for full inspection quickly without increasing memory footprint too much, since // we're not copying elements, but we don't risk the consumer trying to edit the cache (that's our job) ans = _replacementFinder.GetReplacementIndex( new ReadOnlyCollection <TItem>( (IList <TItem>) new ArraySegment <TItem>(_cacheArray, startIndex, endIndex - startIndex + 1))); // Make sure replacement strategy didn't do something crazy if (ans < 0 || ans > (endIndex - startIndex)) { throw new ArgumentException(string.Format("IReplacementAlgorithm ({0}) returned invalid index value.", _replacementFinder.GetType().FullName), "replacementAlgo"); } // determine index in full cache return(startIndex + ans); } finally { cacheLock.ExitReadLock(); } }