예제 #1
0
        /// <summary>
        ///     Indexer for access to the item cache
        /// </summary>
        /// <param name="index">Item Index</param>
        /// <returns></returns>
        public T this[int index]
        {
            get
            {
                // iterates through the cache blocks to find the item
                foreach (CacheEntryBlock <T> block in _cacheBlocks)
                {
                    if (index >= block.FirstIndex && index <= block.LastIndex)
                    {
                        return(block.Items[index - block.FirstIndex]);
                    }
                }

                return(default(T));
            }

            set
            {
                // iterates through the cache blocks to find the right block
                for (int i = 0; i < _cacheBlocks.Count; i++)
                {
                    CacheEntryBlock <T> block = _cacheBlocks[i];
                    if (index >= block.FirstIndex && index <= block.LastIndex)
                    {
                        block.Items[index - block.FirstIndex] = value;

                        // register that we have the result in the cache
                        if (value != null)
                        {
                            _cachedResults.Add((uint)index, 1);
                        }

                        return;
                    }

                    // We have moved past the block where the item is supposed to live
                    if (block.FirstIndex > index)
                    {
                        AddOrExtendBlock(index, value, i);
                        return;
                    }
                }

                // No blocks exist, so creating a new block
                AddOrExtendBlock(index, value, _cacheBlocks.Count);
            }
        }
예제 #2
0
 // Compares the new ranges against the previous ones to see if they have changed
 private bool HasRangesChanged(ItemIndexRange[] ranges)
 {
     if (ranges.Length != cacheBlocks.Count)
     {
         return(true);
     }
     for (int i = 0; i < ranges.Length; i++)
     {
         ItemIndexRange      r     = ranges[i];
         CacheEntryBlock <T> block = this.cacheBlocks[i];
         if (r.FirstIndex != block.FirstIndex || r.LastIndex != block.lastIndex)
         {
             return(true);
         }
     }
     return(false);
 }
예제 #3
0
        // Extends an existing block if the item fits at the end, or creates a new block
        private void AddOrExtendBlock(int index, T value, int insertBeforeBlock)
        {
            if (insertBeforeBlock > 0)
            {
                CacheEntryBlock <T> block = cacheBlocks[insertBeforeBlock - 1];
                if (block.lastIndex == index - 1)
                {
                    T[] newItems = new T[block.Length + 1];
                    Array.Copy(block.Items, newItems, (int)block.Length);
                    newItems[block.Length] = value;
                    block.Length++;
                    block.Items = newItems;
                    return;
                }
            }
            CacheEntryBlock <T> newBlock = new CacheEntryBlock <T>()
            {
                FirstIndex = index, Length = 1, Items = new T[] { value }
            };

            cacheBlocks.Insert(insertBeforeBlock, newBlock);
        }
예제 #4
0
        /// <summary>
        /// Updates the desired item range of the cache, discarding items that are not needed,
        /// and figuring out which items need to be requested. It will then kick off a fetch if required.
        /// </summary>
        /// <param name="ranges">New set of ranges the cache should hold</param>
        public void UpdateRanges(ItemIndexRange[] ranges)
        {
            //Normalize ranges to get a unique set of discontinuous ranges
            ranges = NormalizeRanges(ranges);

            // Fail fast if the ranges haven't changed
            if (!HasRangesChanged(ranges))
            {
                return;
            }

            //To make the cache update easier, we'll create a new set of CacheEntryBlocks
            List <CacheEntryBlock <T> > newCacheBlocks = new List <CacheEntryBlock <T> >();

            foreach (ItemIndexRange range in ranges)
            {
                CacheEntryBlock <T> newBlock = new CacheEntryBlock <T>()
                {
                    FirstIndex = range.FirstIndex, Length = range.Length, Items = new T[range.Length]
                };
                newCacheBlocks.Add(newBlock);
            }

#if TRACE_DATASOURCE
            string s = "┌ " + debugName + ".UpdateRanges: ";
            foreach (ItemIndexRange range in ranges)
            {
                s += range.FirstIndex + "->" + range.LastIndex + " ";
            }
            Debug.WriteLine(s);
#endif
            //Copy over data to the new cache blocks from the old ones where there is overlap
            int lastTransferred = 0;
            for (int i = 0; i < ranges.Length; i++)
            {
                CacheEntryBlock <T> newBlock = newCacheBlocks[i];
                ItemIndexRange      range    = ranges[i];
                int j = lastTransferred;
                while (j < this.cacheBlocks.Count && this.cacheBlocks[j].FirstIndex <= ranges[i].LastIndex)
                {
                    ItemIndexRange      overlap, oldEntryRange;
                    ItemIndexRange[]    added, removed;
                    CacheEntryBlock <T> oldBlock = this.cacheBlocks[j];
                    oldEntryRange = new ItemIndexRange(oldBlock.FirstIndex, oldBlock.Length);
                    bool hasOverlap = oldEntryRange.DiffRanges(range, out overlap, out removed, out added);
                    if (hasOverlap)
                    {
                        Array.Copy(oldBlock.Items, overlap.FirstIndex - oldBlock.FirstIndex, newBlock.Items, overlap.FirstIndex - range.FirstIndex, (int)overlap.Length);
#if TRACE_DATASOURCE
                        Debug.WriteLine("│ Transfering cache items " + overlap.FirstIndex + "->" + overlap.LastIndex);
#endif
                    }
                    j++;
                    if (ranges.Length > i + 1 && oldBlock.lastIndex < ranges[i + 1].FirstIndex)
                    {
                        lastTransferred = j;
                    }
                }
            }
            //swap over to the new cache
            this.cacheBlocks = newCacheBlocks;

            //figure out what items need to be fetched because we don't have them in the cache
            this.requests = new ItemIndexRangeList(ranges);
            ItemIndexRangeList newCachedResults = new ItemIndexRangeList();

            // Use the previous knowlege of what we have cached to form the new list
            foreach (ItemIndexRange range in ranges)
            {
                foreach (ItemIndexRange cached in this.cachedResults)
                {
                    ItemIndexRange   overlap;
                    ItemIndexRange[] added, removed;
                    bool             hasOverlap = cached.DiffRanges(range, out overlap, out removed, out added);
                    if (hasOverlap)
                    {
                        newCachedResults.Add(overlap);
                    }
                }
            }
            // remove the data we know we have cached from the results
            foreach (ItemIndexRange range in newCachedResults)
            {
                this.requests.Subtract(range);
            }
            this.cachedResults = newCachedResults;

            startFetchData();

#if TRACE_DATASOURCE
            s = "└ Pending requests: ";
            foreach (ItemIndexRange range in this.requests)
            {
                s += range.FirstIndex + "->" + range.LastIndex + " ";
            }
            Debug.WriteLine(s);
#endif
        }