예제 #1
0
        /// <summary>
        /// Removes all elements from the sequence from its beginning to the specified position,
        /// considering that data to have been fully processed.
        /// </summary>
        /// <param name="position">
        /// The position of the first element that has not yet been processed.
        /// This is typically <see cref="ReadOnlySequence{T}.End"/> after reading all elements from that instance.
        /// </param>
        public void AdvanceTo(SequencePosition position)
        {
            var firstSegment = (SequenceSegment)position.GetObject();

            if (firstSegment == null)
            {
                // Emulate PipeReader behavior which is to just return for default(SequencePosition)
                return;
            }

            if (ReferenceEquals(firstSegment, SequenceSegment.Empty) && this.Length == 0)
            {
                // We were called with our own empty buffer segment.
                return;
            }

            int firstIndex = position.GetInteger();

            // Before making any mutations, confirm that the block specified belongs to this sequence.
            var current = this.first;

            while (current != firstSegment && current != null)
            {
                current = current.Next;
            }

            Requires.Argument(current != null, nameof(position), "Position does not represent a valid position in this sequence.");

            // Also confirm that the position is not a prior position in the block.
            Requires.Argument(firstIndex >= current.Start, nameof(position), "Position must not be earlier than current position.");

            // Now repeat the loop, performing the mutations.
            current = this.first;
            while (current != firstSegment)
            {
                current = this.RecycleAndGetNext(current !);
            }

            firstSegment.AdvanceTo(firstIndex);

            this.first = firstSegment.Length == 0 ? this.RecycleAndGetNext(firstSegment) : firstSegment;

            if (this.first == null)
            {
                this.last = null;
            }
        }
        private static void Byte_Default()
        {
            ReadOnlySequence <byte> buffer = default;

            foreach (BenchmarkIteration iteration in Benchmark.Iterations)
            {
                int localInt = 0;
                using (iteration.StartMeasurement())
                {
                    for (int i = 0; i < Benchmark.InnerIterationCount; i++)
                    {
                        SequencePosition pos = buffer.GetPosition(0);
                        localInt ^= pos.GetInteger();
                    }
                }
                _volatileInt = localInt;
            }
        }
예제 #3
0
        public bool TryGet(ref SequencePosition position, out T item, bool advance = true)
        {
            int index = position.GetInteger();

            if (index < _count)
            {
                item = _array[index];
                if (advance)
                {
                    position = new SequencePosition(null, index + 1);
                }
                return(true);
            }

            item     = default;
            position = default;
            return(false);
        }
        public bool HasUnexaminedData(SequencePosition examined)
        {
            if (IsEmpty)
            {
                return(false);
            }

            var examinedObject  = examined.GetObject();
            var examinedInteger = examined.GetInteger();

            if (ReferenceEquals(examinedObject, _lastSegment) &&
                examinedInteger == _lastIndex)
            {
                return(false);
            }

            return(true);
        }
예제 #5
0
        public void CheckEndReachableDoesNotCrossPastEnd()
        {
            var bufferSegment1 = new BufferSegment <byte>(new byte[100]);
            BufferSegment <byte> bufferSegment2 = bufferSegment1.Append(new byte[100]);
            BufferSegment <byte> bufferSegment3 = bufferSegment2.Append(new byte[100]);
            BufferSegment <byte> bufferSegment4 = bufferSegment3.Append(new byte[100]);

            var buffer = new ReadOnlySequence <byte>(bufferSegment1, 0, bufferSegment4, 100);

            SequencePosition c1 = buffer.GetPosition(buffer.Start, 200);

            Assert.Equal(0, c1.GetInteger());
            Assert.Equal(bufferSegment3, c1.GetObject());

            ReadOnlySequence <byte> seq = buffer.Slice(0, c1);

            Assert.Equal(200, seq.Length);
        }
        private static void Char_Empty()
        {
            ReadOnlySequence <char> buffer = ReadOnlySequence <char> .Empty;

            foreach (BenchmarkIteration iteration in Benchmark.Iterations)
            {
                int localInt = 0;
                using (iteration.StartMeasurement())
                {
                    for (int i = 0; i < Benchmark.InnerIterationCount; i++)
                    {
                        SequencePosition p = buffer.GetPosition(0);
                        localInt ^= p.GetInteger();
                    }
                }
                _volatileInt = localInt;
            }
        }
