using System.Buffers;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using NewLife.Collections;
namespace NewLife.Data;
/// <summary>数据包接口。基于内存共享理念,统一提供数据包处理能力</summary>
/// <remarks>
/// <para>常用于网络编程和协议解析,通过对象池复用内存避免大量分配和拷贝。</para>
/// <para>数据包接口一般由结构体实现以降低 GC 压力。</para>
/// <para><b>内存管理权转移规则</b>:调用栈上层(获得包的一方)负责最终释放。</para>
/// <list type="bullet">
/// <item>非阻塞 Socket:接收方申请与释放;解析逻辑只消费不负责释放</item>
/// <item>阻塞 Socket:接收函数申请,外部使用方释放,管理权可进一步传递</item>
/// </list>
/// <para>切片 <see cref="Slice(Int32, Int32)"/> 默认共享底层缓冲区,必要时可指定是否转移所有权。</para>
/// <para><b>重要</b>:所有临时获得的 <see cref="Span{T}"/>/<see cref="Memory{T}"/> 仅在当前所有权生命周期内短暂使用,禁止缓存到异步/长期结构中。</para>
/// </remarks>
public interface IPacket
{
/// <summary>数据长度。仅当前数据包,不包括 <see cref="Next"/></summary>
Int32 Length { get; }
/// <summary>下一个链式包</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
IPacket? Next { get; set; }
/// <summary>总长度。包括 <see cref="Next"/> 链的长度</summary>
Int32 Total { get; }
/// <summary>获取/设置 指定绝对位置的字节(跨越链式包)</summary>
/// <param name="index">0 基起始的全局位置</param>
Byte this[Int32 index] { get; set; }
/// <summary>获取分片视图(仅当前数据包,不包括 <see cref="Next"/> 链)。在管理权生命周期内短暂使用,禁止长期保存</summary>
Span<Byte> GetSpan();
/// <summary>获取内存块(仅当前数据包,不包括 <see cref="Next"/> 链)。在管理权生命周期内短暂使用,禁止长期保存</summary>
Memory<Byte> GetMemory();
/// <summary>切片得到新数据包,共享底层内存以减少分配</summary>
/// <param name="offset">相对当前包起始偏移</param>
/// <param name="count">个数。默认 -1 表示到末尾</param>
IPacket Slice(Int32 offset, Int32 count = -1);
/// <summary>切片得到新数据包,可选择转移内存管理权</summary>
/// <remarks>若 <paramref name="transferOwner"/> 为 true,表示新包负责归还缓冲区(仅支持一次转移);多次切分同一来源时不要转移。</remarks>
/// <param name="offset">相对当前包起始偏移</param>
/// <param name="count">个数。默认 -1 表示到末尾</param>
/// <param name="transferOwner">是否转移所有权(实现可能不支持)</param>
IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner);
/// <summary>尝试获取当前片段的 <see cref="ArraySegment{T}"/>(不含链式后续)</summary>
Boolean TryGetArray(out ArraySegment<Byte> segment);
}
/// <summary>拥有管理权的数据包。使用完以后需要释放</summary>
public interface IOwnerPacket : IPacket, IDisposable;
/// <summary>数据包辅助扩展方法</summary>
/// <remarks>
/// <para>提供数据包链式操作、数据转换、流处理等核心功能。</para>
/// <para><b>设计原则</b>:</para>
/// <list type="number">
/// <item>性能优先:单包快速路径,多包链式处理</item>
/// <item>内存友好:复用缓冲区,减少分配</item>
/// <item>安全防护:环检测,边界校验</item>
/// <item>兼容扩展:支持 null 调用,便于链式编程</item>
/// </list>
/// </remarks>
public static class PacketHelper
{
#region 快捷转换
/// <summary>将字节数组包装为数据包</summary>
/// <param name="data">字节数组</param>
/// <returns>包装后的数据包</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ArrayPacket AsPacket(this Byte[] data) => new(data);
/// <summary>将字节数组的指定区域包装为数据包</summary>
/// <param name="data">字节数组</param>
/// <param name="offset">起始偏移</param>
/// <param name="count">数据长度,-1 表示到末尾</param>
/// <returns>包装后的数据包</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ArrayPacket AsPacket(this Byte[] data, Int32 offset, Int32 count = -1) => new(data, offset, count);
/// <summary>将数组段包装为数据包</summary>
/// <param name="segment">数组段</param>
/// <returns>包装后的数据包</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ArrayPacket AsPacket(this ArraySegment<Byte> segment) => new(segment);
#endregion
#region 链式操作
/// <summary>将数据包追加到当前包链末尾</summary>
/// <param name="pk">当前包链头节点</param>
/// <param name="next">要追加的数据包(可包含自身链)</param>
/// <returns>原包链头节点,便于链式调用</returns>
/// <remarks>
/// <list type="bullet">
/// <item>时间复杂度:O(n),n 为当前链长度</item>
/// <item>防护机制:自引用检测、环路检测</item>
/// <item>若 next 已包含链,会整体挂接</item>
/// </list>
/// </remarks>
public static IPacket Append(this IPacket pk, IPacket next)
{
if (next == null) return pk;
if (ReferenceEquals(pk, next)) return pk; // 防止自连接
// 遍历到链尾
var current = pk;
while (current.Next != null)
{
// 环检测:避免形成循环链表
if (ReferenceEquals(current.Next, pk)) break;
current = current.Next;
}
current.Next = next;
return pk;
}
/// <summary>将字节数组作为新包追加到末尾</summary>
/// <param name="pk">当前包链头节点</param>
/// <param name="data">字节数组数据</param>
/// <returns>原包链头节点,便于链式调用</returns>
public static IPacket Append(this IPacket pk, Byte[] data) => Append(pk, new ArrayPacket(data));
#endregion
#region 数据转换
/// <summary>转换为字符串</summary>
/// <param name="pk">数据包(允许 null)</param>
/// <param name="encoding">字符编码,null 表示 UTF8</param>
/// <param name="offset">起始偏移量(跨链全局)</param>
/// <param name="count">读取字节数,-1 表示到末尾</param>
/// <returns>转换后的字符串,pk 为 null 时返回 null</returns>
/// <remarks>
/// <para><b>性能优化策略</b>:</para>
/// <list type="number">
/// <item>单包:直接 Span 切片 + 编码,零分配</item>
/// <item>多包链:StringBuilder 池化,按段拼接</item>
/// <item>参数规范化:负偏移归零,超界截断</item>
/// </list>
/// </remarks>
public static String ToStr(this IPacket pk, Encoding? encoding = null, Int32 offset = 0, Int32 count = -1)
{
// 兼容 null 扩展调用
if (pk == null) return null!;
// 参数规范化
if (offset < 0) offset = 0;
if (count == 0) return String.Empty;
var total = pk.Total;
if (total == 0 || offset >= total) return String.Empty;
// 单包快速路径(热点优化)
if (pk.Next == null)
{
var length = pk.Length;
if (offset >= length) return String.Empty;
var actualCount = count < 0 || count > length - offset ? length - offset : count;
return pk.GetSpan().Slice(offset, actualCount).ToStr(encoding);
}
// 多包链处理
var finalCount = count < 0 || count > total - offset ? total - offset : count;
if (finalCount <= 0) return String.Empty;
return ProcessMultiPacketString(pk, offset, finalCount, encoding);
}
/// <summary>处理多包链的字符串转换</summary>
private static String ProcessMultiPacketString(IPacket pk, Int32 offset, Int32 count, Encoding? encoding)
{
var skip = offset;
var remain = count;
// 预分配容量:UTF-8 平均每字节约 1 个字符,避免 StringBuilder 扩容
var sb = Pool.StringBuilder.Get();
sb.EnsureCapacity(count);
for (var current = pk; current != null && remain > 0; current = current.Next)
{
var span = current.GetSpan();
// 跳过当前段
if (skip >= span.Length)
{
skip -= span.Length;
continue;
}
// 进入有效数据区
if (skip > 0)
{
span = span[skip..];
skip = 0;
}
// 限制读取长度
if (span.Length > remain)
span = span[..remain];
sb.Append(span.ToStr(encoding));
remain -= span.Length;
}
return sb.Return(true);
}
/// <summary>转换为十六进制字符串</summary>
/// <param name="pk">数据包</param>
/// <param name="maxLength">最大显示字节数,默认 32,-1 显示全部</param>
/// <param name="separator">分隔符,null/空表示不分隔</param>
/// <param name="groupSize">分组大小,0 表示每字节分隔,负数等同于 0</param>
/// <returns>十六进制字符串表示</returns>
/// <remarks>
/// <list type="bullet">
/// <item>基于 Total 判空,避免首段为空时误判</item>
/// <item>多包处理:保持全局字节计数,确保分隔符在跨段时连续正确</item>
/// </list>
/// </remarks>
public static String ToHex(this IPacket pk, Int32 maxLength = 32, String? separator = null, Int32 groupSize = 0)
{
if (pk == null) return null!;
var total = pk.Total;
if (total == 0 || maxLength == 0) return String.Empty;
if (groupSize < 0) groupSize = 0;
// 单包快速路径
if (pk.Next == null)
return pk.GetSpan().ToHex(separator, groupSize, maxLength);
// 多包链处理
return ProcessMultiPacketHex(pk, maxLength, separator, groupSize);
}
/// <summary>处理多包链的十六进制转换</summary>
private static String ProcessMultiPacketHex(IPacket pk, Int32 maxLength, String? separator, Int32 groupSize)
{
var sb = Pool.StringBuilder.Get();
const String HexDigits = "0123456789ABCDEF";
var writtenBytes = 0;
for (var current = pk; current != null; current = current.Next)
{
var span = current.GetSpan();
for (var i = 0; i < span.Length && (maxLength < 0 || writtenBytes < maxLength); i++)
{
// 添加分隔符(非首字节且分隔符非空)
if (writtenBytes > 0 && !separator.IsNullOrEmpty())
{
if (groupSize <= 0 || writtenBytes % groupSize == 0)
sb.Append(separator);
}
// 转换字节为十六进制
var b = span[i];
sb.Append(HexDigits[b >> 4]);
sb.Append(HexDigits[b & 0x0F]);
writtenBytes++;
}
// 提前结束检查
if (maxLength >= 0 && writtenBytes >= maxLength) break;
}
return sb.Return(true);
}
/// <summary>将数据包内容以文本形式流式写入 TextWriter,避免构建完整中间字符串</summary>
/// <param name="pk">源数据包</param>
/// <param name="writer">目标文本写入器</param>
/// <param name="encoding">字符编码,null 表示 UTF8</param>
/// <remarks>
/// <para>适用于大数据包的文本输出场景(如日志、调试),避免 ToStr 产生的大量临时 String 分配。</para>
/// <para>按段逐个解码写入,内存开销仅为单段大小而非总数据量。</para>
/// </remarks>
public static void WriteTo(this IPacket pk, TextWriter writer, Encoding? encoding = null)
{
if (pk == null || writer == null) return;
encoding ??= Encoding.UTF8;
#if NETCOREAPP || NETSTANDARD2_1
// 栈缓冲区在循环外分配一次,避免每次迭代累积栈空间
const Int32 MaxStackAllocChars = 1024;
Span<Char> stackChars = stackalloc Char[MaxStackAllocChars];
#endif
for (var current = pk; current != null; current = current.Next)
{
var span = current.GetSpan();
if (span.Length == 0) continue;
#if NETCOREAPP || NETSTANDARD2_1
var charCount = encoding.GetCharCount(span);
if (charCount <= MaxStackAllocChars)
{
var written = encoding.GetChars(span, stackChars);
writer.Write(stackChars[..written]);
}
else
{
// 大段使用池化缓冲区
var chars = ArrayPool<Char>.Shared.Rent(charCount);
try
{
var written = encoding.GetChars(span, chars);
writer.Write(chars, 0, written);
}
finally
{
ArrayPool<Char>.Shared.Return(chars);
}
}
#else
// .NET Framework 回退路径
if (current.TryGetArray(out var segment))
writer.Write(encoding.GetChars(segment.Array!, segment.Offset, segment.Count));
else
writer.Write(encoding.GetString(span.ToArray()));
#endif
}
}
#endregion
#region 流操作
/// <summary>将数据包内容复制到流</summary>
/// <param name="pk">源数据包</param>
/// <param name="stream">目标流</param>
/// <remarks>在 .NET Framework 中可能存在二次拷贝</remarks>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> 为 null</exception>
public static void CopyTo(this IPacket pk, Stream stream)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
for (var current = pk; current != null; current = current.Next)
{
if (current.TryGetArray(out var segment))
stream.Write(segment.Array!, segment.Offset, segment.Count);
else
stream.Write(current.GetMemory());
}
}
/// <summary>异步将数据包内容复制到流</summary>
/// <param name="pk">源数据包</param>
/// <param name="stream">目标流</param>
/// <param name="cancellationToken">取消令牌</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> 为 null</exception>
public static async Task CopyToAsync(this IPacket pk, Stream stream, CancellationToken cancellationToken = default)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
for (var current = pk; current != null; current = current.Next)
{
if (current.TryGetArray(out var segment))
await stream.WriteAsync(segment.Array!, segment.Offset, segment.Count, cancellationToken).ConfigureAwait(false);
else
await stream.WriteAsync(current.GetMemory(), cancellationToken).ConfigureAwait(false);
}
}
/// <summary>获取包含数据包内容的内存流</summary>
/// <param name="pk">源数据包</param>
/// <returns>可读写的内存流,位置已重置为 0</returns>
public static Stream GetStream(this IPacket pk) => GetStream(pk, true);
/// <summary>获取包含数据包内容的内存流</summary>
/// <param name="pk">源数据包</param>
/// <param name="writable">是否可写</param>
/// <returns>可读写的内存流,位置已重置为 0</returns>
public static Stream GetStream(this IPacket pk, Boolean writable)
{
if (pk.Next == null)
{
// 独立包且可获取数组段时直接返回内存流
if (pk.TryGetArray(out var segment))
return new MemoryStream(segment.Array!, segment.Offset, segment.Count, writable);
}
var ms = new MemoryStream(pk.Total);
pk.CopyTo(ms);
ms.Position = 0;
return ms;
}
#endregion
#region 数据段操作
/// <summary>转换为数组段,多包时进行聚合复制</summary>
/// <param name="pk">源数据包</param>
/// <returns>数组段,单包时直接返回,多包时新建聚合数组</returns>
public static ArraySegment<Byte> ToSegment(this IPacket pk)
{
// 单包且可获取数组段时直接返回
if (pk.Next == null && pk.TryGetArray(out var segment))
return segment;
// 多包直接分配目标数组 + Span 拷贝,避免 MemoryStream 开销
var buf = new Byte[pk.Total];
var pos = 0;
for (var current = pk; current != null; current = current.Next)
{
var span = current.GetSpan();
span.CopyTo(buf.AsSpan(pos));
pos += span.Length;
}
return new ArraySegment<Byte>(buf, 0, pos);
}
/// <summary>转换为数组段集合,每个元素对应链上一个包片段</summary>
/// <param name="pk">源数据包</param>
/// <returns>数组段列表,保持原始分段结构</returns>
/// <remarks>不进行展开聚合,保持链式结构的分段信息</remarks>
public static IList<ArraySegment<Byte>> ToSegments(this IPacket pk)
{
var segments = new List<ArraySegment<Byte>>(4); // 预分配 4 个元素优化扩容
for (var current = pk; current != null; current = current.Next)
{
if (current.TryGetArray(out var segment))
segments.Add(segment);
else
segments.Add(new ArraySegment<Byte>(current.GetSpan().ToArray(), 0, current.Length));
}
return segments;
}
/// <summary>转换为字节数组,始终返回新数组副本</summary>
/// <param name="pk">源数据包</param>
/// <returns>包含所有数据的新字节数组</returns>
public static Byte[] ToArray(this IPacket pk)
{
// 单包直接转数组
if (pk.Next == null)
return pk.GetSpan().ToArray();
// 多包直接分配目标数组 + Span 拷贝,避免 MemoryStream 开销
var buf = new Byte[pk.Total];
var pos = 0;
for (var current = pk; current != null; current = current.Next)
{
var span = current.GetSpan();
span.CopyTo(buf.AsSpan(pos));
pos += span.Length;
}
return buf;
}
#endregion
#region 数据读取
/// <summary>读取指定范围的字节数据</summary>
/// <param name="pk">源数据包</param>
/// <param name="offset">相对起始偏移量</param>
/// <param name="count">读取字节数,-1 表示到末尾</param>
/// <returns>读取的字节数组,可能直接返回底层数组以优化性能</returns>
/// <remarks>性能优化:读取全部数据且满足条件时,直接返回底层数组避免复制</remarks>
public static Byte[] ReadBytes(this IPacket pk, Int32 offset = 0, Int32 count = -1)
{
if (pk.Next == null)
{
if (count < 0) count = pk.Length - offset;
if (pk.TryGetArray(out var segment))
{
// 性能优化:读取全部且数组段完整时直接返回
if (offset == 0 && count == pk.Length &&
segment.Offset == 0 && segment.Count == segment.Array!.Length)
return segment.Array;
return segment.Array!.ReadBytes(segment.Offset + offset, count);
}
return pk.GetSpan().Slice(offset, count).ToArray();
}
// 多包链:直接跨段拷贝目标范围,避免先 ToArray 再截取的双重分配
var total = pk.Total;
if (count < 0) count = total - offset;
if (offset + count > total) count = total - offset;
if (count <= 0) return [];
var buf = new Byte[count];
var skip = offset;
var remaining = count;
var pos = 0;
for (var current = pk; current != null && remaining > 0; current = current.Next)
{
var span = current.GetSpan();
// 跳过当前段
if (skip >= span.Length)
{
skip -= span.Length;
continue;
}
// 进入有效数据区
if (skip > 0)
{
span = span[skip..];
skip = 0;
}
// 拷贝所需字节数
var toCopy = Math.Min(span.Length, remaining);
span[..toCopy].CopyTo(buf.AsSpan(pos));
pos += toCopy;
remaining -= toCopy;
}
return buf;
}
/// <summary>深度克隆数据包,完全复制数据内容</summary>
/// <param name="pk">源数据包</param>
/// <returns>独立的数据包副本,内存来自池,实际类型为 <see cref="IOwnerPacket"/>,调用方负责 Dispose</returns>
public static IPacket Clone(this IPacket pk)
{
var total = pk.Total;
var owner = new OwnerPacket(total);
var dest = owner.GetSpan();
var pos = 0;
for (var current = pk; current != null; current = current.Next)
{
var span = current.GetSpan();
span.CopyTo(dest[pos..]);
pos += span.Length;
}
return owner;
}
#endregion
#region 内存访问
/// <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;
}
#endregion
#region 头部扩展
/// <summary>尝试扩展头部空间,用于填充协议头等场景</summary>
/// <param name="pk">原数据包</param>
/// <param name="size">需要扩展的头部字节数</param>
/// <param name="newPacket">扩展后的新数据包</param>
/// <returns>是否成功扩展</returns>
/// <remarks>
/// <para>已过时,请使用 <see cref="ExpandHeader"/> 方法。</para>
/// <para>该方法仅在原包有足够前置空间时成功,否则返回 false。</para>
/// </remarks>
[Obsolete("请改用 ExpandHeader,并确保根据返回结果继续使用新实例。")]
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">原数据包,可为 null</param>
/// <param name="size">需要扩展的头部字节数</param>
/// <returns>扩展后的数据包,可能复用原缓冲区或创建新缓冲区</returns>
/// <remarks>
/// <para><b>扩展策略</b>:</para>
/// <list type="number">
/// <item>ArrayPacket/OwnerPacket 有足够前置空间时,直接扩展</item>
/// <item>否则创建新的 OwnerPacket,原包作为后继链节点</item>
/// </list>
/// </remarks>
public static IPacket ExpandHeader(this IPacket? pk, Int32 size)
{
return pk switch
{
ArrayPacket ap when ap.Offset >= size =>
new ArrayPacket(ap.Buffer, ap.Offset - size, ap.Length + size) { Next = ap.Next },
OwnerPacket owner when owner.Offset >= size =>
new OwnerPacket(owner, size),
_ => new OwnerPacket(size) { Next = pk }
};
}
#endregion
}
/// <summary>所有权内存包。基于 ArrayPool 的高性能内存管理,支持链式结构与所有权转移</summary>
/// <remarks>
/// <para><b>核心特性</b>:</para>
/// <list type="bullet">
/// <item>内存池复用:使用 <see cref="ArrayPool{T}.Shared"/> 减少 GC 压力</item>
/// <item>所有权转移:切片操作可选择转移内存管理责任(仅一次)</item>
/// <item>链式结构:支持多段数据包连接,透明处理跨段访问</item>
/// <item>零拷贝切片:共享底层缓冲区,避免不必要的内存分配</item>
/// </list>
/// <para><b>生命周期管理</b>:必须调用 <see cref="Dispose"/> 归还池化内存,或通过所有权转移由新实例负责释放。</para>
/// <para><b>设计决策</b>:</para>
/// <list type="bullet">
/// <item><b>必须为 class</b>:所有权语义依赖引用同一性。struct 赋值产生值拷贝会导致 double-free(Slice 转移所有权时修改的是副本而非原始实例),
/// 且 IDisposable + struct 在装箱场景下无法正确释放资源。</item>
/// <item><b>sealed 密封</b>:无派生需求,JIT 可对 GetSpan/GetMemory 等热路径方法去虚拟化并内联,显著提升协议解析性能。</item>
/// <item><b>不继承 MemoryManager<T></b>:仅需 IPacket + IDisposable,MemoryManager 的 Pin/Unpin/IMemoryOwner.Memory 均未使用,
/// 移除后消除死代码和多余 vtable 开销。</item>
/// </list>
/// </remarks>
public sealed class OwnerPacket : IPacket, IOwnerPacket
{
#region 字段与属性
private Byte[]? _buffer;
private Int32 _offset;
private Int32 _length;
private Boolean _hasOwner;
/// <summary>缓冲区数组</summary>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
public Byte[] Buffer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _buffer ?? throw new ObjectDisposedException(nameof(OwnerPacket));
}
/// <summary>数据在缓冲区中的起始偏移量</summary>
public Int32 Offset
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _offset;
}
/// <summary>当前数据包的有效数据长度</summary>
public Int32 Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _length;
}
/// <summary>下一个链式数据包节点</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IPacket? Next { get; set; }
/// <summary>包含链式结构的总数据长度</summary>
public Int32 Total
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _length + (Next?.Total ?? 0);
}
/// <summary>获取或设置指定位置的字节值,支持跨链式包访问</summary>
/// <param name="index">从当前包起始的相对索引位置</param>
/// <returns>指定位置的字节值</returns>
/// <exception cref="IndexOutOfRangeException">索引超出有效范围</exception>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
public Byte this[Int32 index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => index switch
{
< 0 => throw new IndexOutOfRangeException($"Index cannot be negative: {index}"),
var i when i < _length => Buffer[_offset + i],
var i when Next != null => Next[i - _length],
_ => throw new IndexOutOfRangeException($"Index {index} exceeds total length {Total}")
};
set
{
switch (index)
{
case < 0:
throw new IndexOutOfRangeException($"Index cannot be negative: {index}");
case var i when i < _length:
Buffer[_offset + i] = value;
break;
case var i when Next != null:
Next[i - _length] = value;
break;
default:
throw new IndexOutOfRangeException($"Index {index} exceeds total length {Total}");
}
}
}
#endregion
#region 构造函数
/// <summary>创建指定长度的内存包,从共享内存池借用缓冲区</summary>
/// <param name="length">所需缓冲区长度</param>
/// <exception cref="ArgumentOutOfRangeException">长度为负数</exception>
/// <remarks>实际分配的缓冲区可能大于请求长度,以适配内存池的分片策略</remarks>
public OwnerPacket(Int32 length)
{
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative.");
_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="ArgumentNullException">缓冲区为 null</exception>
/// <exception cref="ArgumentOutOfRangeException">偏移量或长度超出缓冲区范围</exception>
public OwnerPacket(Byte[] buffer, Int32 offset, Int32 length, Boolean hasOwner)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be non-negative.");
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative.");
if (offset + length > buffer.Length)
throw new ArgumentOutOfRangeException(nameof(length),
"Offset and length must be within buffer bounds.");
_buffer = buffer;
_offset = offset;
_length = length;
_hasOwner = hasOwner;
}
/// <summary>基于现有实例创建扩展头部的新内存包,转移所有权</summary>
/// <param name="owner">源内存包实例</param>
/// <param name="expandSize">向前扩展的字节数</param>
/// <exception cref="ArgumentNullException">源实例为 null</exception>
/// <exception cref="ArgumentOutOfRangeException">扩展大小超出可用前置空间</exception>
/// <remarks>
/// <para>新实例接管源实例的所有权和链式结构,源实例失去管理权限。</para>
/// <para>要求源实例的 Offset 不小于 expandSize,否则无法向前扩展。</para>
/// </remarks>
public OwnerPacket(OwnerPacket owner, Int32 expandSize)
{
if (owner == null) throw new ArgumentNullException(nameof(owner));
if (expandSize < 0)
throw new ArgumentOutOfRangeException(nameof(expandSize), "Expand size must be non-negative.");
if (owner._offset < expandSize)
throw new ArgumentOutOfRangeException(nameof(expandSize),
$"Expand size {expandSize} exceeds available front space {owner._offset}");
_buffer = owner._buffer;
_offset = owner._offset - expandSize;
_length = owner._length + expandSize;
Next = owner.Next;
// 转移所有权:新实例接管,原实例失权
_hasOwner = owner._hasOwner;
owner._hasOwner = false;
}
/// <summary>从数据流创建内存包,优先窃取MemoryStream内部缓冲区,否则从池借用并拷贝数据</summary>
/// <remarks>
/// 窃取成功时不拥有缓冲区,Dispose不归还;窃取失败时从池借用,Dispose自动归还。
/// 若指定预留空间,数据从 offset = reserve 位置开始存放,有效长度为 reserve + data_size。
/// 构造完成后数据流位置不变。
/// </remarks>
/// <param name="stream">数据流</param>
/// <param name="reserve">前置预留字节数,默认0</param>
public OwnerPacket(Stream stream, Int32 reserve = 0)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
if (reserve < 0) throw new ArgumentOutOfRangeException(nameof(reserve), "Reserve must be non-negative.");
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;
_hasOwner = false;
return;
}
#endif
}
// 从池里借字节数组,存放数据流拷贝出来的数据
var size = (Int32)(stream.Length - stream.Position);
_buffer = ArrayPool<Byte>.Shared.Rent(reserve + size);
var count = stream.Read(_buffer, reserve, size);
_offset = 0;
_length = count;
_hasOwner = true;
// 确保数据流位置不变
if (count > 0) stream.Seek(-count, SeekOrigin.Current);
}
#endregion
#region 内存管理
/// <summary>释放资源,归还池化缓冲区并清理链式结构</summary>
public void Dispose()
{
if (!_hasOwner) return;
_hasOwner = false;
var buffer = _buffer;
_buffer = null; // 防止重复使用已释放的缓冲区
if (buffer != null)
ArrayPool<Byte>.Shared.Return(buffer);
// 安全释放链式后续节点
Next.TryDispose();
Next = null;
}
/// <summary>立即放弃所有权,不归还缓冲区</summary>
/// <remarks>
/// <para>用于特殊场景下的所有权转移,调用后实例变为无效状态。</para>
/// <para>警告:缓冲区不会被归还到池中,可能导致内存泄漏。</para>
/// </remarks>
public void Free()
{
_buffer = null;
Next = null;
_hasOwner = false;
}
#endregion
#region 内存访问
/// <summary>获取当前数据包的内存片段视图(仅本段,不含 Next)</summary>
/// <returns>只读内存片段,仅在实例生命周期内有效</returns>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<Byte> GetSpan() => new(Buffer, _offset, _length);
/// <summary>获取当前数据包的内存块(仅本段,不含 Next)</summary>
/// <returns>内存块,仅在实例生命周期内有效</returns>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Memory<Byte> GetMemory() => new(Buffer, _offset, _length);
/// <summary>尝试获取当前片段的数组段表示(仅本段,不含 Next)</summary>
/// <param name="segment">输出的数组段</param>
/// <returns>始终返回 true</returns>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Boolean TryGetArray(out ArraySegment<Byte> segment)
{
segment = new ArraySegment<Byte>(Buffer, _offset, _length);
return true;
}
#endregion
#region 大小调整
/// <summary>调整数据包的有效长度</summary>
/// <param name="size">新的数据长度</param>
/// <returns>当前实例,支持链式调用</returns>
/// <exception cref="ArgumentOutOfRangeException">大小为负数或超出缓冲区容量</exception>
/// <exception cref="NotSupportedException">存在链式后续节点且尝试增大长度</exception>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
/// <remarks>
/// <para>主要用于从缓冲区读取数据后,根据实际读取量调整有效长度。</para>
/// <para>当存在 Next 节点时,仅允许减小当前段长度。</para>
/// </remarks>
public OwnerPacket Resize(Int32 size)
{
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), "Size must be non-negative.");
if (Next == null)
{
if (size > Buffer.Length)
throw new ArgumentOutOfRangeException(nameof(size),
$"Size {size} exceeds buffer capacity {Buffer.Length}");
_length = size;
}
else
{
if (size >= _length)
throw new NotSupportedException("Cannot increase size when Next segment exists");
_length = size;
}
return this;
}
#endregion
#region 切片操作
/// <summary>切片生成新数据包,默认转移所有权</summary>
/// <param name="offset">相对当前包的起始偏移</param>
/// <param name="count">切片长度,-1 表示到末尾</param>
/// <returns>新的数据包实例</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IPacket Slice(Int32 offset, Int32 count = -1) => Slice(offset, count, transferOwner: true);
/// <summary>切片生成新数据包,可选择是否转移所有权</summary>
/// <param name="offset">相对当前包的起始偏移</param>
/// <param name="count">切片长度,-1 表示到末尾</param>
/// <param name="transferOwner">是否转移内存管理权</param>
/// <returns>新的数据包实例</returns>
/// <exception cref="ArgumentOutOfRangeException">偏移量或长度超出有效范围</exception>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
/// <remarks>
/// <para>切片操作共享底层缓冲区以避免内存拷贝。</para>
/// <para>当 transferOwner 为 true 时,新实例负责缓冲区释放,原实例失去管理权。</para>
/// <para>支持跨链式包切片,自动处理边界情况。</para>
/// </remarks>
public IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner)
{
if (_buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be negative.");
if (count > Total - offset)
throw new ArgumentOutOfRangeException(nameof(count),
$"Count {count} with offset {offset} exceeds total length {Total}");
var startPosition = _offset + offset;
var remainInCurrent = _length - offset;
var hasOwnership = _hasOwner && transferOwner;
// 单段数据包处理
if (Next == null)
{
// 转移管理权
if (transferOwner) _hasOwner = false;
var actualCount = count < 0 || count > remainInCurrent ? remainInCurrent : count;
return new OwnerPacket(_buffer, startPosition, actualCount, hasOwnership);
}
// 多段链式包处理
if (remainInCurrent <= 0)
{
// 完全跳过当前段,递归处理后续段
return Next.Slice(offset - _length, count, transferOwner);
}
// 转移管理权
if (transferOwner) _hasOwner = false;
if (count < 0)
{
// 当前段部分 + 所有后续段
return new OwnerPacket(_buffer, startPosition, remainInCurrent, hasOwnership) { Next = Next };
}
if (count <= remainInCurrent)
{
// 仅在当前段内
return new OwnerPacket(_buffer, startPosition, count, hasOwnership);
}
// 跨段处理:当前段 + 后续段切片
return new OwnerPacket(_buffer, startPosition, remainInCurrent, hasOwnership)
{
Next = Next.Slice(0, count - remainInCurrent, transferOwner)
};
}
#endregion
#region 字符串表示
/// <summary>返回数据包的字符串表示形式</summary>
/// <returns>包含缓冲区大小、偏移量、长度和总长度的格式化字符串</returns>
public override String ToString() => $"OwnerPacket[{_buffer?.Length ?? 0}]({_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;
// 缓存底层数组引用,Indexer 直接数组访问避免 Memory.Span 间接开销(1.66ns → ~0.24ns)
private readonly Byte[]? _cachedArray;
private readonly Int32 _cachedOffset;
/// <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];
}
var arr = _cachedArray;
return arr != null ? arr[_cachedOffset + index] : _memory.Span[index];
}
set
{
var p = index - _length;
if (p >= 0)
{
if (Next == null) throw new IndexOutOfRangeException(nameof(index));
Next[p] = value;
}
else
{
var arr = _cachedArray;
if (arr != null)
arr[_cachedOffset + index] = 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;
// 缓存底层数组引用,加速 Indexer 直接数组访问
if (MemoryMarshal.TryGetArray((ReadOnlyMemory<Byte>)memory, out var seg))
{
_cachedArray = seg.Array;
_cachedOffset = seg.Offset;
}
else
{
_cachedArray = null;
_cachedOffset = 0;
}
}
/// <summary>获取分片包。在管理权生命周期内短暂使用</summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Span<Byte> GetSpan() => _memory.Span[.._length];
/// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Memory<Byte> GetMemory() => _memory[.._length];
/// <summary>切片得到新数据包,共用内存块</summary>
/// <param name="offset">偏移</param>
/// <param name="count">个数。默认-1表示到末尾</param>
IPacket IPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
/// <summary>切片得到新数据包,共用内存块</summary>
/// <param name="offset">偏移</param>
/// <param name="count">个数。默认-1表示到末尾</param>
/// <param name="transferOwner">转移所有权。不支持</param>
IPacket IPacket.Slice(Int32 offset, Int32 count, Boolean transferOwner) => Slice(offset, count);
/// <summary>切片得到新数据包,共用内存块,无内存分配</summary>
/// <param name="offset">偏移</param>
/// <param name="count">个数。默认-1表示到末尾</param>
/// <param name="transferOwner">转移所有权。不支持</param>
public MemoryPacket Slice(Int32 offset, Int32 count = -1, Boolean transferOwner = false)
{
// 带有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;
return offset == 0
? new MemoryPacket(_memory, count)
: new MemoryPacket(_memory[offset..], count);
}
/// <summary>尝试获取缓冲区(仅本段,不含 Next)</summary>
/// <param name="segment"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Boolean TryGetArray(out ArraySegment<Byte> segment) => MemoryMarshal.TryGetArray(GetMemory(), out segment);
/// <summary>已重载</summary>
/// <returns></returns>
public override readonly String ToString() => $"MemoryPacket[{_memory.Length}](0, {_length})<{Total}>";
}
/// <summary>字节数组包</summary>
public record struct ArrayPacket : IPacket
{
#region 属性
private readonly 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>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Span<Byte> GetSpan() => new(_buffer, _offset, _length);
/// <summary>获取内存包。在管理权生命周期内短暂使用</summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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)
{
// 内联逻辑,避免 struct 通过 (this as IPacket) 装箱
if (count == 0) return Empty;
var remain = _length - offset;
var next = Next;
if (next != null && remain <= 0) return next.Slice(offset - _length, count, true);
return 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;
return count <= 0 ? Empty : new ArrayPacket(_buffer, start, count);
}
// 如果当前段用完,则取下一段。强转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>尝试获取缓冲区(仅本段,不含 Next)</summary>
/// <param name="segment"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Boolean TryGetArray(out ArraySegment<Byte> segment)
{
segment = new ArraySegment<Byte>(_buffer, _offset, _length);
return true;
}
#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 readonly String ToString() => $"ArrayPacket[{_buffer.Length}]({_offset}, {_length})<{Total}>";
#endregion
}
/// <summary>只读数据包。禁止修改数据,适合多线程共享场景</summary>
/// <remarks>
/// <para>与 <see cref="ArrayPacket"/> 的区别:</para>
/// <list type="bullet">
/// <item>索引器为只读,禁止修改数据</item>
/// <item>不支持 Next 链式结构(始终为 null)</item>
/// <item>GetSpan 返回只读视图(通过 GetMemory().Span 获取)</item>
/// </list>
/// <para>适用场景:配置数据、协议模板、缓存数据等需要防止意外修改的场合</para>
/// </remarks>
public readonly record struct ReadOnlyPacket : IPacket
{
#region 属性
private readonly Byte[] _buffer;
/// <summary>缓冲区</summary>
public Byte[] Buffer => _buffer;
private readonly Int32 _offset;
/// <summary>数据偏移</summary>
public Int32 Offset => _offset;
private readonly Int32 _length;
/// <summary>数据长度</summary>
public Int32 Length => _length;
/// <summary>下一个链式包。只读包不支持链式结构,始终返回 null</summary>
IPacket? IPacket.Next { get => null; set { } }
/// <summary>总长度。只读包不支持链式,等于 Length</summary>
public Int32 Total => _length;
/// <summary>空数据包</summary>
public static ReadOnlyPacket Empty { get; } = new([]);
#endregion
#region 索引
/// <summary>获取指定位置的字节(只读)</summary>
/// <param name="index">索引位置</param>
/// <returns>字节值</returns>
/// <exception cref="IndexOutOfRangeException">索引超出范围</exception>
public Byte this[Int32 index]
{
get
{
if (index < 0 || index >= _length)
throw new IndexOutOfRangeException($"Index {index} is out of range [0, {_length})");
return _buffer[_offset + index];
}
set => throw new NotSupportedException("ReadOnlyPacket does not support modification");
}
#endregion
#region 构造
/// <summary>通过字节数组实例化只读数据包</summary>
/// <param name="buffer">字节数组</param>
/// <param name="offset">起始偏移</param>
/// <param name="count">数据长度,-1 表示到数组末尾</param>
public ReadOnlyPacket(Byte[] buffer, Int32 offset = 0, Int32 count = -1)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
if (count < 0) count = buffer.Length - offset;
if (offset + count > buffer.Length) throw new ArgumentOutOfRangeException(nameof(count));
_buffer = buffer;
_offset = offset;
_length = count;
}
/// <summary>从数组段实例化只读数据包</summary>
/// <param name="segment">数组段</param>
public ReadOnlyPacket(ArraySegment<Byte> segment) : this(segment.Array!, segment.Offset, segment.Count) { }
/// <summary>从 IPacket 创建只读副本</summary>
/// <param name="packet">源数据包</param>
/// <remarks>会复制数据到新的缓冲区,确保完全独立</remarks>
public ReadOnlyPacket(IPacket packet) : this(packet.ToArray()) { }
#endregion
#region 方法
/// <summary>获取分片视图(仅本段,只读包不支持 Next)</summary>
/// <returns>只读字节片段</returns>
public Span<Byte> GetSpan() => new(_buffer, _offset, _length);
/// <summary>获取内存块(仅本段,只读包不支持 Next)</summary>
/// <returns>只读内存块</returns>
public Memory<Byte> GetMemory() => new(_buffer, _offset, _length);
/// <summary>切片得到新的只读数据包</summary>
/// <param name="offset">相对偏移</param>
/// <param name="count">数据长度,-1 表示到末尾</param>
/// <returns>新的只读数据包</returns>
IPacket IPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
/// <summary>切片得到新的只读数据包</summary>
/// <param name="offset">相对偏移</param>
/// <param name="count">数据长度</param>
/// <param name="transferOwner">是否转移所有权(只读包忽略此参数)</param>
/// <returns>新的只读数据包</returns>
IPacket IPacket.Slice(Int32 offset, Int32 count, Boolean transferOwner) => Slice(offset, count);
/// <summary>切片得到新的只读数据包,无内存分配</summary>
/// <param name="offset">相对偏移</param>
/// <param name="count">数据长度,-1 表示到末尾</param>
/// <returns>新的只读数据包</returns>
public ReadOnlyPacket Slice(Int32 offset, Int32 count = -1)
{
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
var newOffset = _offset + offset;
var remain = _length - offset;
if (count < 0 || count > remain) count = remain;
if (count < 0) count = 0;
return new ReadOnlyPacket(_buffer, newOffset, count);
}
/// <summary>尝试获取数组段(仅本段,只读包不支持 Next)</summary>
/// <param name="segment">输出的数组段</param>
/// <returns>始终返回 true</returns>
public Boolean TryGetArray(out ArraySegment<Byte> segment)
{
segment = new ArraySegment<Byte>(_buffer, _offset, _length);
return true;
}
#endregion
#region 转换
/// <summary>转换为字节数组</summary>
/// <returns>字节数组副本</returns>
public Byte[] ToArray()
{
if (_offset == 0 && _length == _buffer.Length) return _buffer;
return GetSpan().ToArray();
}
/// <summary>从字节数组隐式转换</summary>
/// <param name="buffer">字节数组</param>
public static implicit operator ReadOnlyPacket(Byte[] buffer) => new(buffer);
/// <summary>从数组段隐式转换</summary>
/// <param name="segment">数组段</param>
public static implicit operator ReadOnlyPacket(ArraySegment<Byte> segment) => new(segment);
/// <summary>已重载</summary>
public override String ToString() => $"ReadOnlyPacket[{_buffer.Length}]({_offset}, {_length})";
#endregion
}
|