Beispiel #1
0
 public ReadableBufferReader(ReadCursor start, ReadCursor end) : this()
 {
     _end           = false;
     _index         = 0;
     _overallIndex  = 0;
     _enumerator    = new MemoryEnumerator(start, end);
     _currentMemory = default(Span <byte>);
     while (_enumerator.MoveNext())
     {
         if (!_enumerator.Current.IsEmpty)
         {
             _currentMemory = _enumerator.Current.Span;
             return;
         }
     }
     _end = true;
 }
Beispiel #2
0
        private ReadableBuffer(ref ReadableBuffer buffer)
        {
            var begin = buffer._start;
            var end   = buffer._end;

            BufferSegment segmentTail;
            var           segmentHead = BufferSegment.Clone(begin, end, out segmentTail);

            begin = new ReadCursor(segmentHead);
            end   = new ReadCursor(segmentTail, segmentTail.End);

            _start = begin;
            _end   = end;

            _length = buffer._length;

            begin.TryGetBuffer(end, out _first, out begin);
        }
Beispiel #3
0
        public void AdvanceReader(ReadCursor consumed, ReadCursor examined)
        {
            BufferSegment returnStart = null;
            BufferSegment returnEnd   = null;

            if (!consumed.IsDefault)
            {
                returnStart     = _readHead;
                returnEnd       = consumed.Segment;
                _readHead       = consumed.Segment;
                _readHead.Start = consumed.Index;
            }

            // Reading commit head shared with writer
            lock (_sync)
            {
                if (!examined.IsDefault &&
                    examined.Segment == _commitHead &&
                    examined.Index == _commitHeadIndex &&
                    Reading.Status == TaskStatus.WaitingForActivation)
                {
                    Interlocked.CompareExchange(
                        ref _awaitableState,
                        _awaitableIsNotCompleted,
                        _awaitableIsCompleted);
                }
            }

            while (returnStart != returnEnd)
            {
                var returnSegment = returnStart;
                returnStart = returnStart.Next;
                returnSegment.Dispose();
            }

#if DEBUG
            _consumingLocation = null;
#endif
            // CompareExchange not required as its setting to current value if test fails
            if (Interlocked.Exchange(ref _consumingState, State.NotActive) != State.Active)
            {
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.NotConsumingToComplete);
            }
        }
        public static int Seek(ReadCursor begin, ReadCursor end, out ReadCursor result, byte byte0, byte byte1, byte byte2)
        {
            var enumerator = new BufferEnumerator(begin, end);

            while (enumerator.MoveNext())
            {
                var span  = enumerator.Current.Span;
                int index = span.IndexOfAny(byte0, byte1, byte2);

                if (index != -1)
                {
                    result = enumerator.CreateCursor(index);
                    return(span[index]);
                }
            }

            result = end;
            return(-1);
        }
        public static int Seek(ReadCursor begin, ReadCursor end, out ReadCursor result, byte byte0)
        {
            var enumerator = new SegmentEnumerator(begin, end);

            while (enumerator.MoveNext())
            {
                var segmentPart = enumerator.Current;
                var segment     = segmentPart.Segment;
                var span        = segment.Memory.Span.Slice(segmentPart.Start, segmentPart.Length);

                int index = span.IndexOfVectorized(byte0);
                if (index != -1)
                {
                    result = new ReadCursor(segment, segmentPart.Start + index);
                    return(span[index]);
                }
            }

            result = end;
            return(-1);
        }
        internal static long GetLength(ReadCursor begin, ReadCursor end)
        {
            if (begin.IsDefault)
            {
                return(0);
            }

            var segment = begin.Segment;

            switch (segment)
            {
            case IMemoryList <byte> bufferSegment:
                return(GetLength(bufferSegment, begin.Index, end.Get <IMemoryList <byte> >(), end.Index));

            case byte[] _:
            case OwnedMemory <byte> _:
                return(end.Index - begin.Index);
            }

            PipelinesThrowHelper.ThrowInvalidOperationException(ExceptionResource.UnexpectedSegmentType);
            return(default);
Beispiel #7
0
        // Called by the READER
        void IPipeReader.Advance(ReadCursor consumed, ReadCursor examined)
        {
            BufferSegment returnStart = null;
            BufferSegment returnEnd   = null;

            if (!consumed.IsDefault)
            {
                returnStart = _head;
                returnEnd   = consumed.Segment;
                _head       = consumed.Segment;
                _head.Start = consumed.Index;
            }

            // Again, we don't need an interlock here because Read and Write proceed serially.
            // REVIEW: examined.IsEnd (PipelineReaderWriter has changed this logic)
            var consumedEverything = examined.IsEnd &&
                                     Reading.Status == TaskStatus.WaitingForActivation &&
                                     _awaitableState == _awaitableIsCompleted;

            CompareExchange(ref _cancelledState, CancelledState.NotCancelled, CancelledState.CancellationObserved);

            if (consumedEverything && _cancelledState != CancelledState.CancellationRequested)
            {
                _awaitableState = _awaitableIsNotCompleted;
            }

            while (returnStart != returnEnd)
            {
                var returnSegment = returnStart;
                returnStart = returnStart.Next;
                returnSegment.Dispose();
            }

            if (!_consuming)
            {
                throw new InvalidOperationException("No ongoing consuming operation to complete.");
            }
            _consuming = false;
        }
Beispiel #8
0
        private ReadableBuffer Read()
        {
            _readingState.Begin(ExceptionResource.AlreadyReading);

            ReadCursor readEnd;
            // No need to read end if there is no head
            var head = _readHead;

            if (head == null)
            {
                readEnd = new ReadCursor(null);
            }
            else
            {
                // Reading commit head shared with writer
                lock (_sync)
                {
                    readEnd = new ReadCursor(_commitHead, _commitHeadIndex);
                }
            }

            return(new ReadableBuffer(new ReadCursor(head), readEnd));
        }
Beispiel #9
0
        private void GetResult(ref ReadResult result)
        {
            if (_writerCompletion.IsCompletedOrThrow())
            {
                result.ResultFlags |= ResultFlags.Completed;
            }

            var isCancelled = _readerAwaitable.ObserveCancelation();

            if (isCancelled)
            {
                result.ResultFlags |= ResultFlags.Cancelled;
            }

            // No need to read end if there is no head
            var head = _readHead;

            if (head != null)
            {
                // Reading commit head shared with writer
                result.ResultBuffer.BufferEnd.Segment = _commitHead;
                result.ResultBuffer.BufferEnd.Index   = _commitHeadIndex;
                result.ResultBuffer.BufferLength      = ReadCursor.GetLength(head, head.Start, _commitHead, _commitHeadIndex);

                result.ResultBuffer.BufferStart.Segment = head;
                result.ResultBuffer.BufferStart.Index   = head.Start;
            }

            if (isCancelled)
            {
                _readingState.BeginTentative(ExceptionResource.AlreadyReading);
            }
            else
            {
                _readingState.Begin(ExceptionResource.AlreadyReading);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Forms a slice out of the given <see cref="ReadableBuffer"/>, beginning at 'start', ending at the existing <see cref="ReadableBuffer"/>'s end.
        /// </summary>
        /// <param name="start">The starting (inclusive) <see cref="ReadCursor"/> at which to begin this slice.</param>
        public ReadableBuffer Slice(ReadCursor start)
        {
            ReadCursorOperations.BoundsCheck(BufferEnd, start);

            return(new ReadableBuffer(start, BufferEnd));
        }
        /// <summary>
        /// Searches for a span of bytes in the <see cref="ReadableBuffer"/> and returns a sliced <see cref="ReadableBuffer"/> that
        /// contains all data up to and excluding the first byte of the span, and a <see cref="ReadCursor"/> that points to the last byte of the span.
        /// </summary>
        /// <param name="span">The <see cref="Span{Byte}"/> byte to search for</param>
        /// <param name="slice">A <see cref="ReadableBuffer"/> that matches all data up to and excluding the first byte</param>
        /// <param name="cursor">A <see cref="ReadCursor"/> that points to the second byte</param>
        /// <returns>True if the byte sequence was found, false if not found</returns>
        public static bool TrySliceTo(this ReadableBuffer buffer, Span <byte> span, out ReadableBuffer slice, out ReadCursor cursor)
        {
            var result    = false;
            var subBuffer = buffer;

            do
            {
                // Find the first byte
                if (!subBuffer.TrySliceTo(span[0], out slice, out cursor))
                {
                    break;
                }

                // Move the buffer to where you fonud the first byte then search for the next byte
                subBuffer = buffer.Slice(cursor);

                if (subBuffer.StartsWith(span))
                {
                    slice  = buffer.Slice(buffer.Start, cursor);
                    result = true;
                    break;
                }

                // REVIEW: We need to check the performance of Slice in a loop like this
                // Not a match so skip(1)
                subBuffer = subBuffer.Slice(1);
            } while (!subBuffer.IsEmpty);

            return(result);
        }
        /// <summary>
        /// Searches for 2 sequential bytes in the <see cref="ReadableBuffer"/> and returns a sliced <see cref="ReadableBuffer"/> that
        /// contains all data up to and excluding the first byte, and a <see cref="ReadCursor"/> that points to the second byte.
        /// </summary>
        /// <param name="b1">The first byte to search for</param>
        /// <param name="b2">The second byte to search for</param>
        /// <param name="slice">A <see cref="ReadableBuffer"/> slice that contains all data up to and excluding the first byte.</param>
        /// <param name="cursor">A <see cref="ReadCursor"/> that points to the second byte</param>
        /// <returns>True if the byte sequence was found, false if not found</returns>
        public static unsafe bool TrySliceTo(this ReadableBuffer buffer, byte b1, byte b2, out ReadableBuffer slice, out ReadCursor cursor)
        {
            // use address of ushort rather than stackalloc as the inliner won't inline functions with stackalloc
            ushort twoBytes;
            byte * byteArray = (byte *)&twoBytes;

            byteArray[0] = b1;
            byteArray[1] = b2;
            return(buffer.TrySliceTo(new Span <byte>(byteArray, 2), out slice, out cursor));
        }
Beispiel #13
0
 /// <summary>
 /// Moves forward the pipelines read cursor to after the consumed data.
 /// </summary>
 /// <param name="consumed">Marks the extent of the data that has been succesfully proceesed.</param>
 /// <param name="examined">Marks the extent of the data that has been read and examined.</param>
 /// <remarks>
 /// The memory for the consumed data will be released and no longer available.
 /// The examined data communicates to the pipeline when it should signal more data is available.
 /// </remarks>
 public void Advance(ReadCursor consumed, ReadCursor examined) => _input.AdvanceReader(consumed, examined);
Beispiel #14
0
 internal void ClearCursors()
 {
     _start = default(ReadCursor);
     _end   = default(ReadCursor);
 }
Beispiel #15
0
 /// <summary>
 /// Forms a slice out of the given <see cref="ReadableBuffer"/>, beginning at 'start', and is at most length bytes
 /// </summary>
 /// <param name="start">The starting (inclusive) <see cref="ReadCursor"/> at which to begin this slice.</param>
 /// <param name="length">The length of the slice</param>
 public ReadableBuffer Slice(ReadCursor start, int length)
 {
     return(Slice(start, start.Seek(length)));
 }
Beispiel #16
0
        /// <summary>
        /// Searches for a byte in the <see cref="ReadableBuffer"/> and returns a sliced <see cref="ReadableBuffer"/> that
        /// contains all data up to and excluding the byte, and a <see cref="ReadCursor"/> that points to the byte.
        /// </summary>
        /// <param name="b1">The first byte to search for</param>
        /// <param name="slice">A <see cref="ReadableBuffer"/> slice that contains all data up to and excluding the first byte.</param>
        /// <param name="cursor">A <see cref="ReadCursor"/> that points to the second byte</param>
        /// <returns>True if the byte sequence was found, false if not found</returns>
        public bool TrySliceTo(byte b1, out ReadableBuffer slice, out ReadCursor cursor)
        {
            if (IsEmpty)
            {
                slice  = default(ReadableBuffer);
                cursor = default(ReadCursor);
                return(false);
            }

            var byte0Vector = CommonVectors.GetVector(b1);

            var seek = 0;

            foreach (var memory in this)
            {
                var currentSpan = memory.Span;
                var found       = false;

                if (Vector.IsHardwareAccelerated)
                {
                    while (currentSpan.Length >= VectorWidth)
                    {
                        var data        = currentSpan.Read <Vector <byte> >();
                        var byte0Equals = Vector.Equals(data, byte0Vector);

                        if (byte0Equals.Equals(Vector <byte> .Zero))
                        {
                            currentSpan = currentSpan.Slice(VectorWidth);
                            seek       += VectorWidth;
                        }
                        else
                        {
                            var index = FindFirstEqualByte(ref byte0Equals);
                            seek += index;
                            found = true;
                            break;
                        }
                    }
                }

                if (!found)
                {
                    // Slow search
                    for (int i = 0; i < currentSpan.Length; i++)
                    {
                        if (currentSpan[i] == b1)
                        {
                            found = true;
                            break;
                        }
                        seek++;
                    }
                }

                if (found)
                {
                    cursor = _start.Seek(seek);
                    slice  = Slice(_start, cursor);
                    return(true);
                }
            }

            slice  = default(ReadableBuffer);
            cursor = default(ReadCursor);
            return(false);
        }
Beispiel #17
0
 void IPipelineReader.Advance(ReadCursor consumed, ReadCursor examined) => AdvanceReader(consumed, examined);
Beispiel #18
0
        public static unsafe int Seek(ReadCursor begin, ReadCursor end, out ReadCursor result, byte byte0, byte byte1, byte byte2)
        {
            result = end;
            var block = begin.Segment;

            if (block == null)
            {
                return(-1);
            }

            var index        = begin.Index;
            var wasLastBlock = block.Next == null;
            var following    = block.End - index;
            int byteIndex    = int.MaxValue;

            while (true)
            {
                while (following == 0)
                {
                    if ((block == end.Segment && index >= end.Index) ||
                        wasLastBlock)
                    {
                        return(-1);
                    }
                    block        = block.Next;
                    index        = block.Start;
                    wasLastBlock = block.Next == null;
                    following    = block.End - index;
                }
                ArraySegment <byte> array;
                var getArrayResult = block.Memory.TryGetArray(out array);
                Debug.Assert(getArrayResult);

                while (following > 0)
                {
                    // Need unit tests to test Vector path
#if !DEBUG
                    // Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079
                    if (Vector.IsHardwareAccelerated)
                    {
#endif
                    if (following >= _vectorSpan)
                    {
                        var data = new Vector <byte>(array.Array, array.Offset + index);

                        var byteEquals = Vector.Equals(data, GetVector(byte0));
                        byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte1)));
                        byteEquals = Vector.ConditionalSelect(byteEquals, byteEquals, Vector.Equals(data, GetVector(byte2)));

                        if (!byteEquals.Equals(Vector <byte> .Zero))
                        {
                            byteIndex = LocateFirstFoundByte(byteEquals);
                        }

                        if (byteIndex == int.MaxValue)
                        {
                            following -= _vectorSpan;
                            index     += _vectorSpan;

                            if (block == end.Segment && index >= end.Index)
                            {
                                return(-1);
                            }

                            continue;
                        }

                        if (block == end.Segment && index + byteIndex >= end.Index)
                        {
                            // Ensure iterator is left at limit position
                            return(-1);
                        }

                        result = new ReadCursor(block, index + byteIndex);
                        return(array.Array[array.Offset + index + byteIndex]);
                    }
                    // Need unit tests to test Vector path
#if !DEBUG
                }
#endif

                    fixed(byte *pCurrentFixed = array.Array)
                    {
                        var pCurrent = pCurrentFixed + array.Offset + index;

                        var pEnd = block == end.Segment ? pCurrentFixed + array.Offset + end.Index : pCurrent + following;

                        while (pCurrent < pEnd)
                        {
                            if (*pCurrent == byte0)
                            {
                                result = new ReadCursor(block, index);
                                return(byte0);
                            }
                            if (*pCurrent == byte1)
                            {
                                result = new ReadCursor(block, index);
                                return(byte1);
                            }
                            if (*pCurrent == byte2)
                            {
                                result = new ReadCursor(block, index);
                                return(byte2);
                            }
                            pCurrent++;
                            index++;
                        }
                    }

                    following = 0;
                    break;
                }
            }
        }
 internal ReadableBuffer(ReadCursor start, ReadCursor end)
 {
     BufferStart  = start;
     BufferEnd    = end;
     BufferLength = start.GetLength(end);
 }
 internal void ClearCursors()
 {
     BufferStart = default;
     BufferEnd   = default;
 }
        /// <summary>
        /// Forms a slice out of the given <see cref="ReadableBuffer"/>, beginning at 'start', ending at the existing <see cref="ReadableBuffer"/>'s end.
        /// </summary>
        /// <param name="start">The starting (inclusive) <see cref="ReadCursor"/> at which to begin this slice.</param>
        public ReadableBuffer Slice(ReadCursor start)
        {
            BufferEnd.BoundsCheck(start);

            return(new ReadableBuffer(start, BufferEnd));
        }
