/// <summary> /// It is way to find needed block if we don't cache it yet. This method have O(n) complexity. /// In fact we just get last cached block and move next until we find block we need. Also we cache all new blocks until needed. /// </summary> /// <param name="index">Index to find information about block, which containes it.</param> /// <param name="searchBlockRange"><see cref="Range"/> to find index in it. It use to get better performance.</param> /// <returns>Information about block, which contain specified index.</returns> private BlockInfo LinearBlockInfo(int index, Range searchBlockRange) { lock (_locker) { Debug.Assert(index >= GetCachedElementCount()); if (!_blockCollection.IsValidRange(searchBlockRange)) { throw new ArgumentOutOfRangeException("searchBlockRange"); } //Move to start var startBlock = GetStartBlockInfoForLinear(); if (startBlock.Compare(index) == 0) { return(startBlock); } int commonStartIndex = startBlock.CommonStartIndex + startBlock.Count; Debug.Assert(startBlock.CommonStartIndex + startBlock.Count <= index); //Start block must be last cached block info Debug.Assert(startBlock.IndexOfBlock + 1 == _blocksInfoList.Count); //Add new blocks while we try to find needed block for (int i = startBlock.IndexOfBlock + 1; i < searchBlockRange.Index + searchBlockRange.Count; i++) { var elementCount = _blockCollection[i].Count; var newBlock = new BlockInfo(i, commonStartIndex, elementCount); _blocksInfoList.Add(newBlock); //If there is needed block if (index >= commonStartIndex && index < commonStartIndex + elementCount) { if (i + 1 == _blockCollection.Count) { _indexOfFirstChangedBlock = NoBlockChanges; } else { _indexOfFirstChangedBlock = i + 1; } return(newBlock); } commonStartIndex += elementCount; } throw new ArgumentOutOfRangeException("index"); } }