解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
45.16 KiB
X
#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// 注:本文件移植自 dotnet/corefx release/3.1(System.Memory/src/System/Buffers/ 下的 SequenceReader.cs、SequenceReader.Search.cs 与 SequenceReaderExtensions.Binary.cs,合并为单文件)。
// 为 .NET Framework 4.5 / 4.6.1 / 4.6.2 与 .NET Standard 2.0 提供与 BCL 3.1 面一致的 System.Buffers.SequenceReader<T> 与有符号端序扩展;
// 高版本框架由 Stub/SequenceReaderForwards.cs 通过 TypeForwardedTo 转发到 BCL。
// 相比原版主要差异:
// - 移除对 System.Memory 内部实现(ReadOnlySequence.GetFirstSpan、ThrowHelper 等)的依赖,改为等价的公共 API 调用
// - ThrowHelper 调用改为直接 throw new 语句
// - 原三文件合并为单文件,采用文件级命名空间

using System.Buffers.Binary;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.Buffers;

/// <summary>基于 <see cref="ReadOnlySequence{T}"/> 的高性能顺序读取器。移植自 BCL 3.1,为 net45 / net461 / net462 / netstandard2.0 提供与高版本框架一致的类型</summary>
/// <typeparam name="T">序列元素类型</typeparam>
public ref partial struct SequenceReader<T> where T : unmanaged, IEquatable<T>
{
    private SequencePosition _currentPosition;
    private SequencePosition _nextPosition;
    private bool _moreData;
    private long _length;

    /// <summary>
    /// Create a <see cref="SequenceReader{T}"/> over the given <see cref="ReadOnlySequence{T}"/>.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public SequenceReader(ReadOnlySequence<T> sequence)
    {
        CurrentSpanIndex = 0;
        Consumed = 0;
        Sequence = sequence;
        _currentPosition = sequence.Start;
        _length = -1;

        // 等价替换 System.Memory 内部 GetFirstSpan:TryGet 返回首段内存并推进到下一段起始位置(末段/单段时推出默认位置)
        _nextPosition = _currentPosition;
        var hasFirst = sequence.TryGet(ref _nextPosition, out ReadOnlyMemory<T> firstMemory, advance: true);
        CurrentSpan = hasFirst ? firstMemory.Span : default;
        _moreData = CurrentSpan.Length > 0;

        if (!_moreData && !sequence.IsSingleSegment)
        {
            _moreData = true;
            GetNextSpan();
        }
    }

    /// <summary>
    /// True when there is no more data in the <see cref="Sequence"/>.
    /// </summary>
    public readonly bool End => !_moreData;

    /// <summary>
    /// The underlying <see cref="ReadOnlySequence{T}"/> for the reader.
    /// </summary>
    public readonly ReadOnlySequence<T> Sequence { get; }

    /// <summary>
    /// The current position in the <see cref="Sequence"/>.
    /// </summary>
    public readonly SequencePosition Position
        => Sequence.GetPosition(CurrentSpanIndex, _currentPosition);

    /// <summary>
    /// The current segment in the <see cref="Sequence"/> as a span.
    /// </summary>
    public ReadOnlySpan<T> CurrentSpan { readonly get; private set; }

    /// <summary>
    /// The index in the <see cref="CurrentSpan"/>.
    /// </summary>
    public int CurrentSpanIndex { readonly get; private set; }

    /// <summary>
    /// The unread portion of the <see cref="CurrentSpan"/>.
    /// </summary>
    public readonly ReadOnlySpan<T> UnreadSpan
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => CurrentSpan.Slice(CurrentSpanIndex);
    }

    /// <summary>
    /// The total number of <typeparamref name="T"/>'s processed by the reader.
    /// </summary>
    public long Consumed { readonly get; private set; }

    /// <summary>
    /// Remaining <typeparamref name="T"/>'s in the reader's <see cref="Sequence"/>.
    /// </summary>
    public readonly long Remaining => Length - Consumed;

    /// <summary>
    /// Count of <typeparamref name="T"/> in the reader's <see cref="Sequence"/>.
    /// </summary>
    public readonly long Length
    {
        get
        {
            if (_length < 0)
            {
                // Cast-away readonly to initialize lazy field
                Volatile.Write(ref Unsafe.AsRef(_length), Sequence.Length);
            }
            return _length;
        }
    }

    /// <summary>
    /// Peeks at the next value without advancing the reader.
    /// </summary>
    /// <param name="value">The next value or default if at the end.</param>
    /// <returns>False if at the end of the reader.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public readonly bool TryPeek(out T value)
    {
        if (_moreData)
        {
            value = CurrentSpan[CurrentSpanIndex];
            return true;
        }
        else
        {
            value = default;
            return false;
        }
    }

    /// <summary>
    /// Read the next value and advance the reader.
    /// </summary>
    /// <param name="value">The next value or default if at the end.</param>
    /// <returns>False if at the end of the reader.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TryRead(out T value)
    {
        if (End)
        {
            value = default;
            return false;
        }

        value = CurrentSpan[CurrentSpanIndex];
        CurrentSpanIndex++;
        Consumed++;

        if (CurrentSpanIndex >= CurrentSpan.Length)
        {
            GetNextSpan();
        }

        return true;
    }

    /// <summary>
    /// Move the reader back the specified number of items.
    /// </summary>
    /// <exception cref="ArgumentOutOfRangeException">
    /// Thrown if trying to rewind a negative amount or more than <see cref="Consumed"/>.
    /// </exception>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Rewind(long count)
    {
        if ((ulong)count > (ulong)Consumed)
        {
            throw new ArgumentOutOfRangeException(nameof(count));
        }

        Consumed -= count;

        if (CurrentSpanIndex >= count)
        {
            CurrentSpanIndex -= (int)count;
            _moreData = true;
        }
        else
        {
            // Current segment doesn't have enough data, scan backward through segments
            RetreatToPreviousSpan(Consumed);
        }
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private void RetreatToPreviousSpan(long consumed)
    {
        ResetReader();
        Advance(consumed);
    }

    private void ResetReader()
    {
        CurrentSpanIndex = 0;
        Consumed = 0;
        _currentPosition = Sequence.Start;
        _nextPosition = _currentPosition;

        if (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory<T> memory, advance: true))
        {
            _moreData = true;

            if (memory.Length == 0)
            {
                CurrentSpan = default;
                // No data in the first span, move to one with data
                GetNextSpan();
            }
            else
            {
                CurrentSpan = memory.Span;
            }
        }
        else
        {
            // No data in any spans and at end of sequence
            _moreData = false;
            CurrentSpan = default;
        }
    }

    /// <summary>
    /// Get the next segment with available data, if any.
    /// </summary>
    private void GetNextSpan()
    {
        if (!Sequence.IsSingleSegment)
        {
            SequencePosition previousNextPosition = _nextPosition;
            while (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory<T> memory, advance: true))
            {
                _currentPosition = previousNextPosition;
                if (memory.Length > 0)
                {
                    CurrentSpan = memory.Span;
                    CurrentSpanIndex = 0;
                    return;
                }
                else
                {
                    CurrentSpan = default;
                    CurrentSpanIndex = 0;
                    previousNextPosition = _nextPosition;
                }
            }
        }
        _moreData = false;
    }

    /// <summary>
    /// Move the reader ahead the specified number of items.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Advance(long count)
    {
        const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000);
        if ((count & TooBigOrNegative) == 0 && CurrentSpan.Length - CurrentSpanIndex > (int)count)
        {
            CurrentSpanIndex += (int)count;
            Consumed += count;
        }
        else
        {
            // Can't satisfy from the current span
            AdvanceToNextSpan(count);
        }
    }

    /// <summary>
    /// Unchecked helper to avoid unnecessary checks where you know count is valid.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal void AdvanceCurrentSpan(long count)
    {
        Debug.Assert(count >= 0);

        Consumed += count;
        CurrentSpanIndex += (int)count;
        if (CurrentSpanIndex >= CurrentSpan.Length)
            GetNextSpan();
    }

    /// <summary>
    /// Only call this helper if you know that you are advancing in the current span
    /// with valid count and there is no need to fetch the next one.
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal void AdvanceWithinSpan(long count)
    {
        Debug.Assert(count >= 0);

        Consumed += count;
        CurrentSpanIndex += (int)count;

        Debug.Assert(CurrentSpanIndex < CurrentSpan.Length);
    }

    private void AdvanceToNextSpan(long count)
    {
        if (count < 0)
        {
            throw new ArgumentOutOfRangeException(nameof(count));
        }

        Consumed += count;
        while (_moreData)
        {
            int remaining = CurrentSpan.Length - CurrentSpanIndex;

            if (remaining > count)
            {
                CurrentSpanIndex += (int)count;
                count = 0;
                break;
            }

            // As there may not be any further segments we need to
            // push the current index to the end of the span.
            CurrentSpanIndex += remaining;
            count -= remaining;
            Debug.Assert(count >= 0);

            GetNextSpan();

            if (count == 0)
            {
                break;
            }
        }

        if (count != 0)
        {
            // Not enough data left- adjust for where we actually ended and throw
            Consumed -= count;
            throw new ArgumentOutOfRangeException(nameof(count));
        }
    }

    /// <summary>
    /// Copies data from the current <see cref="Position"/> to the given <paramref name="destination"/> span if there
    /// is enough data to fill it.
    /// </summary>
    /// <remarks>
    /// This API is used to copy a fixed amount of data out of the sequence if possible. It does not advance
    /// the reader. To look ahead for a specific stream of data <see cref="IsNext(ReadOnlySpan{T}, bool)"/> can be used.
    /// </remarks>
    /// <param name="destination">Destination span to copy to.</param>
    /// <returns>True if there is enough data to completely fill the <paramref name="destination"/> span.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public readonly bool TryCopyTo(Span<T> destination)
    {
        // This API doesn't advance to facilitate conditional advancement based on the data returned.
        // We don't provide an advance option to allow easier utilizing of stack allocated destination spans.
        // (Because we can make this method readonly we can guarantee that we won't capture the span.)

        ReadOnlySpan<T> firstSpan = UnreadSpan;
        if (firstSpan.Length >= destination.Length)
        {
            firstSpan.Slice(0, destination.Length).CopyTo(destination);
            return true;
        }

        // Not enough in the current span to satisfy the request, fall through to the slow path
        return TryCopyMultisegment(destination);
    }

    internal readonly bool TryCopyMultisegment(Span<T> destination)
    {
        // If we don't have enough to fill the requested buffer, return false
        if (Remaining < destination.Length)
            return false;

        ReadOnlySpan<T> firstSpan = UnreadSpan;
        Debug.Assert(firstSpan.Length < destination.Length);
        firstSpan.CopyTo(destination);
        int copied = firstSpan.Length;

        SequencePosition next = _nextPosition;
        while (Sequence.TryGet(ref next, out ReadOnlyMemory<T> nextSegment, true))
        {
            if (nextSegment.Length > 0)
            {
                ReadOnlySpan<T> nextSpan = nextSegment.Span;
                int toCopy = Math.Min(nextSpan.Length, destination.Length - copied);
                nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied));
                copied += toCopy;
                if (copied >= destination.Length)
                {
                    break;
                }
            }
        }

        return true;
    }
}

