/// <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; } }
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); }
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; } }
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); }
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(); } }
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; } }
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)); }
/// <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; } }
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); } }
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(); }
/// <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}"); }
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);
/// <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()); }
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); }