summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/ArraySegment.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/ArraySegment.cs')
-rw-r--r--src/mscorlib/src/System/ArraySegment.cs342
1 files changed, 342 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/ArraySegment.cs b/src/mscorlib/src/System/ArraySegment.cs
new file mode 100644
index 0000000000..bc39c2474f
--- /dev/null
+++ b/src/mscorlib/src/System/ArraySegment.cs
@@ -0,0 +1,342 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Convenient wrapper for an array, an offset, and
+** a count. Ideally used in streams & collections.
+** Net Classes will consume an array of these.
+**
+**
+===========================================================*/
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ // Note: users should make sure they copy the fields out of an ArraySegment onto their stack
+ // then validate that the fields describe valid bounds within the array. This must be done
+ // because assignments to value types are not atomic, and also because one thread reading
+ // three fields from an ArraySegment may not see the same ArraySegment from one call to another
+ // (ie, users could assign a new value to the old location).
+ [Serializable]
+ public struct ArraySegment<T> : IList<T>, IReadOnlyList<T>
+ {
+ private T[] _array;
+ private int _offset;
+ private int _count;
+
+ public ArraySegment(T[] array)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ Contract.EndContractBlock();
+
+ _array = array;
+ _offset = 0;
+ _count = array.Length;
+ }
+
+ public ArraySegment(T[] array, int offset, int count)
+ {
+ if (array == null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
+ if (offset < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ if (count < 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ if (array.Length - offset < count)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
+ Contract.EndContractBlock();
+
+ _array = array;
+ _offset = offset;
+ _count = count;
+ }
+
+ public T[] Array
+ {
+ get
+ {
+ Contract.Assert( (null == _array && 0 == _offset && 0 == _count)
+ || (null != _array && _offset >= 0 && _count >= 0 && _offset + _count <= _array.Length),
+ "ArraySegment is invalid");
+
+ return _array;
+ }
+ }
+
+ public int Offset
+ {
+ get
+ {
+ // Since copying value types is not atomic & callers cannot atomically
+ // read all three fields, we cannot guarantee that Offset is within
+ // the bounds of Array. That is our intent, but let's not specify
+ // it as a postcondition - force callers to re-verify this themselves
+ // after reading each field out of an ArraySegment into their stack.
+ Contract.Ensures(Contract.Result<int>() >= 0);
+
+ Contract.Assert( (null == _array && 0 == _offset && 0 == _count)
+ || (null != _array && _offset >= 0 && _count >= 0 && _offset + _count <= _array.Length),
+ "ArraySegment is invalid");
+
+ return _offset;
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ // Since copying value types is not atomic & callers cannot atomically
+ // read all three fields, we cannot guarantee that Count is within
+ // the bounds of Array. That's our intent, but let's not specify
+ // it as a postcondition - force callers to re-verify this themselves
+ // after reading each field out of an ArraySegment into their stack.
+ Contract.Ensures(Contract.Result<int>() >= 0);
+
+ Contract.Assert( (null == _array && 0 == _offset && 0 == _count)
+ || (null != _array && _offset >= 0 && _count >= 0 && _offset + _count <= _array.Length),
+ "ArraySegment is invalid");
+
+ return _count;
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return null == _array
+ ? 0
+ : _array.GetHashCode() ^ _offset ^ _count;
+ }
+
+ public override bool Equals(Object obj)
+ {
+ if (obj is ArraySegment<T>)
+ return Equals((ArraySegment<T>)obj);
+ else
+ return false;
+ }
+
+ public bool Equals(ArraySegment<T> obj)
+ {
+ return obj._array == _array && obj._offset == _offset && obj._count == _count;
+ }
+
+ public static bool operator ==(ArraySegment<T> a, ArraySegment<T> b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(ArraySegment<T> a, ArraySegment<T> b)
+ {
+ return !(a == b);
+ }
+
+ #region IList<T>
+ T IList<T>.this[int index]
+ {
+ get
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ if (index < 0 || index >= _count)
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ Contract.EndContractBlock();
+
+ return _array[_offset + index];
+ }
+
+ set
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ if (index < 0 || index >= _count)
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ Contract.EndContractBlock();
+
+ _array[_offset + index] = value;
+ }
+ }
+
+ int IList<T>.IndexOf(T item)
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ Contract.EndContractBlock();
+
+ int index = System.Array.IndexOf<T>(_array, item, _offset, _count);
+
+ Contract.Assert(index == -1 ||
+ (index >= _offset && index < _offset + _count));
+
+ return index >= 0 ? index - _offset : -1;
+ }
+
+ void IList<T>.Insert(int index, T item)
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ void IList<T>.RemoveAt(int index)
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+ #endregion
+
+ #region IReadOnlyList<T>
+ T IReadOnlyList<T>.this[int index]
+ {
+ get
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ if (index < 0 || index >= _count)
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ Contract.EndContractBlock();
+
+ return _array[_offset + index];
+ }
+ }
+ #endregion IReadOnlyList<T>
+
+ #region ICollection<T>
+ bool ICollection<T>.IsReadOnly
+ {
+ get
+ {
+ // the indexer setter does not throw an exception although IsReadOnly is true.
+ // This is to match the behavior of arrays.
+ return true;
+ }
+ }
+
+ void ICollection<T>.Add(T item)
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ void ICollection<T>.Clear()
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ bool ICollection<T>.Contains(T item)
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ Contract.EndContractBlock();
+
+ int index = System.Array.IndexOf<T>(_array, item, _offset, _count);
+
+ Contract.Assert(index == -1 ||
+ (index >= _offset && index < _offset + _count));
+
+ return index >= 0;
+ }
+
+ void ICollection<T>.CopyTo(T[] array, int arrayIndex)
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ Contract.EndContractBlock();
+
+ System.Array.Copy(_array, _offset, array, arrayIndex, _count);
+ }
+
+ bool ICollection<T>.Remove(T item)
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ return default(bool);
+ }
+ #endregion
+
+ #region IEnumerable<T>
+ IEnumerator<T> IEnumerable<T>.GetEnumerator()
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ Contract.EndContractBlock();
+
+ return new ArraySegmentEnumerator(this);
+ }
+ #endregion
+
+ #region IEnumerable
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ if (_array == null)
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
+ Contract.EndContractBlock();
+
+ return new ArraySegmentEnumerator(this);
+ }
+ #endregion
+
+ [Serializable]
+ private sealed class ArraySegmentEnumerator : IEnumerator<T>
+ {
+ private T[] _array;
+ private int _start;
+ private int _end;
+ private int _current;
+
+ internal ArraySegmentEnumerator(ArraySegment<T> arraySegment)
+ {
+ Contract.Requires(arraySegment.Array != null);
+ Contract.Requires(arraySegment.Offset >= 0);
+ Contract.Requires(arraySegment.Count >= 0);
+ Contract.Requires(arraySegment.Offset + arraySegment.Count <= arraySegment.Array.Length);
+
+ _array = arraySegment._array;
+ _start = arraySegment._offset;
+ _end = _start + arraySegment._count;
+ _current = _start - 1;
+ }
+
+ public bool MoveNext()
+ {
+ if (_current < _end)
+ {
+ _current++;
+ return (_current < _end);
+ }
+ return false;
+ }
+
+ public T Current
+ {
+ get
+ {
+ if (_current < _start) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
+ if (_current >= _end) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
+ return _array[_current];
+ }
+ }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return Current;
+ }
+ }
+
+ void IEnumerator.Reset()
+ {
+ _current = _start - 1;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+ }
+}