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; }
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); }
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);
// 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; }
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)); }
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); } }
/// <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)); }
/// <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);
internal void ClearCursors() { _start = default(ReadCursor); _end = default(ReadCursor); }
/// <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))); }
/// <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); }
void IPipelineReader.Advance(ReadCursor consumed, ReadCursor examined) => AdvanceReader(consumed, examined);
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)); }
// 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); }
internal ReadableBuffer(byte[] startSegment, int startIndex, int length) { BufferStart = new ReadCursor(startSegment, startIndex); BufferEnd = new ReadCursor(startSegment, startIndex + length); }
/// <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)); }
internal ReadableBuffer(ReadCursor start, ReadCursor end) { BufferStart = start; BufferEnd = end; }
/// <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)); }
internal ReadableBuffer(BufferSegment startSegment, int startIndex, BufferSegment endSegment, int endIndex) { BufferStart = new ReadCursor(startSegment, startIndex); BufferEnd = new ReadCursor(endSegment, endIndex); }
internal ReadableBuffer(OwnedMemory <byte> startSegment, int startIndex, int length) { BufferStart = new ReadCursor(startSegment, startIndex); BufferEnd = new ReadCursor(startSegment, startIndex + length); }
internal void ClearCursors() { BufferStart = default(ReadCursor); BufferEnd = default(ReadCursor); }