예제 #7
0
        public bool TryFindPosition(byte[] value, out SequencePosition lastPosition)
        {
            if (value == null || value.Length == 0)
            {
                throw new ArgumentNullException(nameof(value));
            }

            int valueLength = value.Length;

            if (valueLength == 1)
            {
                if (this.TryFindPosition(value[valueLength - 1], out lastPosition))
                {
                    lastPosition.Offet(1);
                }
            }

            SequencePosition startPosition = this.buffer.Start;

            while (true)
            {
                if (startPosition.GetInteger() >= this.buffer.End.GetInteger())
                {
                    break;
                }

                if (this.TryFindPosition(value[valueLength - 1], out var endPos))
                {
                    var tempEndPos   = endPos.Offet(1);
                    var startPos     = this.GetStartPosition(ref tempEndPos, valueLength);
                    var tempSequence = this.buffer.Slice(startPos, tempEndPos);
                    if (this.VerifySequence(ref tempSequence, value))
                    {
                        lastPosition = tempEndPos;
                        return(true);
                    }

                    startPosition = tempEndPos;
                }
            }

            lastPosition = default;
            return(false);
        }
예제 #8
0
        public ReadWriteBytes Slice(SequencePosition start, SequencePosition end)
        {
            var kind = Kind;

            switch (kind)
            {
            case Type.Array:
                var(array, index) = start.Get <byte[]>();
                return(new ReadWriteBytes(array, index, end.GetInteger() - index));

            case Type.MemoryList:
                var(startList, startIndex) = start.Get <ReadOnlySequenceSegment <byte> >();
                var(endList, endIndex)     = end.Get <ReadOnlySequenceSegment <byte> >();
                return(new ReadWriteBytes(startList, startIndex, endList, endIndex));

            default:
                throw new NotImplementedException();
            }
        }
예제 #9
0
        public void ConsumeTo(SequencePosition consumed)
        {
            var segment = (Segment)consumed.GetObject();
            var index   = consumed.GetInteger();

            if (segment == _endSegment && index == _endIndex)
            {
                // keep the last page; burn anything else
                _startSegment !.RecycleBefore(segment);
                _startSegment = _endSegment;
                _startIndex   = _endIndex = 0;
            }
            else
            {
                // discard any pages that we no longer need
                _startSegment !.RecycleBefore(segment);
                _startSegment = segment;
                _startIndex   = index;
            }
        }
예제 #10
0
        public ReadOnlySequence <byte> Slice(SequencePosition markStart, SequencePosition markEnd)
        {
            if (start == null)
            {
                return(ReadOnlySequence <byte> .Empty);
            }

            var a = (_Segment)(markStart.GetObject() ?? start);
            var b = (_Segment)(markEnd.GetObject() ?? start);

            var pa = markStart.GetInteger();
            var pb = markEnd.GetInteger();

            if (a == b && pa == pb)
            {
                return(ReadOnlySequence <byte> .Empty);
            }

            return(new ReadOnlySequence <byte>(a, pa, b, pb));
        }
