Summary

Class:System.Buffers.ReadOnlySequence
Assembly:System.Memory
File(s):C:\GitHub\corefx\src\System.Memory\src\System\Buffers\ReadOnlySequence.cs
Covered lines:8
Uncovered lines:0
Coverable lines:8
Total lines:453
Line coverage:100%

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
SegmentToSequenceStart(...)10100100
SegmentToSequenceEnd(...)10100100
ArrayToSequenceStart(...)10100100
ArrayToSequenceEnd(...)10100100
OwnedMemoryToSequenceStart(...)10100100
OwnedMemoryToSequenceEnd(...)10100100
StringToSequenceStart(...)10100100
StringToSequenceEnd(...)10100100

File(s)

C:\GitHub\corefx\src\System.Memory\src\System\Buffers\ReadOnlySequence.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System.Diagnostics;
 6using System.Runtime.CompilerServices;
 7using System.Runtime.InteropServices;
 8
 9namespace System.Buffers
 10{
 11    /// <summary>
 12    /// Represents a sequence that can read a sequential series of <typeparam name="T" />.
 13    /// </summary>
 14    public readonly partial struct ReadOnlySequence<T>
 15    {
 16        private readonly SequencePosition _sequenceStart;
 17        private readonly SequencePosition _sequenceEnd;
 18
 19        /// <summary>
 20        /// Returns empty <see cref="ReadOnlySequence{T}"/>
 21        /// </summary>
 22#if FEATURE_PORTABLE_SPAN
 23        public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(SpanHelpers.PerTypeValues<T>.EmptyArr
 24#else
 25        public static readonly ReadOnlySequence<T> Empty = new ReadOnlySequence<T>(Array.Empty<T>());
 26#endif // FEATURE_PORTABLE_SPAN
 27        /// <summary>
 28        /// Length of the <see cref="ReadOnlySequence{T}"/>.
 29        /// </summary>
 30        public long Length => GetLength(_sequenceStart, _sequenceEnd);
 31
 32        /// <summary>
 33        /// Determines if the <see cref="ReadOnlySequence{T}"/> is empty.
 34        /// </summary>
 35        public bool IsEmpty => Length == 0;
 36
 37        /// <summary>
 38        /// Determines if the <see cref="ReadOnlySequence{T}"/> contains a single <see cref="ReadOnlyMemory{T}"/> segmen
 39        /// </summary>
 40        public bool IsSingleSegment
 41        {
 42            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 43            get => _sequenceStart.GetObject() == _sequenceEnd.GetObject();
 44        }
 45
 46        /// <summary>
 47        /// Gets <see cref="ReadOnlyMemory{T}"/> from the first segment.
 48        /// </summary>
 49        public ReadOnlyMemory<T> First => GetFirstBuffer(_sequenceStart, _sequenceEnd);
 50
 51        /// <summary>
 52        /// A position to the start of the <see cref="ReadOnlySequence{T}"/>.
 53        /// </summary>
 54        public SequencePosition Start => _sequenceStart;
 55
 56        /// <summary>
 57        /// A position to the end of the <see cref="ReadOnlySequence{T}"/>
 58        /// </summary>
 59        public SequencePosition End => _sequenceEnd;
 60
 61        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 62        private ReadOnlySequence(object startSegment, int startIndexAndFlags, object endSegment, int endIndexAndFlags)
 63        {
 64            // Used by SliceImpl to create new ReadOnlySequence
 65            Debug.Assert(startSegment != null);
 66            Debug.Assert(endSegment != null);
 67
 68            _sequenceStart = new SequencePosition(startSegment, startIndexAndFlags);
 69            _sequenceEnd = new SequencePosition(endSegment, endIndexAndFlags);
 70        }
 71
 72        /// <summary>
 73        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from linked memory list represented by start and en
 74        /// and corresponding indexes in them.
 75        /// </summary>
 76        public ReadOnlySequence(ReadOnlySequenceSegment<T> startSegment, int startIndex, ReadOnlySequenceSegment<T> endS
 77        {
 78            if (startSegment == null ||
 79                endSegment == null ||
 80                (uint)startSegment.Memory.Length < (uint)startIndex ||
 81                (uint)endSegment.Memory.Length < (uint)endIndex ||
 82                (startSegment == endSegment && endIndex < startIndex))
 83                ThrowHelper.ThrowArgumentValidationException(startSegment, startIndex, endSegment);
 84
 85            _sequenceStart = new SequencePosition(startSegment, ReadOnlySequence.SegmentToSequenceStart(startIndex));
 86            _sequenceEnd = new SequencePosition(endSegment, ReadOnlySequence.SegmentToSequenceEnd(endIndex));
 87        }
 88
 89        /// <summary>
 90        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="T:T[]"/>.
 91        /// </summary>
 92        public ReadOnlySequence(T[] array)
 93        {
 94            if (array == null)
 95                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
 96
 97            _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(0));
 98            _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(array.Length));
 99        }
 100
 101        /// <summary>
 102        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="T:T[]"/>, start and index.
 103        /// </summary>
 104        public ReadOnlySequence(T[] array, int start, int length)
 105        {
 106            if (array == null ||
 107                (uint)start > (uint)array.Length ||
 108                (uint)length > (uint)(array.Length - start))
 109                ThrowHelper.ThrowArgumentValidationException(array, start);
 110
 111            _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
 112            _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + length));
 113        }
 114
 115        /// <summary>
 116        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="ReadOnlyMemory{T}"/>.
 117        /// Consumer is expected to manage lifetime of memory until <see cref="ReadOnlySequence{T}"/> is not used anymor
 118        /// </summary>
 119        public ReadOnlySequence(ReadOnlyMemory<T> readOnlyMemory)
 120        {
 121            if (MemoryMarshal.TryGetOwnedMemory(readOnlyMemory, out OwnedMemory<T> ownedMemory, out int index, out int l
 122            {
 123                _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(index));
 124                _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(length));
 125            }
 126            else if (MemoryMarshal.TryGetArray(readOnlyMemory, out ArraySegment<T> arraySegment))
 127            {
 128                T[] array = arraySegment.Array;
 129                int start = arraySegment.Offset;
 130                _sequenceStart = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceStart(start));
 131                _sequenceEnd = new SequencePosition(array, ReadOnlySequence.ArrayToSequenceEnd(start + arraySegment.Coun
 132            }
 133            else if (typeof(T) == typeof(char))
 134            {
 135                if (!MemoryMarshal.TryGetString(((ReadOnlyMemory<char>)(object)readOnlyMemory), out string text, out int
 136                    ThrowHelper.ThrowInvalidOperationException();
 137
 138                _sequenceStart = new SequencePosition(text, ReadOnlySequence.StringToSequenceStart(start));
 139                _sequenceEnd = new SequencePosition(text, ReadOnlySequence.StringToSequenceEnd(start + length));
 140            }
 141            else
 142            {
 143                // Should never be reached
 144                ThrowHelper.ThrowInvalidOperationException();
 145                _sequenceStart = default;
 146                _sequenceEnd = default;
 147            }
 148        }
 149
 150        /// <summary>
 151        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="OwnedMemory{T}"/>.
 152        /// Consumer is expected to manage lifetime of memory until <see cref="ReadOnlySequence{T}"/> is not used anymor
 153        /// </summary>
 154        public ReadOnlySequence(OwnedMemory<T> ownedMemory)
 155        {
 156            if (ownedMemory == null)
 157                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ownedMemory);
 158
 159            _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(0));
 160            _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(ownedMemory.Lengt
 161        }
 162
 163        /// <summary>
 164        /// Creates an instance of <see cref="ReadOnlySequence{T}"/> from the <see cref="OwnedMemory{T}"/>, start and le
 165        /// Consumer is expected to manage lifetime of memory until <see cref="ReadOnlySequence{T}"/> is not used anymor
 166        /// </summary>
 167        public ReadOnlySequence(OwnedMemory<T> ownedMemory, int start, int length)
 168        {
 169            if (ownedMemory == null ||
 170                (uint)start > (uint)ownedMemory.Length ||
 171                (uint)length > (uint)(ownedMemory.Length - start))
 172                ThrowHelper.ThrowArgumentValidationException(ownedMemory, start);
 173
 174            _sequenceStart = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceStart(start));
 175            _sequenceEnd = new SequencePosition(ownedMemory, ReadOnlySequence.OwnedMemoryToSequenceEnd(start + length));
 176        }
 177
 178        /// <summary>
 179        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, wit
 180        /// </summary>
 181        /// <param name="start">The index at which to begin this slice.</param>
 182        /// <param name="length">The length of the slice</param>
 183        public ReadOnlySequence<T> Slice(long start, long length)
 184        {
 185            SequencePosition begin = Seek(_sequenceStart, _sequenceEnd, start);
 186            SequencePosition end = Seek(begin, _sequenceEnd, length);
 187            return SliceImpl(begin, end);
 188        }
 189
 190        /// <summary>
 191        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, end
 192        /// </summary>
 193        /// <param name="start">The index at which to begin this slice.</param>
 194        /// <param name="end">The end (inclusive) of the slice</param>
 195        public ReadOnlySequence<T> Slice(long start, SequencePosition end)
 196        {
 197            BoundsCheck(end, _sequenceEnd);
 198
 199            SequencePosition begin = Seek(_sequenceStart, end, start);
 200            object beginObject = begin.GetObject();
 201            object endObject = end.GetObject();
 202            if (beginObject != endObject)
 203            {
 204                CheckEndReachable(beginObject, endObject);
 205            }
 206            return SliceImpl(begin, end);
 207        }
 208
 209        /// <summary>
 210        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, wit
 211        /// </summary>
 212        /// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</p
 213        /// <param name="length">The length of the slice</param>
 214        public ReadOnlySequence<T> Slice(SequencePosition start, long length)
 215        {
 216            BoundsCheck(start, _sequenceEnd);
 217
 218            SequencePosition end = Seek(start, _sequenceEnd, length);
 219            return SliceImpl(start, end);
 220        }
 221
 222        /// <summary>
 223        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, wit
 224        /// </summary>
 225        /// <param name="start">The index at which to begin this slice.</param>
 226        /// <param name="length">The length of the slice</param>
 227        public ReadOnlySequence<T> Slice(int start, int length)
 228        {
 229            SequencePosition begin = Seek(_sequenceStart, _sequenceEnd, start);
 230            SequencePosition end = Seek(begin, _sequenceEnd, length);
 231            return SliceImpl(begin, end);
 232        }
 233
 234        /// <summary>
 235        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, end
 236        /// </summary>
 237        /// <param name="start">The index at which to begin this slice.</param>
 238        /// <param name="end">The end (inclusive) of the slice</param>
 239        public ReadOnlySequence<T> Slice(int start, SequencePosition end)
 240        {
 241            BoundsCheck(end, _sequenceEnd);
 242
 243            SequencePosition begin = Seek(_sequenceStart, end, start);
 244            object beginObject = begin.GetObject();
 245            object endObject = end.GetObject();
 246            if (beginObject != endObject)
 247            {
 248                CheckEndReachable(beginObject, endObject);
 249            }
 250            return SliceImpl(begin, end);
 251        }
 252
 253        /// <summary>
 254        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at '<paramref name="start"/>, wi
 255        /// </summary>
 256        /// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</p
 257        /// <param name="length">The length of the slice</param>
 258        public ReadOnlySequence<T> Slice(SequencePosition start, int length)
 259        {
 260            BoundsCheck(start, _sequenceEnd);
 261
 262            SequencePosition end = Seek(start, _sequenceEnd, length);
 263            return SliceImpl(start, end);
 264        }
 265
 266        /// <summary>
 267        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, end
 268        /// </summary>
 269        /// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</p
 270        /// <param name="end">The ending (inclusive) <see cref="SequencePosition"/> of the slice</param>
 271        public ReadOnlySequence<T> Slice(SequencePosition start, SequencePosition end)
 272        {
 273            BoundsCheck(end, _sequenceEnd);
 274            BoundsCheck(start, end);
 275
 276            return SliceImpl(start, end);
 277        }
 278
 279        /// <summary>
 280        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, end
 281        /// </summary>
 282        /// <param name="start">The starting (inclusive) <see cref="SequencePosition"/> at which to begin this slice.</p
 283        public ReadOnlySequence<T> Slice(SequencePosition start)
 284        {
 285            BoundsCheck(start, _sequenceEnd);
 286
 287            return SliceImpl(start, _sequenceEnd);
 288        }
 289
 290        /// <summary>
 291        /// Forms a slice out of the given <see cref="ReadOnlySequence{T}"/>, beginning at <paramref name="start"/>, end
 292        /// </summary>
 293        /// <param name="start">The start index at which to begin this slice.</param>
 294        public ReadOnlySequence<T> Slice(long start)
 295        {
 296            if (start == 0)
 297            {
 298                return this;
 299            }
 300
 301            SequencePosition begin = Seek(_sequenceStart, _sequenceEnd, start);
 302            return SliceImpl(begin, _sequenceEnd);
 303        }
 304
 305        /// <inheritdoc />
 306        public override string ToString() => string.Format("System.Buffers.ReadOnlySequence<{0}>[{1}]", typeof(T).Name, 
 307
 308        /// <summary>
 309        /// Returns an enumerator over the <see cref="ReadOnlySequence{T}"/>
 310        /// </summary>
 311        public Enumerator GetEnumerator() => new Enumerator(this);
 312
 313        /// <summary>
 314        /// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the start of the sequence.
 315        /// </summary>
 316        public SequencePosition GetPosition(long offset) => GetPosition(offset, _sequenceStart);
 317
 318        /// <summary>
 319        /// Returns a new <see cref="SequencePosition"/> at an <paramref name="offset"/> from the <paramref name="origin
 320        /// </summary>
 321        public SequencePosition GetPosition(long offset, SequencePosition origin)
 322        {
 323            if (offset < 0)
 324                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offset);
 325
 326            return Seek(origin, _sequenceEnd, offset);
 327        }
 328
 329        /// <summary>
 330        /// Tries to retrieve next segment after <paramref name="position"/> and return its contents in <paramref name="
 331        /// Returns <code>false</code> if end of <see cref="ReadOnlySequence{T}"/> was reached otherwise <code>true</cod
 332        /// Sets <paramref name="position"/> to the beginning of next segment if <paramref name="advance"/> is set to <c
 333        /// </summary>
 334        public bool TryGet(ref SequencePosition position, out ReadOnlyMemory<T> data, bool advance = true)
 335        {
 336            bool result = TryGetBuffer(position, End, out data, out SequencePosition next);
 337            if (advance)
 338            {
 339                position = next;
 340            }
 341
 342            return result;
 343        }
 344
 345        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 346        private ReadOnlySequence<T> SliceImpl(in SequencePosition begin, in SequencePosition end)
 347        {
 348            // In this method we reset high order bits from indices
 349            // of positions that were passed in
 350            // and apply type bits specific for current ReadOnlySequence type
 351
 352            return new ReadOnlySequence<T>(
 353                begin.GetObject(),
 354                begin.GetInteger() & ReadOnlySequence.IndexBitMask | (Start.GetInteger() & ReadOnlySequence.FlagBitMask)
 355                end.GetObject(),
 356                end.GetInteger() & ReadOnlySequence.IndexBitMask | (End.GetInteger() & ReadOnlySequence.FlagBitMask)
 357            );
 358        }
 359
 360        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 361        private void GetTypeAndIndices(int start, int end, out SequenceType sequenceType, out int startIndex, out int en
 362        {
 363            startIndex = start & ReadOnlySequence.IndexBitMask;
 364            endIndex = end & ReadOnlySequence.IndexBitMask;
 365            // We take high order bits of two indexes index and move them
 366            // to a first and second position to convert to BufferType
 367            // Masking with 2 is required to only keep the second bit of Start.GetInteger()
 368            sequenceType = Start.GetObject() == null ? SequenceType.Empty : (SequenceType)((((uint)Start.GetInteger() >>
 369        }
 370
 371        /// <summary>
 372        /// An enumerator over the <see cref="ReadOnlySequence{T}"/>
 373        /// </summary>
 374        public struct Enumerator
 375        {
 376            private readonly ReadOnlySequence<T> _sequence;
 377            private SequencePosition _next;
 378            private ReadOnlyMemory<T> _currentMemory;
 379
 380            /// <summary>Initialize the enumerator.</summary>
 381            /// <param name="sequence">The <see cref="ReadOnlySequence{T}"/> to enumerate.</param>
 382            public Enumerator(in ReadOnlySequence<T> sequence)
 383            {
 384                _currentMemory = default;
 385                _next = sequence.Start;
 386                _sequence = sequence;
 387            }
 388
 389            /// <summary>
 390            /// The current <see cref="ReadOnlyMemory{T}"/>
 391            /// </summary>
 392            public ReadOnlyMemory<T> Current => _currentMemory;
 393
 394            /// <summary>
 395            /// Moves to the next <see cref="ReadOnlyMemory{T}"/> in the <see cref="ReadOnlySequence{T}"/>
 396            /// </summary>
 397            /// <returns></returns>
 398            public bool MoveNext()
 399            {
 400                if (_next.GetObject() == null)
 401                {
 402                    return false;
 403                }
 404
 405                return _sequence.TryGet(ref _next, out _currentMemory);
 406            }
 407        }
 408
 409        private enum SequenceType
 410        {
 411            MultiSegment = 0x00,
 412            Array = 0x1,
 413            OwnedMemory = 0x2,
 414            String = 0x3,
 415            Empty = 0x4
 416        }
 417    }
 418
 419    internal static class ReadOnlySequence
 420    {
 421        public const int FlagBitMask = 1 << 31;
 422        public const int IndexBitMask = ~FlagBitMask;
 423
 424        public const int SegmentStartMask = 0;
 425        public const int SegmentEndMask = 0;
 426
 427        public const int ArrayStartMask = 0;
 428        public const int ArrayEndMask = FlagBitMask;
 429
 430        public const int OwnedMemoryStartMask = FlagBitMask;
 431        public const int OwnedMemoryEndMask = 0;
 432
 433        public const int StringStartMask = FlagBitMask;
 434        public const int StringEndMask = FlagBitMask;
 435
 436        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1437        public static int SegmentToSequenceStart(int startIndex) => startIndex | SegmentStartMask;
 438        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1439        public static int SegmentToSequenceEnd(int endIndex) => endIndex | SegmentEndMask;
 440        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1441        public static int ArrayToSequenceStart(int startIndex) => startIndex | ArrayStartMask;
 442        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1443        public static int ArrayToSequenceEnd(int endIndex) => endIndex | ArrayEndMask;
 444        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1445        public static int OwnedMemoryToSequenceStart(int startIndex) => startIndex | OwnedMemoryStartMask;
 446        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1447        public static int OwnedMemoryToSequenceEnd(int endIndex) => endIndex | OwnedMemoryEndMask;
 448        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1449        public static int StringToSequenceStart(int startIndex) => startIndex | StringStartMask;
 450        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1451        public static int StringToSequenceEnd(int endIndex) => endIndex | StringEndMask;
 452    }
 453}