Upgrade Nuget
石头 authored at 2024-11-20 14:01:20
32.26 KiB
X
using System.Buffers;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
using NewLife.Collections;

namespace NewLife.Data;

/// <summary>数据包接口。几乎内存共享理念,统一提供数据包,内部可能是内存池、数组和旧版Packet等多种实现</summary>
/// <remarks>
/// 常用于网络编程和协议解析,为了避免大量内存分配和拷贝,采用数据包对象池,复用内存。
/// 数据包接口一般由结构体实现,提升GC性能。
/// 
/// 特别需要注意内存管理权转移问题,一般由调用栈的上部负责释放内存。
/// Socket非阻塞事件接收时,负责申请与释放内存,数据处理是调用栈下游;
/// Socket阻塞接收时,接收函数内部申请内存,外部使用方释放内存,管理权甚至在此次传递给消息层;
/// 
/// 作为过渡期,旧版Packet也会实现该接口,以便逐步替换。
/// </remarks>
public interface IPacket
{
    /// <summary>数据长度。仅当前数据包,不包括Next</summary>
    Int32 Length { get; }

    /// <summary>下一个链式包</summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    IPacket? Next { get; set; }

    /// <summary>总长度。包括Next链的长度</summary>
    Int32 Total { get; }

    /// <summary>获取/设置 指定位置的字节</summary>
    /// <param name="index"></param>
    /// <returns></returns>
    Byte this[Int32 index] { get; set; }

    /// <summary>获取分片包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    Span<Byte> GetSpan();

    /// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    Memory<Byte> GetMemory();

    /// <summary>切片得到新数据包</summary>
    /// <remarks>引用相同内存块或缓冲区,减少内存分配</remarks>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <returns></returns>
    IPacket Slice(Int32 offset, Int32 count = -1);

    /// <summary>切片得到新数据包,同时转移内存管理权</summary>
    /// <remarks>
    /// 引用相同内存块或缓冲区,减少内存分配。
    /// 如果原数据包只切一次给新包,可以转移内存管理权,由新数据包负责释放;
    /// 如果原数据包需要切多次,不要转移内存管理权,由原数据包负责释放。
    /// </remarks>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <param name="transferOwner">转移所有权。若为true则由新数据包负责归还缓冲区,只能转移一次。并非所有数据包都支持</param>
    /// <returns></returns>
    IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner);

    /// <summary>尝试获取缓冲区</summary>
    /// <param name="segment"></param>
    /// <returns></returns>
    Boolean TryGetArray(out ArraySegment<Byte> segment);
}

/// <summary>拥有管理权的数据包。使用完以后需要释放</summary>
public interface IOwnerPacket : IPacket, IDisposable { }

/// <summary>内存包辅助类</summary>
public static class PacketHelper
{
    /// <summary>附加一个包到当前包链的末尾</summary>
    /// <param name="pk"></param>
    /// <param name="next"></param>
    public static IPacket Append(this IPacket pk, IPacket next)
    {
        if (next == null) return pk;

        var p = pk;
        while (p.Next != null) p = p.Next;
        p.Next = next;

        return pk;
    }

    /// <summary>附加一个包到当前包链的末尾</summary>
    /// <param name="pk"></param>
    /// <param name="next"></param>
    public static IPacket Append(this IPacket pk, Byte[] next) => Append(pk, new ArrayPacket(next));

    /// <summary>转字符串</summary>
    /// <param name="pk"></param>
    /// <param name="encoding"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static String ToStr(this IPacket pk, Encoding? encoding = null, Int32 offset = 0, Int32 count = -1)
    {
        if (pk.Next == null)
        {
            if (count < 0) count = pk.Length - offset;
            var span = pk.GetSpan();
            if (span.Length > count) span = span[..count];

            return span.ToStr(encoding);
        }

        if (count < 0) count = pk.Total - offset;

        var sb = Pool.StringBuilder.Get();
        for (var p = pk; p != null; p = p.Next)
        {
            var span = p.GetSpan();
            if (span.Length > count) span = span[..count];

            sb.Append(span.ToStr(encoding));

            count -= span.Length;
            if (count <= 0) break;
        }
        return sb.Return(true);
    }