Beispiel #22
0
        // Reading

        void IPipeReader.Advance(ReadCursor consumed, ReadCursor examined)
        {
            BufferSegment returnStart = null;
            BufferSegment returnEnd   = null;

            // Reading commit head shared with writer
            Action continuation = null;

            lock (_sync)
            {
                var examinedEverything = examined.Segment == _commitHead && examined.Index == _commitHeadIndex;

                if (!consumed.IsDefault)
                {
                    if (_readHead == null)
                    {
                        PipelinesThrowHelper.ThrowInvalidOperationException(ExceptionResource.AdvanceToInvalidCursor);
                        return;
                    }

                    var consumedSegment = consumed.GetSegment();

                    returnStart = _readHead;
                    returnEnd   = consumedSegment;

                    // Check if we crossed _maximumSizeLow and complete backpressure
                    var consumedBytes = ReadCursor.GetLength(returnStart, returnStart.Start, consumedSegment, consumed.Index);
                    var oldLength     = _length;
                    _length -= consumedBytes;

                    if (oldLength >= _maximumSizeLow &&
                        _length < _maximumSizeLow)
                    {
                        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.Index == returnEnd.End &&
                        !(_commitHead == returnEnd && _writingState.IsActive))
                    {
                        var nextBlock = returnEnd.Next;
                        if (_commitHead == returnEnd)
                        {
                            _commitHead      = nextBlock;
                            _commitHeadIndex = nextBlock?.Start ?? 0;
                        }

                        _readHead = nextBlock;
                        returnEnd = nextBlock;
                    }
                    else
                    {
                        _readHead       = consumedSegment;
                        _readHead.Start = consumed.Index;
                    }
                }

                // 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)
                    {
                        PipelinesThrowHelper.ThrowInvalidOperationException(ExceptionResource.BackpressureDeadlock);
                    }
                    _readerAwaitable.Reset();
                }

                _readingState.End(ExceptionResource.NoReadToComplete);

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

            TrySchedule(_writerScheduler, continuation);
        }
 public static void Advance(this IPipeReader input, ReadCursor cursor)
 {
     input.Advance(cursor, cursor);
 }
