// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections; using System.Collections.Generic; namespace Microsoft.SqlTools.ServiceLayer.Utility { /// /// Collection class that permits storage of over int.MaxValue items. This is performed /// by using a 2D list of lists. The internal lists are only initialized as necessary. This /// collection implements IEnumerable to make it easier to run LINQ queries against it. /// /// /// This class is based on code from $\Data Tools\SSMS_Main\sql\ssms\core\DataStorage\ArrayList64.cs /// with additions to bring it up to .NET 4.5 standards /// /// Type of the values to store public class LongList : IEnumerable { #region Member Variables private int expandListSize = int.MaxValue; private List> expandedList; private readonly List shortList; #endregion /// /// Creates a new long list /// public LongList() { shortList = new List(); Count = 0; } #region Properties /// /// The total number of elements in the array /// public long Count { get; private set; } public T this[long index] { get { return GetItem(index); } } public int ExpandListSize { get { return this.expandListSize; } internal set { this.expandListSize = value; } } #endregion #region Public Methods /// /// Adds the specified value to the end of the list /// /// Value to add to the list /// Index of the item that was just added public long Add(T val) { if (Count <= this.ExpandListSize) { shortList.Add(val); } else // need to split values into several arrays { if (expandedList == null) { // very inefficient so delay as much as possible // immediately add 0th array expandedList = new List> {shortList}; } int arrayIndex = (int)(Count / this.ExpandListSize); // 0 based List arr; if (expandedList.Count <= arrayIndex) // need to make a new array { arr = new List(); expandedList.Add(arr); } else // use existing array { arr = expandedList[arrayIndex]; } arr.Add(val); } return (++Count); } /// /// Returns the item at the specified index /// /// Index of the item to return /// The item at the index specified public T GetItem(long index) { T val = default(T); if (Count <= this.ExpandListSize) { int i32Index = Convert.ToInt32(index); val = shortList[i32Index]; } else { int iArray32Index = (int) (Count / this.ExpandListSize); if (expandedList.Count > iArray32Index) { List arr = expandedList[iArray32Index]; int i32Index = (int) (Count % this.ExpandListSize); if (arr.Count > i32Index) { val = arr[i32Index]; } } } return val; } /// /// Removes an item at the specified location and shifts all the items after the provided /// index up by one. /// /// The index to remove from the list public void RemoveAt(long index) { if (Count <= this.ExpandListSize) { int iArray32MemberIndex = Convert.ToInt32(index); // 0 based shortList.RemoveAt(iArray32MemberIndex); } else // handle the case of multiple arrays { // find out which array it is in int arrayIndex = (int) (index / this.ExpandListSize); List arr = expandedList[arrayIndex]; // find out index into this array int iArray32MemberIndex = (int) (index % this.ExpandListSize); arr.RemoveAt(iArray32MemberIndex); // now shift members of the array back one int iArray32TotalIndex = (int) (Count / this.ExpandListSize); for (int i = arrayIndex + 1; i < iArray32TotalIndex; i++) { List arr1 = expandedList[i - 1]; List arr2 = expandedList[i]; arr1.Add(arr2[this.ExpandListSize - 1]); arr2.RemoveAt(0); } } --Count; } #endregion #region IEnumerable Implementation /// /// Returns a generic enumerator for enumeration of this LongList /// /// Enumerator for LongList public IEnumerator GetEnumerator() { return new LongListEnumerator(this); } /// /// Returns an enumerator for enumeration of this LongList /// /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion public class LongListEnumerator : IEnumerator { #region Member Variables /// /// The index into the list of the item that is the current item /// private long index; /// /// The current list that we're iterating over. /// private readonly LongList localList; #endregion /// /// Constructs a new enumerator for a given LongList /// /// The list to enumerate public LongListEnumerator(LongList list) { localList = list; index = 0; Current = default(TEt); } #region IEnumerator Implementation /// /// Returns the current item in the enumeration /// public TEt Current { get; private set; } object IEnumerator.Current { get { return Current; } } /// /// Moves to the next item in the list we're iterating over /// /// Whether or not the move was successful public bool MoveNext() { if (index < localList.Count) { Current = localList[index]; index++; return true; } Current = default(TEt); return false; } /// /// Resets the enumeration /// public void Reset() { index = 0; Current = default(TEt); } /// /// Disposal method. Does nothing. /// public void Dispose() { } #endregion } } }