    /// <summary>以十六进制编码表示</summary>
    /// <param name="pk"></param>
    /// <param name="maxLength">最大显示多少个字节。默认-1显示全部</param>
    /// <param name="separate">分隔符</param>
    /// <param name="groupSize">分组大小,为0时对每个字节应用分隔符,否则对每个分组使用</param>
    /// <returns></returns>
    public static String ToHex(this IPacket pk, Int32 maxLength = 32, String? separate = null, Int32 groupSize = 0)
    {
        if (pk.Length == 0) return String.Empty;

        if (pk.Next == null)
            return pk.GetSpan().ToHex(separate, groupSize, maxLength);

        var sb = Pool.StringBuilder.Get();
        for (var p = pk; p != null; p = p.Next)
        {
            sb.Append(p.GetSpan().ToHex(separate, groupSize, maxLength));

            maxLength -= p.Length;
            if (maxLength <= 0) break;
        }
        return sb.Return(true);
    }

    /// <summary>拷贝</summary>
    /// <param name="pk"></param>
    /// <param name="stream"></param>
    public static void CopyTo(this IPacket pk, Stream stream)
    {
        for (var p = pk; p != null; p = p.Next)
        {
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
            stream.Write(p.GetSpan());
#else
            if (p is ArrayPacket ap)
                stream.Write(ap.Buffer, ap.Offset, ap.Length);
            else
                stream.Write(p.GetMemory());
#endif
        }
    }

    /// <summary>异步拷贝</summary>
    /// <param name="pk"></param>
    /// <param name="stream"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task CopyToAsync(this IPacket pk, Stream stream, CancellationToken cancellationToken = default)
    {
        for (var p = pk; p != null; p = p.Next)
        {
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
            await stream.WriteAsync(p.GetMemory(), cancellationToken);
#else
            if (p is ArrayPacket ap)
                await stream.WriteAsync(ap.Buffer, ap.Offset, ap.Length, cancellationToken);
            else
                await stream.WriteAsync(p.GetMemory(), cancellationToken);
#endif
        }
    }

    /// <summary>获取数据流</summary>
    /// <param name="pk"></param>
    /// <returns></returns>
    public static Stream GetStream(this IPacket pk)
    {
        if (pk.TryGetArray(out var segment)) return new MemoryStream(segment.Array!, segment.Offset, segment.Count);

        var ms = new MemoryStream();
        pk.CopyTo(ms);
        ms.Position = 0;

        return ms;
    }

    /// <summary>返回数据段</summary>
    /// <returns></returns>
    public static ArraySegment<Byte> ToSegment(this IPacket pk)
    {
        if (pk.TryGetArray(out var segment)) return segment;

        var ms = new MemoryStream();
        pk.CopyTo(ms);
        ms.Position = 0;

        return new ArraySegment<Byte>(ms.Return(true));
    }

    /// <summary>返回数据段集合</summary>
    /// <returns></returns>
    public static IList<ArraySegment<Byte>> ToSegments(this IPacket pk)
    {
        // 初始4元素,优化扩容
        var list = new List<ArraySegment<Byte>>(4);

        for (var p = pk; p != null; p = p.Next)
        {
            if (p is ArrayPacket ap)
                list.Add(new ArraySegment<Byte>(ap.Buffer, ap.Offset, ap.Length));
            else
                list.Add(new ArraySegment<Byte>(p.GetSpan().ToArray(), 0, p.Length));
        }

        return list;
    }

    /// <summary>返回字节数组。无差别复制,一定返回新数组</summary>
    /// <returns></returns>
    public static Byte[] ToArray(this IPacket pk)
    {
        if (pk.Next == null) return pk.GetSpan().ToArray();

        // 链式包输出
        var ms = Pool.MemoryStream.Get();
        pk.CopyTo(ms);

        return ms.Return(true);
    }