public ref partial struct SequenceReader<T> where T : unmanaged, IEquatable<T>
{
    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiter"/>.
    /// </summary>
    /// <param name="span">The read data, if any.</param>
    /// <param name="delimiter">The delimiter to look for.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the <paramref name="delimiter"/> was found.</returns>
    public bool TryReadTo(out ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter = true)
    {
        ReadOnlySpan<T> remaining = UnreadSpan;
        int index = remaining.IndexOf(delimiter);

        if (index != -1)
        {
            span = index == 0 ? default : remaining.Slice(0, index);
            AdvanceCurrentSpan(index + (advancePastDelimiter ? 1 : 0));
            return true;
        }

        return TryReadToSlow(out span, delimiter, advancePastDelimiter);
    }

    private bool TryReadToSlow(out ReadOnlySpan<T> span, T delimiter, bool advancePastDelimiter)
    {
        if (!TryReadToInternal(out ReadOnlySequence<T> sequence, delimiter, advancePastDelimiter, CurrentSpan.Length - CurrentSpanIndex))
        {
            span = default;
            return false;
        }

        span = sequence.IsSingleSegment ? sequence.First.Span : sequence.ToArray();
        return true;
    }

    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiter"/>, ignoring delimiters that are
    /// preceded by <paramref name="delimiterEscape"/>.
    /// </summary>
    /// <param name="span">The read data, if any.</param>
    /// <param name="delimiter">The delimiter to look for.</param>
    /// <param name="delimiterEscape">If found prior to <paramref name="delimiter"/> it will skip that occurrence.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the <paramref name="delimiter"/> was found.</returns>
    public bool TryReadTo(out ReadOnlySpan<T> span, T delimiter, T delimiterEscape, bool advancePastDelimiter = true)
    {
        ReadOnlySpan<T> remaining = UnreadSpan;
        int index = remaining.IndexOf(delimiter);

        if ((index > 0 && !remaining[index - 1].Equals(delimiterEscape)) || index == 0)
        {
            span = remaining.Slice(0, index);
            AdvanceCurrentSpan(index + (advancePastDelimiter ? 1 : 0));
            return true;
        }

        // This delimiter might be skipped, go down the slow path
        return TryReadToSlow(out span, delimiter, delimiterEscape, index, advancePastDelimiter);
    }

    private bool TryReadToSlow(out ReadOnlySpan<T> span, T delimiter, T delimiterEscape, int index, bool advancePastDelimiter)
    {
        if (!TryReadToSlow(out ReadOnlySequence<T> sequence, delimiter, delimiterEscape, index, advancePastDelimiter))
        {
            span = default;
            return false;
        }

        Debug.Assert(sequence.Length > 0);
        span = sequence.IsSingleSegment ? sequence.First.Span : sequence.ToArray();
        return true;
    }

    private bool TryReadToSlow(out ReadOnlySequence<T> sequence, T delimiter, T delimiterEscape, int index, bool advancePastDelimiter)
    {
        SequenceReader<T> copy = this;

        ReadOnlySpan<T> remaining = UnreadSpan;
        bool priorEscape = false;

        do
        {
            if (index >= 0)
            {
                if (index == 0 && priorEscape)
                {
                    // We were in the escaped state, so skip this delimiter
                    priorEscape = false;
                    Advance(index + 1);
                    remaining = UnreadSpan;
                    goto Continue;
                }
                else if (index > 0 && remaining[index - 1].Equals(delimiterEscape))
                {
                    // This delimiter might be skipped

                    // Count our escapes
                    int escapeCount = 1;
                    int i = index - 2;
                    for (; i >= 0; i--)
                    {
                        if (!remaining[i].Equals(delimiterEscape))
                            break;
                    }
                    if (i < 0 && priorEscape)
                    {
                        // Started and ended with escape, increment once more
                        escapeCount++;
                    }
                    escapeCount += index - 2 - i;

                    if ((escapeCount & 1) != 0)
                    {
                        // An odd escape count means we're currently escaped,
                        // skip the delimiter and reset escaped state.
                        Advance(index + 1);
                        priorEscape = false;
                        remaining = UnreadSpan;
                        goto Continue;
                    }
                }

                // Found the delimiter. Move to it, slice, then move past it.
                AdvanceCurrentSpan(index);

                sequence = Sequence.Slice(copy.Position, Position);
                if (advancePastDelimiter)
                {
                    Advance(1);
                }
                return true;
            }
            else
            {
                // No delimiter, need to check the end of the span for odd number of escapes then advance
                if (remaining.Length > 0 && remaining[remaining.Length - 1].Equals(delimiterEscape))
                {
                    int escapeCount = 1;
                    int i = remaining.Length - 2;
                    for (; i >= 0; i--)
                    {
                        if (!remaining[i].Equals(delimiterEscape))
                            break;
                    }

                    escapeCount += remaining.Length - 2 - i;
                    if (i < 0 && priorEscape)
                        priorEscape = (escapeCount & 1) == 0;   // equivalent to incrementing escapeCount before setting priorEscape
                    else
                        priorEscape = (escapeCount & 1) != 0;
                }
                else
                {
                    priorEscape = false;
                }
            }

            // Nothing in the current span, move to the end, checking for the skip delimiter
            AdvanceCurrentSpan(remaining.Length);
            remaining = CurrentSpan;

        Continue:
            index = remaining.IndexOf(delimiter);
        } while (!End);

        // Didn't find anything, reset our original state.
        this = copy;
        sequence = default;
        return false;
    }

    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiter"/>.
    /// </summary>
    /// <param name="sequence">The read data, if any.</param>
    /// <param name="delimiter">The delimiter to look for.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the <paramref name="delimiter"/> was found.</returns>
    public bool TryReadTo(out ReadOnlySequence<T> sequence, T delimiter, bool advancePastDelimiter = true)
    {
        return TryReadToInternal(out sequence, delimiter, advancePastDelimiter);
    }

    private bool TryReadToInternal(out ReadOnlySequence<T> sequence, T delimiter, bool advancePastDelimiter, int skip = 0)
    {
        Debug.Assert(skip >= 0);
        SequenceReader<T> copy = this;
        if (skip > 0)
            Advance(skip);
        ReadOnlySpan<T> remaining = UnreadSpan;

        while (_moreData)
        {
            int index = remaining.IndexOf(delimiter);
            if (index != -1)
            {
                // Found the delimiter. Move to it, slice, then move past it.
                if (index > 0)
                {
                    AdvanceCurrentSpan(index);
                }

                sequence = Sequence.Slice(copy.Position, Position);
                if (advancePastDelimiter)
                {
                    Advance(1);
                }
                return true;
            }

            AdvanceCurrentSpan(remaining.Length);
            remaining = CurrentSpan;
        }

        // Didn't find anything, reset our original state.
        this = copy;
        sequence = default;
        return false;
    }

    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiter"/>, ignoring delimiters that are
    /// preceded by <paramref name="delimiterEscape"/>.
    /// </summary>
    /// <param name="sequence">The read data, if any.</param>
    /// <param name="delimiter">The delimiter to look for.</param>
    /// <param name="delimiterEscape">If found prior to <paramref name="delimiter"/> it will skip that occurrence.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the <paramref name="delimiter"/> was found.</returns>
    public bool TryReadTo(out ReadOnlySequence<T> sequence, T delimiter, T delimiterEscape, bool advancePastDelimiter = true)
    {
        SequenceReader<T> copy = this;

        ReadOnlySpan<T> remaining = UnreadSpan;
        bool priorEscape = false;

        while (_moreData)
        {
            int index = remaining.IndexOf(delimiter);
            if (index != -1)
            {
                if (index == 0 && priorEscape)
                {
                    // We were in the escaped state, so skip this delimiter
                    priorEscape = false;
                    Advance(index + 1);
                    remaining = UnreadSpan;
                    continue;
                }
                else if (index > 0 && remaining[index - 1].Equals(delimiterEscape))
                {
                    // This delimiter might be skipped

                    // Count our escapes
                    int escapeCount = 0;
                    for (int i = index; i > 0 && remaining[i - 1].Equals(delimiterEscape); i--, escapeCount++)
                        ;
                    if (escapeCount == index && priorEscape)
                    {
                        // Started and ended with escape, increment once more
                        escapeCount++;
                    }

                    priorEscape = false;
                    if ((escapeCount & 1) != 0)
                    {
                        // Odd escape count means we're in the escaped state, so skip this delimiter
                        Advance(index + 1);
                        remaining = UnreadSpan;
                        continue;
                    }
                }

                // Found the delimiter. Move to it, slice, then move past it.
                if (index > 0)
                {
                    Advance(index);
                }

                sequence = Sequence.Slice(copy.Position, Position);
                if (advancePastDelimiter)
                {
                    Advance(1);
                }
                return true;
            }

            // No delimiter, need to check the end of the span for odd number of escapes then advance
            {
                int escapeCount = 0;
                for (int i = remaining.Length; i > 0 && remaining[i - 1].Equals(delimiterEscape); i--, escapeCount++)
                    ;
                if (priorEscape && escapeCount == remaining.Length)
                {
                    escapeCount++;
                }
                priorEscape = escapeCount % 2 != 0;
            }

            // Nothing in the current span, move to the end, checking for the skip delimiter
            Advance(remaining.Length);
            remaining = CurrentSpan;
        }

        // Didn't find anything, reset our original state.
        this = copy;
        sequence = default;
        return false;
    }

    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiters"/>.
    /// </summary>
    /// <param name="span">The read data, if any.</param>
    /// <param name="delimiters">The delimiters to look for.</param>
    /// <param name="advancePastDelimiter">True to move past the first found instance of any of the given <paramref name="delimiters"/>.</param>
    /// <returns>True if any of the <paramref name="delimiters"/> were found.</returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TryReadToAny(out ReadOnlySpan<T> span, ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true)
    {
        ReadOnlySpan<T> remaining = UnreadSpan;
        int index = delimiters.Length == 2
            ? remaining.IndexOfAny(delimiters[0], delimiters[1])
            : remaining.IndexOfAny(delimiters);

        if (index != -1)
        {
            span = remaining.Slice(0, index);
            Advance(index + (advancePastDelimiter ? 1 : 0));
            return true;
        }

        return TryReadToAnySlow(out span, delimiters, advancePastDelimiter);
    }

    private bool TryReadToAnySlow(out ReadOnlySpan<T> span, ReadOnlySpan<T> delimiters, bool advancePastDelimiter)
    {
        if (!TryReadToAnyInternal(out ReadOnlySequence<T> sequence, delimiters, advancePastDelimiter, CurrentSpan.Length - CurrentSpanIndex))
        {
            span = default;
            return false;
        }

        span = sequence.IsSingleSegment ? sequence.First.Span : sequence.ToArray();
        return true;
    }

    /// <summary>
    /// Try to read everything up to the given <paramref name="delimiters"/>.
    /// </summary>
    /// <param name="sequence">The read data, if any.</param>
    /// <param name="delimiters">The delimiters to look for.</param>
    /// <param name="advancePastDelimiter">True to move past the first found instance of any of the given <paramref name="delimiters"/>.</param>
    /// <returns>True if any of the <paramref name="delimiters"/> were found.</returns>
    public bool TryReadToAny(out ReadOnlySequence<T> sequence, ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true)
    {
        return TryReadToAnyInternal(out sequence, delimiters, advancePastDelimiter);
    }

    private bool TryReadToAnyInternal(out ReadOnlySequence<T> sequence, ReadOnlySpan<T> delimiters, bool advancePastDelimiter, int skip = 0)
    {
        SequenceReader<T> copy = this;
        if (skip > 0)
            Advance(skip);
        ReadOnlySpan<T> remaining = UnreadSpan;

        while (!End)
        {
            int index = delimiters.Length == 2
                ? remaining.IndexOfAny(delimiters[0], delimiters[1])
                : remaining.IndexOfAny(delimiters);

            if (index != -1)
            {
                // Found one of the delimiters. Move to it, slice, then move past it.
                if (index > 0)
                {
                    AdvanceCurrentSpan(index);
                }

                sequence = Sequence.Slice(copy.Position, Position);
                if (advancePastDelimiter)
                {
                    Advance(1);
                }
                return true;
            }

            Advance(remaining.Length);
            remaining = CurrentSpan;
        }

        // Didn't find anything, reset our original state.
        this = copy;
        sequence = default;
        return false;
    }

    /// <summary>
    /// Try to read data until the entire given <paramref name="delimiter"/> matches.
    /// </summary>
    /// <param name="sequence">The read data, if any.</param>
    /// <param name="delimiter">The multi (T) delimiter.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the <paramref name="delimiter"/> was found.</returns>
    public bool TryReadTo(out ReadOnlySequence<T> sequence, ReadOnlySpan<T> delimiter, bool advancePastDelimiter = true)
    {
        if (delimiter.Length == 0)
        {
            sequence = default;
            return true;
        }

        SequenceReader<T> copy = this;

        bool advanced = false;
        while (!End)
        {
            if (!TryReadTo(out sequence, delimiter[0], advancePastDelimiter: false))
            {
                this = copy;
                return false;
            }

            if (delimiter.Length == 1)
            {
                if (advancePastDelimiter)
                {
                    Advance(1);
                }
                return true;
            }

            if (IsNext(delimiter))
            {
                // Probably a faster way to do this, potentially by avoiding the Advance in the previous TryReadTo call
                if (advanced)
                {
                    sequence = copy.Sequence.Slice(copy.Consumed, Consumed - copy.Consumed);
                }

                if (advancePastDelimiter)
                {
                    Advance(delimiter.Length);
                }
                return true;
            }
            else
            {
                Advance(1);
                advanced = true;
            }
        }

        this = copy;
        sequence = default;
        return false;
    }

    /// <summary>
    /// Advance until the given <paramref name="delimiter"/>, if found.
    /// </summary>
    /// <param name="delimiter">The delimiter to search for.</param>
    /// <param name="advancePastDelimiter">True to move past the <paramref name="delimiter"/> if found.</param>
    /// <returns>True if the given <paramref name="delimiter"/> was found.</returns>
    public bool TryAdvanceTo(T delimiter, bool advancePastDelimiter = true)
    {
        ReadOnlySpan<T> remaining = UnreadSpan;
        int index = remaining.IndexOf(delimiter);
        if (index != -1)
        {
            Advance(advancePastDelimiter ? index + 1 : index);
            return true;
        }

        return TryReadToInternal(out _, delimiter, advancePastDelimiter);
    }

    /// <summary>
    /// Advance until any of the given <paramref name="delimiters"/>, if found.
    /// </summary>
    /// <param name="delimiters">The delimiters to search for.</param>
    /// <param name="advancePastDelimiter">True to move past the first found instance of any of the given <paramref name="delimiters"/>.</param>
    /// <returns>True if any of the given <paramref name="delimiters"/> were found.</returns>
    public bool TryAdvanceToAny(ReadOnlySpan<T> delimiters, bool advancePastDelimiter = true)
    {
        ReadOnlySpan<T> remaining = UnreadSpan;
        int index = remaining.IndexOfAny(delimiters);
        if (index != -1)
        {
            AdvanceCurrentSpan(index + (advancePastDelimiter ? 1 : 0));
            return true;
        }

        return TryReadToAnyInternal(out _, delimiters, advancePastDelimiter);
    }

    /// <summary>
    /// Advance past consecutive instances of the given <paramref name="value"/>.
    /// </summary>
    /// <returns>How many positions the reader has been advanced.</returns>
    public long AdvancePast(T value)
    {
        long start = Consumed;

        do
        {
            // Advance past all matches in the current span
            int i;
            for (i = CurrentSpanIndex; i < CurrentSpan.Length && CurrentSpan[i].Equals(value); i++)
            {
            }

            int advanced = i - CurrentSpanIndex;
            if (advanced == 0)
            {
                // Didn't advance at all in this span, exit.
                break;
            }

            AdvanceCurrentSpan(advanced);

            // If we're at postion 0 after advancing and not at the End,
            // we're in a new span and should continue the loop.
        } while (CurrentSpanIndex == 0 && !End);

        return Consumed - start;
    }

    /// <summary>
    /// Skip consecutive instances of any of the given <paramref name="values"/>.
    /// </summary>
    /// <returns>How many positions the reader has been advanced.</returns>
    public long AdvancePastAny(ReadOnlySpan<T> values)
    {
        long start = Consumed;

        do
        {
            // Advance past all matches in the current span
            int i;
            for (i = CurrentSpanIndex; i < CurrentSpan.Length && values.IndexOf(CurrentSpan[i]) != -1; i++)
            {
            }

            int advanced = i - CurrentSpanIndex;
            if (advanced == 0)
            {
                // Didn't advance at all in this span, exit.
                break;
            }

            AdvanceCurrentSpan(advanced);

            // If we're at postion 0 after advancing and not at the End,
            // we're in a new span and should continue the loop.
        } while (CurrentSpanIndex == 0 && !End);

        return Consumed - start;
    }

    /// <summary>
    /// Advance past consecutive instances of any of the given values.
    /// </summary>
    /// <returns>How many positions the reader has been advanced.</returns>
    public long AdvancePastAny(T value0, T value1, T value2, T value3)
    {
        long start = Consumed;

        do
        {
            // Advance past all matches in the current span
            int i;
            for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
            {
                T value = CurrentSpan[i];
                if (!value.Equals(value0) && !value.Equals(value1) && !value.Equals(value2) && !value.Equals(value3))
                {
                    break;
                }
            }

            int advanced = i - CurrentSpanIndex;
            if (advanced == 0)
            {
                // Didn't advance at all in this span, exit.
                break;
            }

            AdvanceCurrentSpan(advanced);

            // If we're at postion 0 after advancing and not at the End,
            // we're in a new span and should continue the loop.
        } while (CurrentSpanIndex == 0 && !End);

        return Consumed - start;
    }

    /// <summary>
    /// Advance past consecutive instances of any of the given values.
    /// </summary>
    /// <returns>How many positions the reader has been advanced.</returns>
    public long AdvancePastAny(T value0, T value1, T value2)
    {
        long start = Consumed;

        do
        {
            // Advance past all matches in the current span
            int i;
            for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
            {
                T value = CurrentSpan[i];
                if (!value.Equals(value0) && !value.Equals(value1) && !value.Equals(value2))
                {
                    break;
                }
            }

            int advanced = i - CurrentSpanIndex;
            if (advanced == 0)
            {
                // Didn't advance at all in this span, exit.
                break;
            }

            AdvanceCurrentSpan(advanced);

            // If we're at postion 0 after advancing and not at the End,
            // we're in a new span and should continue the loop.
        } while (CurrentSpanIndex == 0 && !End);

        return Consumed - start;
    }

    /// <summary>
    /// Advance past consecutive instances of any of the given values.
    /// </summary>
    /// <returns>How many positions the reader has been advanced.</returns>
    public long AdvancePastAny(T value0, T value1)
    {
        long start = Consumed;

        do
        {
            // Advance past all matches in the current span
            int i;
            for (i = CurrentSpanIndex; i < CurrentSpan.Length; i++)
            {
                T value = CurrentSpan[i];
                if (!value.Equals(value0) && !value.Equals(value1))
                {
                    break;
                }
            }

            int advanced = i - CurrentSpanIndex;
            if (advanced == 0)
            {
                // Didn't advance at all in this span, exit.
                break;
            }

            AdvanceCurrentSpan(advanced);

            // If we're at postion 0 after advancing and not at the End,
            // we're in a new span and should continue the loop.
        } while (CurrentSpanIndex == 0 && !End);

        return Consumed - start;
    }

    /// <summary>
    /// Check to see if the given <paramref name="next"/> value is next.
    /// </summary>
    /// <param name="next">The value to compare the next items to.</param>
    /// <param name="advancePast">Move past the <paramref name="next"/> value if found.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsNext(T next, bool advancePast = false)
    {
        if (End)
            return false;

        if (CurrentSpan[CurrentSpanIndex].Equals(next))
        {
            if (advancePast)
            {
                AdvanceCurrentSpan(1);
            }
            return true;
        }
        return false;
    }

    /// <summary>
    /// Check to see if the given <paramref name="next"/> values are next.
    /// </summary>
    /// <param name="next">The span to compare the next items to.</param>
    /// <param name="advancePast">Move past the <paramref name="next"/> values if found.</param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsNext(ReadOnlySpan<T> next, bool advancePast = false)
    {
        ReadOnlySpan<T> unread = UnreadSpan;
        if (unread.StartsWith(next))
        {
            if (advancePast)
            {
                AdvanceCurrentSpan(next.Length);
            }
            return true;
        }

        // Only check the slow path if there wasn't enough to satisfy next
        return unread.Length < next.Length && IsNextSlow(next, advancePast);
    }

    private unsafe bool IsNextSlow(ReadOnlySpan<T> next, bool advancePast)
    {
        ReadOnlySpan<T> currentSpan = UnreadSpan;

        // We should only come in here if we need more data than we have in our current span
        Debug.Assert(currentSpan.Length < next.Length);

        int fullLength = next.Length;
        SequencePosition nextPosition = _nextPosition;

        while (next.StartsWith(currentSpan))
        {
            if (next.Length == currentSpan.Length)
            {
                // Fully matched
                if (advancePast)
                {
                    Advance(fullLength);
                }
                return true;
            }

            // Need to check the next segment
            while (true)
            {
                if (!Sequence.TryGet(ref nextPosition, out ReadOnlyMemory<T> nextSegment, advance: true))
                {
                    // Nothing left
                    return false;
                }

                if (nextSegment.Length > 0)
                {
                    next = next.Slice(currentSpan.Length);
                    currentSpan = nextSegment.Span;
                    if (currentSpan.Length > next.Length)
                    {
                        currentSpan = currentSpan.Slice(0, next.Length);
                    }
                    break;
                }
            }
        }

        return false;
    }
}

