using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SuperSocket.Common { /// /// ArraySegmentList /// /// public class ArraySegmentList : IList where T : IEquatable { private IList> m_Segments; internal IList> Segments { get { return m_Segments; } } private ArraySegmentEx m_PrevSegment; private int m_PrevSegmentIndex; private int m_Count; /// /// Initializes a new instance of the class. /// public ArraySegmentList() { m_Segments = new List>(); } #region IList Members /// /// Determines the index of a specific item in the . /// /// The object to locate in the . /// /// The index of if found in the list; otherwise, -1. /// public int IndexOf(T item) { int index = 0; for (int i = 0; i < m_Segments.Count; i++) { var currentSegment = m_Segments[i]; int offset = currentSegment.Offset; for (int j = 0; j < currentSegment.Count; j++) { if (currentSegment.Array[j + offset].Equals(item)) return index; index++; } } return -1; } /// /// NotSupported /// public void Insert(int index, T item) { throw new NotSupportedException(); } /// /// NotSupported /// public void RemoveAt(int index) { throw new NotSupportedException(); } /// /// Gets or sets the element at the specified index. /// /// /// The element at the specified index. /// /// /// is not a valid index in the . /// /// /// /// The property is set and the is read-only. /// public T this[int index] { get { ArraySegmentEx segment; var internalIndex = GetElementInternalIndex(index, out segment); if (internalIndex < 0) throw new IndexOutOfRangeException(); return segment.Array[internalIndex]; } set { ArraySegmentEx segment; var internalIndex = GetElementInternalIndex(index, out segment); if (internalIndex < 0) throw new IndexOutOfRangeException(); segment.Array[internalIndex] = value; } } private int GetElementInternalIndex(int index, out ArraySegmentEx segment) { segment = null; if (index < 0 || index > Count - 1) return -1; if (index == 0) { m_PrevSegment = m_Segments[0]; m_PrevSegmentIndex = 0; segment = m_PrevSegment; return m_PrevSegment.Offset; } int compareValue = 0; if (m_PrevSegment != null) { if (index >= m_PrevSegment.From) { if (index <= m_PrevSegment.To) { segment = m_PrevSegment; return m_PrevSegment.Offset + index - m_PrevSegment.From; } else { compareValue = 1; } } else { compareValue = -1; } } int from, to; if (compareValue != 0) { from = m_PrevSegmentIndex + compareValue; var trySegment = m_Segments[from]; if (index >= trySegment.From && index <= trySegment.To) { segment = trySegment; return trySegment.Offset + index - trySegment.From; } from += compareValue; var currentSegment = m_Segments[from]; if (index >= currentSegment.From && index <= currentSegment.To) { m_PrevSegment = currentSegment; m_PrevSegmentIndex = from; segment = currentSegment; return currentSegment.Offset + index - currentSegment.From; } if (compareValue > 0) { from++; to = m_Segments.Count - 1; } else { var tmp = from - 1; from = 0; to = tmp; } } else { from = 0; to = m_Segments.Count - 1; } int segmentIndex = -1; var result = QuickSearchSegment(from, to, index, out segmentIndex); if (result != null) { m_PrevSegment = result; m_PrevSegmentIndex = segmentIndex; segment = m_PrevSegment; return result.Offset + index - result.From; } m_PrevSegment = null; return -1; } internal ArraySegmentEx QuickSearchSegment(int from, int to, int index, out int segmentIndex) { ArraySegmentEx segment; segmentIndex = -1; int diff = to - from; if (diff == 0) { segment = m_Segments[from]; if (index >= segment.From && index <= segment.To) { segmentIndex = from; return segment; } return null; } else if (diff == 1) { segment = m_Segments[from]; if (index >= segment.From && index <= segment.To) { segmentIndex = from; return segment; } segment = m_Segments[to]; if (index >= segment.From && index <= segment.To) { segmentIndex = to; return segment; } return null; } int middle = from + diff / 2; segment = m_Segments[middle]; if (index >= segment.From) { if (index <= segment.To) { segmentIndex = middle; return segment; } else { return QuickSearchSegment(middle + 1, to, index, out segmentIndex); } } else { return QuickSearchSegment(from, middle - 1, index, out segmentIndex); } } #endregion #region ICollection Members /// /// NotSupported /// public void Add(T item) { throw new NotSupportedException(); } /// /// NotSupported /// public void Clear() { throw new NotSupportedException(); } /// /// NotSupported /// public bool Contains(T item) { throw new NotSupportedException(); } /// /// Copies to. /// /// The array. /// Index of the array. public void CopyTo(T[] array, int arrayIndex) { CopyTo(array, 0, arrayIndex, Math.Min(array.Length, this.Count - arrayIndex)); } /// /// Gets the number of elements contained in the . /// /// /// The number of elements contained in the . /// public int Count { get { return m_Count; } } /// /// Gets a value indicating whether the is read-only. /// /// true if the is read-only; otherwise, false. /// public bool IsReadOnly { get { return true; } } /// /// NotSupported /// public bool Remove(T item) { throw new NotSupportedException(); } #endregion #region IEnumerable Members /// /// NotSupported /// public IEnumerator GetEnumerator() { throw new NotSupportedException(); } #endregion #region IEnumerable Members /// /// NotSupported /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotSupportedException(); } #endregion /// /// Removes the segment at. /// /// The index. public void RemoveSegmentAt(int index) { var removedSegment = m_Segments[index]; int removedLen = removedSegment.To - removedSegment.From + 1; m_Segments.RemoveAt(index); m_PrevSegment = null; //the removed item is not the the last item if (index != m_Segments.Count) { for (int i = index; i < m_Segments.Count; i++) { m_Segments[i].From -= removedLen; m_Segments[i].To -= removedLen; } } m_Count -= removedLen; } /// /// Adds the segment to the list. /// /// The array. /// The offset. /// The length. public void AddSegment(T[] array, int offset, int length) { AddSegment(array, offset, length, false); } /// /// Adds the segment to the list. /// /// The array. /// The offset. /// The length. /// if set to true [to be copied]. public void AddSegment(T[] array, int offset, int length, bool toBeCopied) { if (length <= 0) return; var currentTotal = m_Count; ArraySegmentEx segment = null; if (!toBeCopied) segment = new ArraySegmentEx(array, offset, length); else segment = new ArraySegmentEx(array.CloneRange(offset, length), 0, length); segment.From = currentTotal; m_Count = currentTotal + segment.Count; segment.To = m_Count - 1; m_Segments.Add(segment); } /// /// Gets the segment count. /// public int SegmentCount { get { return m_Segments.Count; } } /// /// Clears all the segements. /// public void ClearSegements() { m_Segments.Clear(); m_PrevSegment = null; m_Count = 0; } /// /// Read all data in this list to the array data. /// /// public T[] ToArrayData() { return ToArrayData(0, m_Count); } /// /// Read the data in specific range to the array data. /// /// The start index. /// The length. /// public T[] ToArrayData(int startIndex, int length) { var result = new T[length]; int from = 0, len = 0, total = 0; var startSegmentIndex = 0; if (startIndex != 0) { var startSegment = QuickSearchSegment(0, m_Segments.Count - 1, startIndex, out startSegmentIndex); if (startSegment == null) throw new IndexOutOfRangeException(); from = startIndex - startSegment.From; } for (var i = startSegmentIndex; i < m_Segments.Count; i++) { var currentSegment = m_Segments[i]; len = Math.Min(currentSegment.Count - from, length - total); Array.Copy(currentSegment.Array, currentSegment.Offset + from, result, total, len); total += len; if (total >= length) break; from = 0; } return result; } /// /// Trims the end. /// /// Size of the trim. public void TrimEnd(int trimSize) { if (trimSize <= 0) return; int expectedTo = this.Count - trimSize - 1; for (int i = m_Segments.Count - 1; i >= 0; i--) { var s = m_Segments[i]; if (s.From <= expectedTo && expectedTo < s.To) { s.To = expectedTo; m_Count -= trimSize; return; } RemoveSegmentAt(i); } } /// /// Searches the last segment. /// /// The state. /// public int SearchLastSegment(SearchMarkState state) { if (m_Segments.Count <= 0) return -1; var lastSegment = m_Segments[m_Segments.Count - 1]; if (lastSegment == null) return -1; var result = lastSegment.Array.SearchMark(lastSegment.Offset, lastSegment.Count, state.Mark); if (!result.HasValue) return -1; if (result.Value > 0) { state.Matched = 0; return result.Value - lastSegment.Offset + lastSegment.From; } state.Matched = 0 - result.Value; return -1; } /// /// Copies to. /// /// To. /// public int CopyTo(T[] to) { return CopyTo(to, 0, 0, Math.Min(m_Count, to.Length)); } /// /// Copies to. /// /// To. /// Index of the SRC. /// To index. /// The length. /// public int CopyTo(T[] to, int srcIndex, int toIndex, int length) { int copied = 0; int thisCopied = 0; int offsetSegmentIndex; ArraySegmentEx offsetSegment; if (srcIndex > 0) offsetSegment = QuickSearchSegment(0, m_Segments.Count - 1, srcIndex, out offsetSegmentIndex); else { offsetSegment = m_Segments[0]; offsetSegmentIndex = 0; } int thisOffset = srcIndex - offsetSegment.From + offsetSegment.Offset; thisCopied = Math.Min(offsetSegment.Count - thisOffset + offsetSegment.Offset, length - copied); Array.Copy(offsetSegment.Array, thisOffset, to, copied + toIndex, thisCopied); copied += thisCopied; if (copied >= length) return copied; for (var i = offsetSegmentIndex + 1; i < this.m_Segments.Count; i++) { var segment = m_Segments[i]; thisCopied = Math.Min(segment.Count, length - copied); Array.Copy(segment.Array, segment.Offset, to, copied + toIndex, thisCopied); copied += thisCopied; if (copied >= length) break; } return copied; } } /// /// ArraySegmentList /// public class ArraySegmentList : ArraySegmentList { /// /// Decodes bytes to string by the specified encoding. /// /// The encoding. /// public string Decode(Encoding encoding) { return Decode(encoding, 0, Count); } /// /// Decodes bytes to string by the specified encoding. /// /// The encoding. /// The offset. /// The length. /// public string Decode(Encoding encoding, int offset, int length) { var arraySegments = Segments; if (arraySegments == null || arraySegments.Count <= 0) return string.Empty; var charsBuffer = new char[encoding.GetMaxCharCount(this.Count)]; int bytesUsed, charsUsed; bool completed; int totalBytes = 0; int totalChars = 0; int lastSegIndex = arraySegments.Count - 1; var flush = false; var decoder = encoding.GetDecoder(); int startSegmentIndex = 0; if (offset > 0) { QuickSearchSegment(0, arraySegments.Count - 1, offset, out startSegmentIndex); } for (var i = startSegmentIndex; i < arraySegments.Count; i++) { var segment = arraySegments[i]; if (i == lastSegIndex) flush = true; int decodeOffset = segment.Offset; int toBeDecoded = Math.Min(length - totalBytes, segment.Count); if (i == startSegmentIndex && offset > 0) { decodeOffset = offset - segment.From + segment.Offset; toBeDecoded = Math.Min(segment.Count - offset + segment.From, toBeDecoded); } decoder.Convert(segment.Array, decodeOffset, toBeDecoded, charsBuffer, totalChars, charsBuffer.Length - totalChars, flush, out bytesUsed, out charsUsed, out completed); totalChars += charsUsed; totalBytes += bytesUsed; if (totalBytes >= length) break; } return new string(charsBuffer, 0, totalChars); } /// /// Decodes data by the mask. /// /// The mask. /// The offset. /// The length. public void DecodeMask(byte[] mask, int offset, int length) { int maskLen = mask.Length; var startSegmentIndex = 0; var startSegment = QuickSearchSegment(0, Segments.Count - 1, offset, out startSegmentIndex); var shouldDecode = Math.Min(length, startSegment.Count - offset + startSegment.From); var from = offset - startSegment.From + startSegment.Offset; var index = 0; for (var i = from; i < from + shouldDecode; i++) { startSegment.Array[i] = (byte)(startSegment.Array[i] ^ mask[index++ % maskLen]); } if (index >= length) return; for (var i = startSegmentIndex + 1; i < SegmentCount; i++) { var segment = Segments[i]; shouldDecode = Math.Min(length - index, segment.Count); for (var j = segment.Offset; j < segment.Offset + shouldDecode; j++) { segment.Array[j] = (byte)(segment.Array[j] ^ mask[index++ % maskLen]); } if (index >= length) return; } } } }