    /// <summary>从封包中读取指定数据区,读取全部时直接返回缓冲区,以提升性能</summary>
    /// <param name="pk"></param>
    /// <param name="offset">相对于数据包的起始位置,实际上是数组的Offset+offset</param>
    /// <param name="count">字节个数</param>
    /// <returns></returns>
    public static Byte[] ReadBytes(this IPacket pk, Int32 offset = 0, Int32 count = -1)
    {
        if (pk.Next == null)
        {
            // 读取全部
            if (offset == 0 && count < 0 && pk is ArrayPacket ap)
            {
                if (ap.Offset == 0 && (ap.Length < 0 || ap.Offset + ap.Length == ap.Buffer.Length))
                    return ap.Buffer;
            }

            var span = pk.GetSpan();
            if (count < 0) count = span.Length - offset;
            return span.Slice(offset, count).ToArray();
        }

        return pk.ToArray().ReadBytes(offset, count);
    }

    /// <summary>深度克隆一份数据包,拷贝数据区</summary>
    /// <returns></returns>
    public static IPacket Clone(this IPacket pk)
    {
        if (pk.Next == null)
        {
            // 需要深度拷贝,避免重用缓冲区
            return new ArrayPacket(pk.GetSpan().ToArray());
        }

        // 链式包输出
        var ms = new MemoryStream();
        pk.CopyTo(ms);
        ms.Position = 0;

        return new ArrayPacket(ms);
    }

    /// <summary>尝试获取内存片段。非链式数据包时直接返回</summary>
    /// <param name="pk"></param>
    /// <param name="span"></param>
    /// <returns></returns>
    public static Boolean TryGetSpan(this IPacket pk, out Span<Byte> span)
    {
        if (pk.Next == null)
        {
            span = pk.GetSpan();
            return true;
        }

        span = default;
        return false;
    }

    /// <summary>尝试扩展头部,用于填充包头,减少内存分配</summary>
    /// <param name="pk">数据包</param>
    /// <param name="size">要扩大的头部大小,不包括负载数据</param>
    /// <param name="newPacket">扩展后的数据包</param>
    /// <returns></returns>
    [Obsolete]
    public static Boolean TryExpandHeader(this IPacket pk, Int32 size, [NotNullWhen(true)] out IPacket? newPacket)
    {
        newPacket = null;

        if (pk is ArrayPacket ap && ap.Offset >= size)
        {
            newPacket = new ArrayPacket(ap.Buffer, ap.Offset - size, ap.Length + size) { Next = ap.Next };

            return true;
        }
        else if (pk is OwnerPacket owner && owner.Offset >= size)
        {
            newPacket = new OwnerPacket(owner, size);

            return true;
        }

        return false;
    }

    /// <summary>扩展头部,用于填充包头,减少内存分配</summary>
    /// <param name="pk">数据包</param>
    /// <param name="size">要扩大的头部大小,不包括负载数据</param>
    /// <returns>扩展后的数据包</returns>
    public static IPacket ExpandHeader(this IPacket? pk, Int32 size)
    {
        if (pk is ArrayPacket ap && ap.Offset >= size)
        {
            return new ArrayPacket(ap.Buffer, ap.Offset - size, ap.Length + size) { Next = ap.Next };
        }
        else if (pk is OwnerPacket owner && owner.Offset >= size)
        {
            return new OwnerPacket(owner, size);
        }

        return new OwnerPacket(size) { Next = pk };
    }
}

/// <summary>所有权内存包。具有所有权管理,不再使用时释放</summary>
/// <remarks>
/// 使用时务必明确所有权归属,用完后及时释放。
/// </remarks>
public class OwnerPacket : MemoryManager<Byte>, IPacket, IOwnerPacket
{
    #region 属性
    private Byte[] _buffer;
    /// <summary>缓冲区</summary>
    public Byte[] Buffer => _buffer;

    private Int32 _offset;
    /// <summary>数据偏移</summary>
    public Int32 Offset => _offset;

    private Int32 _length;
    /// <summary>数据长度</summary>
    public Int32 Length => _length;