/// <summary>有符号端序读取扩展(Int16 / Int32 / Int64),移植自 BCL 3.1。无符号端序见 <see cref="NewLife.Buffers.SequenceReaderHelper"/></summary>
public static partial class SequenceReaderExtensions
{
    /// <summary>
    /// Try to read the given type out of the buffer if possible. Warning: this is dangerous to use with arbitrary
    /// structs- see remarks for full details.
    /// </summary>
    /// <remarks>
    /// IMPORTANT: The read is a straight copy of bits. If a struct depends on specific state of it's members to
    /// behave correctly this can lead to exceptions, etc. If reading endian specific integers, use the explicit
    /// overloads such as <see cref="TryReadLittleEndian(ref SequenceReader{byte}, out short)"/>
    /// </remarks>
    /// <returns>
    /// True if successful. <paramref name="value"/> will be default if failed (due to lack of space).
    /// </returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal static unsafe bool TryRead<T>(ref this SequenceReader<byte> reader, out T value) where T : unmanaged
    {
        ReadOnlySpan<byte> span = reader.UnreadSpan;
        if (span.Length < sizeof(T))
            return TryReadMultisegment(ref reader, out value);

        value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
        reader.Advance(sizeof(T));
        return true;
    }

    private static unsafe bool TryReadMultisegment<T>(ref SequenceReader<byte> reader, out T value) where T : unmanaged
    {
        Debug.Assert(reader.UnreadSpan.Length < sizeof(T));

        // Not enough data in the current segment, try to peek for the data we need.
        T buffer = default;
        Span<byte> tempSpan = new Span<byte>(&buffer, sizeof(T));

        if (!reader.TryCopyTo(tempSpan))
        {
            value = default;
            return false;
        }

        value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(tempSpan));
        reader.Advance(sizeof(T));
        return true;
    }

    /// <summary>
    /// Reads an <see cref="Int16"/> as little endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int16"/>.</returns>
    public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out short value)
    {
        if (BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    /// <summary>
    /// Reads an <see cref="Int16"/> as big endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int16"/>.</returns>
    public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out short value)
    {
        if (!BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out short value)
    {
        if (reader.TryRead(out value))
        {
            value = BinaryPrimitives.ReverseEndianness(value);
            return true;
        }

        return false;
    }

    /// <summary>
    /// Reads an <see cref="Int32"/> as little endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int32"/>.</returns>
    public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out int value)
    {
        if (BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    /// <summary>
    /// Reads an <see cref="Int32"/> as big endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int32"/>.</returns>
    public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out int value)
    {
        if (!BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out int value)
    {
        if (reader.TryRead(out value))
        {
            value = BinaryPrimitives.ReverseEndianness(value);
            return true;
        }

        return false;
    }

    /// <summary>
    /// Reads an <see cref="Int64"/> as little endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int64"/>.</returns>
    public static bool TryReadLittleEndian(ref this SequenceReader<byte> reader, out long value)
    {
        if (BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    /// <summary>
    /// Reads an <see cref="Int64"/> as big endian.
    /// </summary>
    /// <returns>False if there wasn't enough data for an <see cref="Int64"/>.</returns>
    public static bool TryReadBigEndian(ref this SequenceReader<byte> reader, out long value)
    {
        if (!BitConverter.IsLittleEndian)
        {
            return reader.TryRead(out value);
        }

        return TryReadReverseEndianness(ref reader, out value);
    }

    private static bool TryReadReverseEndianness(ref SequenceReader<byte> reader, out long value)
    {
        if (reader.TryRead(out value))
        {
            value = BinaryPrimitives.ReverseEndianness(value);
            return true;
        }

        return false;
    }
}

#endif