예제 #11
0
        /// <summary>
        /// Removes all elements from the sequence from its beginning to the specified position,
        /// considering that data to have been fully processed.
        /// </summary>
        /// <param name="position">
        /// The position of the first element that has not yet been processed.
        /// This is typically <see cref="ReadOnlySequence{T}.End"/> after reading all elements from that instance.
        /// </param>
        public void AdvanceTo(SequencePosition position)
        {
            var firstSegment = (SequenceSegment)position.GetObject();
            int firstIndex   = position.GetInteger();

            // Before making any mutations, confirm that the block specified belongs to this sequence.
            var current = this.first;

            while (current != firstSegment && current != null)
            {
                current = current.Next;
            }

            Requires.Argument(current != null, nameof(position), "Position does not represent a valid position in this sequence.");

            // Also confirm that the position is not a prior position in the block.
            Requires.Argument(firstIndex >= current.Start, nameof(position), "Position must not be earlier than current position.");

            // Now repeat the loop, performing the mutations.
            current = this.first;
            while (current != firstSegment)
            {
                var next = current.Next;
                current.ResetMemory();
                current = next;
            }

            firstSegment.AdvanceTo(firstIndex);

            if (firstSegment.Length == 0)
            {
                firstSegment = this.RecycleAndGetNext(firstSegment);
            }

            this.first = firstSegment;

            if (this.first == null)
            {
                this.last = null;
            }
        }
예제 #12
0
        internal SequencePosition Seek(SequencePosition start, SequencePosition end, int count, bool checkEndReachable = true)
        {
            int          startIndex = start.GetInteger();
            int          endIndex   = end.GetInteger();
            SequenceType type       = GetSequenceType();

            startIndex = GetIndex(startIndex);
            endIndex   = GetIndex(endIndex);

            switch (type)
            {
            case SequenceType.MemoryList:
                if (start.GetObject() == end.GetObject() && endIndex - startIndex >= count)
                {
                    return(new SequencePosition(start.GetObject(), startIndex + count));
                }
                return(SeekMultiSegment((IMemoryList <byte>)start.GetObject(), startIndex, (IMemoryList <byte>)end.GetObject(), endIndex, count, checkEndReachable));

            case SequenceType.OwnedMemory:
            case SequenceType.Array:
                if (start.GetObject() != end.GetObject())
                {
                    ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
                }

                if (endIndex - startIndex >= count)
                {
                    return(new SequencePosition(start.GetObject(), startIndex + count));
                }

                ThrowHelper.ThrowArgumentOutOfRangeException_CountOutOfRange();
                return(default);

            default:
                ThrowHelper.ThrowInvalidOperationException_UnexpectedSegmentType();
                return(default);
            }
        }