    /// <summary>获取/设置 指定位置的字节</summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public Byte this[Int32 index]
    {
        get
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                return Next[p];
            }

            return _buffer[_offset + index];
        }
        set
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                Next[p] = value;
            }
            else
            {
                _buffer[_offset + index] = value;
            }
        }
    }

    /// <summary>下一个链式包</summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public IPacket? Next { get; set; }

    /// <summary>总长度</summary>
    public Int32 Total => Length + (Next?.Total ?? 0);

    private Boolean _hasOwner;
    #endregion

    #region 构造
    /// <summary>实例化指定长度的内存包,从共享内存池中借出</summary>
    /// <param name="length">长度</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public OwnerPacket(Int32 length)
    {
        if (length < 0)
            throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative and less than or equal to the memory owner's length.");

        _buffer = ArrayPool<Byte>.Shared.Rent(length);
        _offset = 0;
        _length = length;
        _hasOwner = true;
    }

    /// <summary>实例化内存包,指定内存所有者和长度</summary>
    /// <param name="buffer">缓冲区</param>
    /// <param name="offset"></param>
    /// <param name="length">长度</param>
    /// <param name="hasOwner">是否转移所有权</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public OwnerPacket(Byte[] buffer, Int32 offset, Int32 length, Boolean hasOwner)
    {
        if (offset < 0 || length < 0 || offset + length > buffer.Length)
            throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative and less than or equal to the memory owner's length.");

        _buffer = buffer;
        _offset = offset;
        _length = length;
        _hasOwner = hasOwner;
    }

    /// <summary>从另一个内存包创建新内存包,共用缓冲区</summary>
    /// <param name="owner"></param>
    /// <param name="expandSize"></param>
    public OwnerPacket(OwnerPacket owner, Int32 expandSize)
    {
        _buffer = owner.Buffer;
        _offset = owner.Offset - expandSize;
        _length = owner.Length + expandSize;
        Next = owner.Next;

        // 转移所有权
        _hasOwner = owner._hasOwner;
        owner._hasOwner = false;
    }

    /// <summary>销毁释放</summary>
    /// <param name="disposing"></param>
    protected override void Dispose(Boolean disposing)
    {
        if (!_hasOwner) return;
        _hasOwner = true;

        var buffer = _buffer;
        if (buffer != null)
        {
            // 释放内存所有者以后,直接置空,避免重复使用
            _buffer = null!;

            ArrayPool<Byte>.Shared.Return(buffer);
        }

        Next.TryDispose();
    }
    #endregion

    /// <summary>获取分片包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public override Span<Byte> GetSpan() => new(_buffer, _offset, _length);

    /// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public Memory<Byte> GetMemory() => new(_buffer, _offset, _length);

    /// <summary>重新设置数据包大小。一般用于申请缓冲区并读取数据后设置为实际大小</summary>
    /// <param name="size"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public OwnerPacket Resize(Int32 size)
    {
        if (size < 0) throw new ArgumentNullException(nameof(size));

        if (Next == null)
        {
            if (size > _buffer.Length) throw new ArgumentOutOfRangeException(nameof(size));

            _length = size;
        }
        else
        {
            if (size >= _length) throw new NotSupportedException();

            _length = size;
        }

        return this;
    }

    /// <summary>切片得到新数据包</summary>
    /// <remarks>引用相同内存块,减少内存分配</remarks>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    public IPacket Slice(Int32 offset, Int32 count) => Slice(offset, count, true);

    /// <summary>切片得到新数据包,同时转移内存管理权</summary>
    /// <remarks>引用相同内存块,减少内存分配</remarks>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <param name="transferOwner">转移所有权。若为true则由新数据包负责归还缓冲区,只能转移一次</param>
    public IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner)
    {
        // 释放后无法再次使用
        if (_buffer == null) throw new InvalidDataException();

        var buffer = _buffer;
        var start = _offset + offset;
        var remain = _length - offset;
        var hasOwner = _hasOwner && transferOwner;

        // 超出范围
        if (count > Total - offset) throw new ArgumentOutOfRangeException(nameof(count), "count must be non-negative and less than or equal to the memory owner's length.");

        // 单个数据包
        if (Next == null)
        {
            // 转移管理权
            if (transferOwner) _hasOwner = false;

            if (count < 0 || count > remain) count = remain;
            return new OwnerPacket(buffer, start, count, hasOwner);
        }
        else
        {
            // 如果当前段用完,则取下一段。当前包自己负责释放
            if (remain <= 0) return Next.Slice(offset - _length, count, transferOwner);

            // 转移管理权
            if (transferOwner) _hasOwner = false;

            // 当前包用一截,剩下的全部。转移管理权后,Next随新包一起释放
            if (count < 0) return new OwnerPacket(buffer, start, remain, hasOwner) { Next = Next };

            // 当前包可以读完。转移管理权后,Next失去释放机会
            if (count <= remain) return new OwnerPacket(buffer, start, count, hasOwner);

            // 当前包用一截,剩下的再截取。转移管理权后,Next再次转移管理权,随新包一起释放
            return new OwnerPacket(buffer, start, remain, hasOwner) { Next = Next.Slice(0, count - remain, transferOwner) };
        }
    }

    /// <summary>尝试获取数据段</summary>
    /// <param name="segment"></param>
    /// <returns></returns>
    protected override Boolean TryGetArray(out ArraySegment<Byte> segment)
    {
        segment = new ArraySegment<Byte>(_buffer, _offset, _length);
        return true;
    }

    /// <summary>尝试获取数据段</summary>
    /// <param name="segment"></param>
    /// <returns></returns>
    Boolean IPacket.TryGetArray(out ArraySegment<Byte> segment)
    {
        if (Next == null) return TryGetArray(out segment);

        segment = default;

        return false;
    }

    /// <summary>释放所有权,不再使用</summary>
    public void Free()
    {
        _buffer = null!;
        Next = null;
    }

    /// <summary>钉住内存</summary>
    /// <param name="elementIndex"></param>
    /// <returns></returns>
    /// <exception cref="NotSupportedException"></exception>
    public override MemoryHandle Pin(Int32 elementIndex = 0) => throw new NotSupportedException();

    /// <summary>取消钉内存</summary>
    /// <exception cref="NotImplementedException"></exception>
    public override void Unpin() => throw new NotImplementedException();

    #region 重载运算符
    /// <summary>已重载</summary>
    /// <returns></returns>
    public override String ToString() => $"[{_buffer.Length}]({_offset}, {_length})<{Total}>";
    #endregion
}

