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;
}
}
}
}