Beispiel #24
0
 internal ReadableBuffer(byte[] startSegment, int startIndex, int length)
 {
     BufferStart = new ReadCursor(startSegment, startIndex);
     BufferEnd   = new ReadCursor(startSegment, startIndex + length);
 }
Beispiel #25
0
 /// <summary>
 /// Forms a slice out of the given <see cref="ReadableBuffer"/>, beginning at 'start', ending at 'end' (inclusive).
 /// </summary>
 /// <param name="start">The index at which to begin this slice.</param>
 /// <param name="end">The end (inclusive) of the slice</param>
 public ReadableBuffer Slice(int start, ReadCursor end)
 {
     return(Slice(_start.Seek(start), end));
 }
Beispiel #26
0
 internal ReadableBuffer(ReadCursor start, ReadCursor end)
 {
     BufferStart = start;
     BufferEnd   = end;
 }
Beispiel #27
0
 /// <summary>
 /// Forms a slice out of the given <see cref="ReadableBuffer"/>, beginning at 'start', ending at the existing <see cref="ReadableBuffer"/>'s end.
 /// </summary>
 /// <param name="start">The starting (inclusive) <see cref="ReadCursor"/> at which to begin this slice.</param>
 public ReadableBuffer Slice(ReadCursor start)
 {
     return(new ReadableBuffer(start, _end));
 }
Beispiel #28
0
 internal ReadableBuffer(BufferSegment startSegment, int startIndex, BufferSegment endSegment, int endIndex)
 {
     BufferStart = new ReadCursor(startSegment, startIndex);
     BufferEnd   = new ReadCursor(endSegment, endIndex);
 }
Beispiel #29
0
 internal ReadableBuffer(OwnedMemory <byte> startSegment, int startIndex, int length)
 {
     BufferStart = new ReadCursor(startSegment, startIndex);
     BufferEnd   = new ReadCursor(startSegment, startIndex + length);
 }
Beispiel #30
0
 internal void ClearCursors()
 {
     BufferStart = default(ReadCursor);
     BufferEnd   = default(ReadCursor);
 }