예제 #13
0
        private long GetLength(SequencePosition start, SequencePosition end)
        {
            int          startIndex = start.GetInteger();
            int          endIndex   = end.GetInteger();
            SequenceType type       = GetSequenceType();

            startIndex = GetIndex(startIndex);
            endIndex   = GetIndex(endIndex);

            switch (type)
            {
            case SequenceType.MemoryList:
                return(GetLength((IMemoryList <T>)start.GetObject(), startIndex, (IMemoryList <T>)end.GetObject(), endIndex));

            case SequenceType.OwnedMemory:
            case SequenceType.Array:
                return(endIndex - startIndex);

            default:
                ThrowHelper.ThrowInvalidOperationException_UnexpectedSegmentType();
                return(default);
            }
        }
        private static void Byte_Array(int bufSize, int bufOffset)
        {
            var buffer           = new ReadOnlySequence <byte>(new byte[bufSize], bufOffset, bufSize - 2 * bufOffset);
            int offset           = (int)buffer.Length / 10;
            SequencePosition end = buffer.GetPosition(0, buffer.End);

            foreach (BenchmarkIteration iteration in Benchmark.Iterations)
            {
                int localInt = 0;
                using (iteration.StartMeasurement())
                {
                    for (int i = 0; i < Benchmark.InnerIterationCount; i++)
                    {
                        SequencePosition pos = buffer.Start;
                        while (!pos.Equals(end))
                        {
                            pos       = buffer.GetPosition(offset, pos);
                            localInt ^= pos.GetInteger();
                        }
                    }
                }
                _volatileInt = localInt;
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="InvalidPacketHeaderException"/> class.
 /// </summary>
 /// <param name="header">The header which is invalid.</param>
 /// <param name="bufferContent">Content of the buffer when the header was read. With this information you can e.g. find out if a previous packet was malformed.</param>
 /// <param name="position">The position of the <paramref name="header"/> in the <paramref name="bufferContent"/>.</param>
 public InvalidPacketHeaderException(byte[] header, ReadOnlySequence <byte> bufferContent, SequencePosition position)
 {
     this.Header        = header.ToArray();
     this.BufferContent = bufferContent.ToArray();
     this.Position      = position.GetInteger();
 }
예제 #16
0
        /// <inheritdoc />
        public override void AdvanceTo(SequencePosition consumed, SequencePosition examined)
        {
            ThrowIfCompleted();

            AdvanceTo((BufferSegment?)consumed.GetObject(), consumed.GetInteger(), (BufferSegment?)examined.GetObject(), examined.GetInteger());
        }
        /// <summary>
        /// Indicates how much data was consumed, and how much examined, from a read operation
        /// </summary>
        public override void AdvanceTo(SequencePosition consumed, SequencePosition examined)
        {
            var cPage = (MappedPage)consumed.GetObject();
            var ePage = (MappedPage)examined.GetObject();

            if (cPage == null || ePage == null)
            {
                if (_first == null)
                {
                    return;                 // that's fine - means they called Advance on an empty EOF
                }
                Throw.Argument("Invalid position; consumed/examined must remain inside the buffer");
            }

            Debug.Assert(ePage != null, "No examined page");
            Debug.Assert(cPage != null, "No consumed page");

            MappedPage newKeep;
            var        cOffset = consumed.GetInteger();

            if (cOffset == cPage.Capacity)
            {
                newKeep = cPage.Next;
            }
            else
            {
                newKeep          = cPage;
                newKeep.Consumed = cOffset;
            }
            if (newKeep == null)
            {
                DebugLog($"Retaining nothing");
                _last = null;
            }
            else
            {
                DebugLog($"Retaining page {newKeep}");
            }

            // now drop any pages we don't need
            if (newKeep != _first)
            {
                var page = _first;
                while (page != null && page != newKeep)
                {
                    DebugLog($"Dropping page {page}");
                    page.Dispose();
                    page = page.Next;
                }
                _first = newKeep;
            }

            // check whether they looked at everything
            if (_last == null)
            {
                _loadMore = true; // definitely
            }
            else
            {
                var eOffset = examined.GetInteger();
                _loadMore = ePage == _last && eOffset == ePage.Capacity;
            }
            DebugLog($"After AdvanceTo, {CountAvailable(_first)} available bytes, {_remaining} remaining unloaded bytes, load more: {_loadMore}");
        }
예제 #18
0
        internal bool TryGetBuffer(SequencePosition start, SequencePosition end, out ReadOnlyMemory <T> data, out SequencePosition next)
        {
            if (start.GetObject() == null)
            {
                data = default;
                next = default;
                return(false);
            }

            int          startIndex = start.GetInteger();
            int          endIndex   = end.GetInteger();
            SequenceType type       = GetSequenceType();

            startIndex = GetIndex(startIndex);
            endIndex   = GetIndex(endIndex);

            switch (type)
            {
            case SequenceType.MemoryList:
                var        segment             = (IMemoryList <T>)start.GetObject();
                Memory <T> bufferSegmentMemory = segment.Memory;
                int        currentEndIndex     = bufferSegmentMemory.Length;

                if (segment == end.GetObject())
                {
                    currentEndIndex = endIndex;
                    next            = default;
                }
                else
                {
                    IMemoryList <T> nextSegment = segment.Next;
                    if (nextSegment == null)
                    {
                        if (end.GetObject() != null)
                        {
                            ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
                        }

                        next = default;
                    }
                    else
                    {
                        next = new SequencePosition(nextSegment, 0);
                    }
                }

                data = bufferSegmentMemory.Slice(startIndex, currentEndIndex - startIndex);
                return(true);

            case SequenceType.OwnedMemory:
                var ownedMemory = (OwnedMemory <T>)start.GetObject();
                if (ownedMemory != end.GetObject())
                {
                    ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
                }

                data = ownedMemory.Memory.Slice(startIndex, endIndex - startIndex);
                next = default;
                return(true);

            case SequenceType.Array:
                var array = (T[])start.GetObject();

                if (array != end.GetObject())
                {
                    ThrowHelper.ThrowInvalidOperationException_EndPositionNotReached();
                }

                data = new Memory <T>(array, startIndex, endIndex - startIndex);
                next = default;
                return(true);

            default:
                ThrowHelper.ThrowInvalidOperationException_UnexpectedSegmentType();
                next = default;
                data = default;
                return(false);
            }
        }
 public static SequencePosition Min(SequencePosition x, SequencePosition y) =>
 x.GetInteger() < y.GetInteger() ? x : y;
 public static SequencePosition Add(this SequencePosition x, int y)
 => new SequencePosition(x.GetObject(), x.GetInteger() + y);
예제 #21
0
        /// <inheritdoc />
        public override void AdvanceTo(SequencePosition consumed, SequencePosition examined)
        {
            ThrowIfCompleted();

            if (_readHead == null || _readTail == null)
            {
                ThrowHelper.ThrowInvalidOperationException_NoDataRead();
            }

            AdvanceTo((BufferSegment)consumed.GetObject(), consumed.GetInteger(), (BufferSegment)examined.GetObject(), examined.GetInteger());
        }
예제 #22
0
        internal void AdvanceReader(SequencePosition consumed, SequencePosition examined)
        {
            BufferSegment returnStart = null;
            BufferSegment returnEnd   = null;

            Action continuation = null;

            lock (_sync)
            {
                var examinedEverything = false;
                if (examined.GetObject() == _commitHead)
                {
                    examinedEverything = _commitHead != null?examined.GetInteger() == _commitHeadIndex - _commitHead.Start : examined.GetInteger() == 0;
                }

                if (consumed.GetObject() != null)
                {
                    if (_readHead == null)
                    {
                        ThrowHelper.ThrowInvalidOperationException_AdvanceToInvalidCursor();
                        return;
                    }

                    var consumedSegment = (BufferSegment)consumed.GetObject();

                    returnStart = _readHead;
                    returnEnd   = consumedSegment;

                    // Check if we crossed _maximumSizeLow and complete backpressure
                    long consumedBytes = new ReadOnlySequence <byte>(returnStart, _readHeadIndex, consumedSegment, consumed.GetInteger()).Length;
                    long oldLength     = _length;
                    _length -= consumedBytes;

                    if (oldLength >= _resumeWriterThreshold &&
                        _length < _resumeWriterThreshold)
                    {
                        continuation = _writerAwaitable.Complete();
                    }

                    // Check if we consumed entire last segment
                    // if we are going to return commit head we need to check that there is no writing operation that
                    // might be using tailspace
                    if (consumed.GetInteger() == returnEnd.Length && _writingHead != returnEnd)
                    {
                        BufferSegment nextBlock = returnEnd.NextSegment;
                        if (_commitHead == returnEnd)
                        {
                            _commitHead      = nextBlock;
                            _commitHeadIndex = 0;
                        }

                        _readHead      = nextBlock;
                        _readHeadIndex = 0;
                        returnEnd      = nextBlock;
                    }
                    else
                    {
                        _readHead      = consumedSegment;
                        _readHeadIndex = consumed.GetInteger();
                    }
                }

                // We reset the awaitable to not completed if we've examined everything the producer produced so far
                // but only if writer is not completed yet
                if (examinedEverything && !_writerCompletion.IsCompleted)
                {
                    // Prevent deadlock where reader awaits new data and writer await backpressure
                    if (!_writerAwaitable.IsCompleted)
                    {
                        ThrowHelper.ThrowInvalidOperationException_BackpressureDeadlock();
                    }
                    _readerAwaitable.Reset();
                }

                while (returnStart != null && returnStart != returnEnd)
                {
                    returnStart.ResetMemory();
                    ReturnSegmentUnsynchronized(returnStart);
                    returnStart = returnStart.NextSegment;
                }

                _readingState.End();
            }

            TrySchedule(_writerScheduler, continuation);
        }