/// <summary>内存包</summary>
/// <remarks>
/// 内存包可能来自内存池,失去所有权时已被释放,因此不应该长期持有。
/// </remarks>
public struct MemoryPacket : IPacket
{
    #region 属性
    private readonly Memory<Byte> _memory;
    /// <summary>内存</summary>
    public readonly Memory<Byte> Memory => _memory;

    private readonly Int32 _length;
    /// <summary>数据长度</summary>
    public readonly Int32 Length => _length;

    /// <summary>获取/设置 指定位置的字节</summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public Byte this[Int32 index]
    {
        get
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                return Next[p];
            }

            return _memory.Span[index];
        }
        set
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                Next[p] = value;
            }
            else
            {
                _memory.Span[index] = value;
            }
        }
    }

    /// <summary>下一个链式包</summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public IPacket? Next { get; set; }

    /// <summary>总长度</summary>
    public readonly Int32 Total => Length + (Next?.Total ?? 0);
    #endregion

    /// <summary>实例化内存包,指定内存和长度</summary>
    /// <param name="memory">内存</param>
    /// <param name="length">长度</param>
    /// <exception cref="ArgumentOutOfRangeException"></exception>
    public MemoryPacket(Memory<Byte> memory, Int32 length)
    {
        if (length < 0 || length > memory.Length)
            throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative and less than or equal to the memory owner's length.");

        _memory = memory;
        _length = length;
    }

    /// <summary>获取分片包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public readonly Span<Byte> GetSpan() => _memory.Span[.._length];

    /// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public readonly Memory<Byte> GetMemory() => _memory[.._length];

    /// <summary>切片得到新数据包,共用内存块</summary>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    public IPacket Slice(Int32 offset, Int32 count) => Slice(offset, count, true);

    /// <summary>切片得到新数据包,共用内存块</summary>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <param name="transferOwner">转移所有权。不支持</param>
    public IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner)
    {
        // 带有Next时,不支持Slice
        if (Next != null) throw new NotSupportedException("Slice with Next");

        var remain = _length - offset;
        if (count < 0 || count > remain) count = remain;
        if (offset == 0 && count == _length) return this;

        if (offset == 0)
            return new MemoryPacket(_memory, count);

        return new MemoryPacket(_memory[offset..], count);
    }

    /// <summary>尝试获取缓冲区</summary>
    /// <param name="segment"></param>
    /// <returns></returns>
    public readonly Boolean TryGetArray(out ArraySegment<Byte> segment)
    {
        if (Next == null)
        {
            if (MemoryMarshal.TryGetArray(GetMemory(), out segment)) return true;
        }

        segment = default;

        return false;
    }

    /// <summary>已重载</summary>
    /// <returns></returns>
    public override readonly String ToString() => $"[{_memory.Length}](0, {_length})<{Total}>";
}

