| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- // 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.Diagnostics;
- 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]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public readonly struct ArraySegment<T> : IList<T>, IReadOnlyList<T>
- {
- // Do not replace the array allocation with Array.Empty. We don't want to have the overhead of
- // instantiating another generic type in addition to ArraySegment<T> for new type parameters.
- public static ArraySegment<T> Empty { get; } = new ArraySegment<T>(new T[0]);
- private readonly T[] _array; // Do not rename (binary serialization)
- private readonly int _offset; // Do not rename (binary serialization)
- private readonly int _count; // Do not rename (binary serialization)
- public ArraySegment(T[] array)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- _array = array;
- _offset = 0;
- _count = array.Length;
- }
- public ArraySegment(T[] array, int offset, int count)
- {
- // Validate arguments, check is minimal instructions with reduced branching for inlinable fast-path
- // Negative values discovered though conversion to high values when converted to unsigned
- // Failure should be rare and location determination and message is delegated to failure functions
- if (array == null || (uint)offset > (uint)array.Length || (uint)count > (uint)(array.Length - offset))
- ThrowHelper.ThrowArraySegmentCtorValidationFailedExceptions(array, offset, count);
- _array = array;
- _offset = offset;
- _count = count;
- }
- public T[] Array => _array;
- public int Offset => _offset;
- public int Count => _count;
- public T this[int index]
- {
- get
- {
- if ((uint)index >= (uint)_count)
- {
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- }
- return _array[_offset + index];
- }
- set
- {
- if ((uint)index >= (uint)_count)
- {
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- }
- _array[_offset + index] = value;
- }
- }
- public Enumerator GetEnumerator()
- {
- ThrowInvalidOperationIfDefault();
- return new Enumerator(this);
- }
- public override int GetHashCode()
- {
- if (_array == null)
- {
- return 0;
- }
- int hash = 5381;
- hash = System.Numerics.Hashing.HashHelpers.Combine(hash, _offset);
- hash = System.Numerics.Hashing.HashHelpers.Combine(hash, _count);
- // The array hash is expected to be an evenly-distributed mixture of bits,
- // so rather than adding the cost of another rotation we just xor it.
- hash ^= _array.GetHashCode();
- return hash;
- }
- public void CopyTo(T[] destination) => CopyTo(destination, 0);
- public void CopyTo(T[] destination, int destinationIndex)
- {
- ThrowInvalidOperationIfDefault();
- System.Array.Copy(_array, _offset, destination, destinationIndex, _count);
- }
- public void CopyTo(ArraySegment<T> destination)
- {
- ThrowInvalidOperationIfDefault();
- destination.ThrowInvalidOperationIfDefault();
- if (_count > destination._count)
- {
- ThrowHelper.ThrowArgumentException_DestinationTooShort();
- }
- System.Array.Copy(_array, _offset, destination._array, destination._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 ArraySegment<T> Slice(int index)
- {
- ThrowInvalidOperationIfDefault();
-
- if ((uint)index > (uint)_count)
- {
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- }
- return new ArraySegment<T>(_array, _offset + index, _count - index);
- }
- public ArraySegment<T> Slice(int index, int count)
- {
- ThrowInvalidOperationIfDefault();
- if ((uint)index > (uint)_count || (uint)count > (uint)(_count - index))
- {
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- }
- return new ArraySegment<T>(_array, _offset + index, count);
- }
- public T[] ToArray()
- {
- ThrowInvalidOperationIfDefault();
- if (_count == 0)
- {
- return Empty._array;
- }
- var array = new T[_count];
- System.Array.Copy(_array, _offset, array, 0, _count);
- return array;
- }
- 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);
- }
- public static implicit operator ArraySegment<T>(T[] array) => array != null ? new ArraySegment<T>(array) : default;
- #region IList<T>
- T IList<T>.this[int index]
- {
- get
- {
- ThrowInvalidOperationIfDefault();
- if (index < 0 || index >= _count)
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- return _array[_offset + index];
- }
- set
- {
- ThrowInvalidOperationIfDefault();
- if (index < 0 || index >= _count)
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- _array[_offset + index] = value;
- }
- }
- int IList<T>.IndexOf(T item)
- {
- ThrowInvalidOperationIfDefault();
- int index = System.Array.IndexOf<T>(_array, item, _offset, _count);
- Debug.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
- {
- ThrowInvalidOperationIfDefault();
- if (index < 0 || index >= _count)
- ThrowHelper.ThrowArgumentOutOfRange_IndexException();
- 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)
- {
- ThrowInvalidOperationIfDefault();
- int index = System.Array.IndexOf<T>(_array, item, _offset, _count);
- Debug.Assert(index == -1 ||
- (index >= _offset && index < _offset + _count));
- return index >= 0;
- }
- bool ICollection<T>.Remove(T item)
- {
- ThrowHelper.ThrowNotSupportedException();
- return default;
- }
- #endregion
- #region IEnumerable<T>
- IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
- #endregion
- #region IEnumerable
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- #endregion
- private void ThrowInvalidOperationIfDefault()
- {
- if (_array == null)
- {
- ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NullArray);
- }
- }
- public struct Enumerator : IEnumerator<T>
- {
- private readonly T[] _array;
- private readonly int _start;
- private readonly int _end; // cache Offset + Count, since it's a little slow
- private int _current;
- internal Enumerator(ArraySegment<T> arraySegment)
- {
- Debug.Assert(arraySegment.Array != null);
- Debug.Assert(arraySegment.Offset >= 0);
- Debug.Assert(arraySegment.Count >= 0);
- Debug.Assert(arraySegment.Offset + arraySegment.Count <= arraySegment.Array.Length);
- _array = arraySegment.Array;
- _start = arraySegment.Offset;
- _end = arraySegment.Offset + arraySegment.Count;
- _current = arraySegment.Offset - 1;
- }
- public bool MoveNext()
- {
- if (_current < _end)
- {
- _current++;
- return (_current < _end);
- }
- return false;
- }
- public T Current
- {
- get
- {
- if (_current < _start)
- ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumNotStarted();
- if (_current >= _end)
- ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumEnded();
- return _array[_current];
- }
- }
- object IEnumerator.Current => Current;
- void IEnumerator.Reset()
- {
- _current = _start - 1;
- }
- public void Dispose()
- {
- }
- }
- }
- }
|