/// <summary>字节数组包</summary>
public struct ArrayPacket : IPacket
{
    #region 属性
    private Byte[] _buffer;
    /// <summary>缓冲区</summary>
    public readonly Byte[] Buffer => _buffer;

    private readonly Int32 _offset;
    /// <summary>数据偏移</summary>
    public readonly Int32 Offset => _offset;

    private readonly Int32 _length;
    /// <summary>数据长度</summary>
    public readonly Int32 Length => _length;

    /// <summary>下一个链式包</summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public IPacket? Next { get; set; }

    /// <summary>总长度</summary>
    public readonly Int32 Total => Length + (Next?.Total ?? 0);

    /// <summary>空数组</summary>
    public static ArrayPacket Empty = new([]);
    #endregion

    #region 索引
    /// <summary>获取/设置 指定位置的字节</summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public Byte this[Int32 index]
    {
        get
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                return Next[p];
            }

            return _buffer[_offset + index];
        }
        set
        {
            var p = index - _length;
            if (p >= 0)
            {
                if (Next == null) throw new IndexOutOfRangeException(nameof(index));

                Next[p] = value;
            }
            else
            {
                _buffer[_offset + index] = value;
            }
        }
    }
    #endregion

    #region 构造
    /// <summary>通过指定字节数组来实例化数据包</summary>
    /// <param name="buf"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public ArrayPacket(Byte[] buf, Int32 offset = 0, Int32 count = -1)
    {
        if (count < 0) count = buf.Length - offset;

        _buffer = buf;
        _offset = offset;
        _length = count;
    }

    /// <summary>从可扩展内存流实例化,尝试窃取内存流内部的字节数组,失败后拷贝</summary>
    /// <remarks>因数据包内数组窃取自内存流,需要特别小心,避免多线程共用。常用于内存流转数据包,而内存流不再使用</remarks>
    /// <param name="stream"></param>
    public ArrayPacket(Stream stream)
    {
        if (stream is MemoryStream ms)
        {
#if !NET45
            // 尝试抠了内部存储区,下面代码需要.Net 4.6支持
            if (ms.TryGetBuffer(out var seg))
            {
                if (seg.Array == null) throw new InvalidDataException();

                _buffer = seg.Array;
                _offset = seg.Offset + (Int32)ms.Position;
                _length = seg.Count - (Int32)ms.Position;
                return;
            }
            // GetBuffer窃取内部缓冲区后,无法得知真正的起始位置index,可能导致错误取数
            // public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)

            //try
            //{
            //    Set(ms.GetBuffer(), (Int32)ms.Position, (Int32)(ms.Length - ms.Position));
            //}
            //catch (UnauthorizedAccessException) { }
#endif
        }

        var buf = new Byte[stream.Length - stream.Position];
        var count = stream.Read(buf, 0, buf.Length);
        _buffer = buf;
        _offset = 0;
        _length = count;

        // 必须确保数据流位置不变
        if (count > 0) stream.Seek(-count, SeekOrigin.Current);
    }

    /// <summary>从数据段实例化数据包</summary>
    /// <param name="segment"></param>
    public ArrayPacket(ArraySegment<Byte> segment) : this(segment.Array!, segment.Offset, segment.Count) { }
    #endregion

    /// <summary>获取分片包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public readonly Span<Byte> GetSpan() => new(_buffer, _offset, _length);

    /// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
    /// <returns></returns>
    public readonly Memory<Byte> GetMemory() => new(_buffer, _offset, _length);

    /// <summary>切片得到新数据包,共用缓冲区</summary>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    IPacket IPacket.Slice(Int32 offset, Int32 count) => (this as IPacket).Slice(offset, count, true);

    /// <summary>切片得到新数据包,共用缓冲区</summary>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <param name="transferOwner">转移所有权。仅对Next有效</param>
    IPacket IPacket.Slice(Int32 offset, Int32 count, Boolean transferOwner)
    {
        if (count == 0) return Empty;

        var remain = _length - offset;
        var next = Next;
        if (next != null && remain <= 0) return next.Slice(offset - _length, count, transferOwner);

        return Slice(offset, count, transferOwner);
    }

    /// <summary>切片得到新数据包,共用缓冲区,无内存分配</summary>
    /// <param name="offset">偏移</param>
    /// <param name="count">个数。默认-1表示到末尾</param>
    /// <param name="transferOwner">转移所有权。仅对Next有效</param>
    public ArrayPacket Slice(Int32 offset, Int32 count = -1, Boolean transferOwner = false)
    {
        if (count == 0) return Empty;

        var start = Offset + offset;
        var remain = _length - offset;

        var next = Next;
        if (next == null)
        {
            // count 是 offset 之后的个数
            if (count < 0 || count > remain) count = remain;
            if (count < 0) count = 0;

            return new ArrayPacket(_buffer, start, count);
        }
        else
        {
            // 如果当前段用完,则取下一段。强转ArrayPacket,如果不是则抛出异常
            if (remain <= 0)
                return (ArrayPacket)next.Slice(offset - _length, count, transferOwner);

            // 当前包用一截,剩下的全部
            if (count < 0)
                return new ArrayPacket(_buffer, start, remain) { Next = next };

            // 当前包可以读完
            if (count <= remain)
                return new ArrayPacket(_buffer, start, count);

            // 当前包用一截,剩下的再截取
            return new ArrayPacket(_buffer, start, remain) { Next = next.Slice(0, count - remain, transferOwner) };
        }
    }

    /// <summary>尝试获取缓冲区</summary>
    /// <param name="segment"></param>
    /// <returns></returns>
    public readonly Boolean TryGetArray(out ArraySegment<Byte> segment)
    {
        if (Next == null)
        {
            segment = new ArraySegment<Byte>(_buffer, _offset, _length);
            return true;
        }

        segment = default;

        return false;
    }

    #region 重载运算符
    /// <summary>重载类型转换,字节数组直接转为Packet对象</summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static implicit operator ArrayPacket(Byte[] value) => new(value);

    /// <summary>重载类型转换,一维数组直接转为Packet对象</summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static implicit operator ArrayPacket(ArraySegment<Byte> value) => new(value.Array!, value.Offset, value.Count);

    /// <summary>重载类型转换,字符串直接转为Packet对象</summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static implicit operator ArrayPacket(String value) => new(value.GetBytes());

    /// <summary>已重载</summary>
    /// <returns></returns>
    public override String ToString() => $"[{_buffer.Length}]({_offset}, {_length})<{Total}>";
    #endregion
}