using System.Buffers;
using System.Runtime.CompilerServices;
using System.Text;
using NewLife.Collections;
namespace NewLife.Data;
/// <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>
private const Int32 MaxChainSteps = 1_000_000;
/// <summary>将数æ®åŒ…è¿½åŠ åˆ°å½“å‰åŒ…链末尾</summary>
/// <param name="pk">当å‰åŒ…链头节点</param>
/// <param name="next">è¦è¿½åŠ çš„æ•°æ®åŒ…(å¯åŒ…å«è‡ªèº«é“¾ï¼‰</param>
/// <returns>原包链头节点,便于链å¼è°ƒç”¨</returns>
/// <remarks>
/// <list type="bullet">
/// <item>æ—¶é—´å¤æ‚度:O(n),n 为当å‰é“¾é•¿åº¦</item>
/// <item>防护机制:自引用检测;回指链首的环在链尾处切æ–ï¼›next é“¾åŒ…å« pk æ—¶æ‹’ç»æŒ‚接(å¦åˆ™æˆçŽ¯ï¼‰ï¼›è¶…é•¿é历放弃挂接</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 steps = 0;
var current = pk;
while (current.Next != null)
{
if (ReferenceEquals(current.Next, pk)) break;
current = current.Next;
if (++steps > MaxChainSteps) return pk;
}
// next é“¾è‹¥åŒ…å« pk,挂接åŽä¼šå½¢æˆçŽ¯ï¼ˆå¦‚å°†é“¾é¦–æŒ‚åˆ°é“¾ä¸é—´èŠ‚ç‚¹ä¹‹åŽï¼‰ï¼šæ‹’ç»ã€‚
// åŒæ—¶é™æ¥ï¼Œé˜²æ¢ next 链自身已æŸå(å«çŽ¯ï¼‰æ—¶é™·å…¥æ»å¾ªçޝ
steps = 0;
for (var n = next; n != null; n = n.Next)
{
if (ReferenceEquals(n, pk)) return pk;
if (++steps > MaxChainSteps) return pk;
}
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">æ•°æ®åŒ…ï¼ˆå•æ®µæˆ–链å¼ï¼‰</param>
/// <returns>åªè¯»å—节åºåˆ—;空包返回空åºåˆ—</returns>
/// <exception cref="ArgumentNullException">æ•°æ®åŒ…为 null</exception>
/// <remarks>
/// <para>é›¶æ‹·è´ï¼šåºåˆ—直接引用数æ®åŒ…底层缓冲区,ä¸å¤åˆ¶æ•°æ®ã€ä¸å¢žåŠ å¼•ç”¨è®¡æ•°ã€‚</para>
/// <para><b>生命周期</b>:åºåˆ—仅在数æ®åŒ…奿Ÿ„有效期内å¯ç”¨â€”â€”æ‹¥æœ‰å¥æŸ„ Dispose å½’è¿˜æ± åŒ–ç¼“å†²ã€æˆ–借阅视图的接收缓冲被å¤ç”¨åŽï¼Œç»§ç»è¯»å–将产生未定义行为。需è¦è·¨è½®/è·¨å¼‚æ¥æŒæœ‰æ—¶ï¼Œè¯·å…ˆåˆ‡å‡ºå…±äº«åˆ‡ç‰‡ï¼ˆ<see cref="IOwnerPacket.Slice(Int32, Int32)"/>)或克隆。</para>
/// </remarks>
/// <example>
/// <code>
/// var seq = pk.AsReadOnlySequence();
/// foreach (var memory in seq) { /* 逿®µé›¶æ‹·è´è¯»å– */ }
/// </code>
/// </example>
public static ReadOnlySequence<Byte> AsReadOnlySequence(this IPacket pk)
{
if (pk == null) throw new ArgumentNullException(nameof(pk));
// 啿®µå¿«é€Ÿè·¯å¾„:直接以内å˜åˆ›å»ºåºåˆ—ï¼Œå…æ®µå¯¹è±¡åˆ†é…
if (pk.Next == null) return new ReadOnlySequence<Byte>(pk.GetMemory());
var first = new PacketSequenceSegment(pk.GetMemory());
var last = first;
for (var node = pk.Next; node != null; node = node.Next)
{
last = last.Append(node.GetMemory());
}
return new ReadOnlySequence<Byte>(first, 0, last, last.Memory.Length);
}
/// <summary>将数æ®åŒ…链的指定窗å£è½¬æ¢ä¸ºåªè¯»å—节åºåˆ—(零拷è´è§†å›¾ï¼Œè·¨æ®µçª—å£è‡ªåЍè£å‰ªï¼‰</summary>
/// <param name="pk">æ•°æ®åŒ…ï¼ˆå•æ®µæˆ–链å¼ï¼‰</param>
/// <param name="offset">相对链首的起始åç§»</param>
/// <param name="count">å—节数,-1 表示到链尾</param>
/// <returns>åªè¯»å—节åºåˆ—;窗å£ä¸ºç©ºæ—¶è¿”回空åºåˆ—</returns>
/// <exception cref="ArgumentNullException">æ•°æ®åŒ…为 null</exception>
/// <exception cref="ArgumentOutOfRangeException">å移或长度超出数æ®èŒƒå›´</exception>
public static ReadOnlySequence<Byte> AsReadOnlySequence(this IPacket pk, Int32 offset, Int32 count = -1)
{
if (pk == null) throw new ArgumentNullException(nameof(pk));
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be negative.");
var total = pk.Total;
if (count < 0) count = total - offset;
if (offset > total || count > total - offset)
throw new ArgumentOutOfRangeException(nameof(count), $"Offset {offset} with count {count} exceeds total length {total}");
if (count == 0) return ReadOnlySequence<Byte>.Empty;
// 啿®µå¿«é€Ÿè·¯å¾„
if (pk.Next == null) return new ReadOnlySequence<Byte>(pk.GetMemory().Slice(offset, count));
// 跳过 offset 所在的整段(空段跳过时ä¸ä¼šåœç•™ï¼‰
var skip = offset;
var node = pk;
while (node != null && skip >= node.Length)
{
skip -= node.Length;
node = node.Next;
}
if (node == null) return ReadOnlySequence<Byte>.Empty;
var remain = count;
// 起点段:切掉段内åç§»åŽå–窗å£å†…部分
var memory = node.GetMemory();
if (skip > 0) memory = memory.Slice(skip);
var take = Math.Min(memory.Length, remain);
var first = new PacketSequenceSegment(memory.Slice(0, take));
var last = first;
remain -= take;
// åŽç»æ®µï¼šé€æ®µè£å‰ªï¼Œç›´åˆ°çª—å£å–满
for (node = node.Next; node != null && remain > 0; node = node.Next)
{
var mem = node.GetMemory();
var len = Math.Min(mem.Length, remain);
last = last.Append(mem.Slice(0, len));
remain -= len;
}
return new ReadOnlySequence<Byte>(first, 0, last, last.Memory.Length);
}
/// <summary>把åºåˆ—å‰ç¼€å¤åˆ¶åˆ°ç›®æ ‡è·¨åº¦ï¼ˆæœ€å¤šå¤åˆ¶ç›®æ ‡é•¿åº¦å—节)</summary>
/// <param name="source">æºåºåˆ—</param>
/// <param name="destination">ç›®æ ‡è·¨åº¦</param>
/// <returns>实际å¤åˆ¶çš„å—节数</returns>
/// <remarks>ç”¨äºŽæŠŠè·¨æ®µå¤´éƒ¨æ‹¼å…¥æ ˆç¼“å†²åŽå¤ç”¨è·¨åº¦è§£æžï¼›è·¨æ®µå¤åˆ¶å¤ç”¨ BCL çš„ <see cref="BuffersExtensions.CopyTo{T}"/></remarks>
internal static Int32 CopyPrefix(ReadOnlySequence<Byte> source, Span<Byte> destination)
{
var count = (Int32)Math.Min(source.Length, destination.Length);
if (count <= 0) return 0;
source.Slice(0, count).CopyTo(destination[..count]);
return count;
}
/// <summary>æ•°æ®åŒ…åºåˆ—段。把数æ®åŒ…é“¾çš„å•æ®µå†…å˜é€‚é…ä¸ºæ ‡å‡† <see cref="ReadOnlySequenceSegment{T}"/>,支撑零拷è´åºåˆ—视图</summary>
internal sealed class PacketSequenceSegment : ReadOnlySequenceSegment<Byte>
{
/// <summary>段承载的数æ®å¥æŸ„。数æ®ç®¡é“用它承担消费归还;åºåˆ—æ¡¥æŽ¥ï¼ˆæ— æ‰€æœ‰æƒï¼‰ä¸º null</summary>
public IPacket? Packet { get; set; }
/// <summary>使用内å˜ç‰‡æ®µåˆ›å»ºåºåˆ—段</summary>
/// <param name="memory">内å˜ç‰‡æ®µ</param>
public PacketSequenceSegment(ReadOnlyMemory<Byte> memory) => Memory = memory;
/// <summary>åœ¨é“¾å°¾è¿½åŠ å†…å˜ç‰‡æ®µï¼Œè¿”回新的链尾段</summary>
/// <param name="memory">内å˜ç‰‡æ®µ</param>
/// <returns>新的链尾段</returns>
public PacketSequenceSegment Append(ReadOnlyMemory<Byte> memory)
{
var segment = new PacketSequenceSegment(memory)
{
RunningIndex = RunningIndex + Memory.Length,
};
Next = segment;
return segment;
}
}
#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;
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);
using var chars = Pool.Rent<Char>(charCount);
var written = encoding.GetChars(span, chars.Span);
writer.Write(chars.Span[..written]);
#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;
// ä¸Žæœ¬ç±»çš„å¤šåŒ…é“¾åˆ†æ”¯ä¿æŒä¸€è‡´ï¼šæ˜¾å¼ä¼ 入过大的 count 时夹紧到å¯ç”¨èŒƒå›´ï¼Œé¿å…越窗读
if (offset < 0) offset = 0;
if (count > pk.Length - offset) count = pk.Length - offset;
if (count <= 0) return [];
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>
/// <param name="buffer">ç›®æ ‡ç¼“å†²åŒº</param>
/// <returns>实际读å–çš„å—节数(ä¸è¶…è¿‡ç¼“å†²åŒºé•¿åº¦ä¸Žæ•°æ®æ€»é•¿åº¦ï¼‰</returns>
/// <remarks>从数æ®åŒ…链起点开始读å–ï¼›å•节点时直接拷è´ï¼Œé“¾å¼æ—¶é€æ®µç»æŽ¥</remarks>
public static Int32 ReadBytes(this IPacket pk, Span<Byte> buffer)
{
if (buffer.Length == 0) return 0;
var total = pk.Total;
var count = buffer.Length < total ? buffer.Length : total;
if (count <= 0) return 0;
// å•节点直接拷è´
if (pk.Next == null)
{
pk.GetSpan()[..count].CopyTo(buffer);
return count;
}
// å¤šèŠ‚ç‚¹é“¾ï¼šè·¨æ®µç»æŽ¥æ‹·è´
var pos = 0;
for (var node = pk; node != null && pos < count; node = node.Next)
{
var span = node.GetSpan();
var c = Math.Min(span.Length, count - pos);
if (c > 0)
{
span[..c].CopyTo(buffer[pos..]);
pos += c;
}
}
return pos;
}
/// <summary>èŽ·å–æ•°æ®åŒ…链的å‰ç¼€è·¨åº¦ã€‚帧首节点足够时直接引用;ä¸è¶³ä¸”ä¸ºé“¾å¼æ—¶æ‹¼å…¥ç¼“冲(ä¸ç‰©åŒ–整帧)</summary>
/// <param name="pk">æ•°æ®åŒ…ï¼ˆå•æ®µæˆ–链å¼ï¼‰</param>
/// <param name="buffer">跨段拼读缓冲(调用方分é…,长度ä¸å°äºŽ count)</param>
/// <param name="count">需è¦çš„æœ€å¤§å‰ç¼€å—节数</param>
/// <returns>å‰ç¼€è·¨åº¦ï¼›å¸§é¦–节点足够时零拷è´ç›´å¼•,跨段时拼入 bufferï¼ˆæ•°æ®æ€»é‡ä¸è¶³æ—¶å¯èƒ½çŸäºŽ count)</returns>
/// <remarks>å议头部解æžçš„公共入å£ï¼šç»Ÿä¸€â€œé¦–段直读 + 跨段拼读â€ä¸¤æ¡è·¯å¾„,é¿å…å„åè®®å„自手写拼读兜底。</remarks>
internal static ReadOnlySpan<Byte> GetPrefix(this IPacket pk, Span<Byte> buffer, Int32 count)
{
// å¸§é¦–èŠ‚ç‚¹è¶³å¤Ÿæˆ–æ— åŽç»é“¾ï¼šç›´æŽ¥å¼•用(数æ®ä¸è¶³ç”±è§£æžæ–¹åˆ¤å®šï¼‰
var span = pk.GetSpan();
if (span.Length >= count || pk.Next == null) return span;
// å‰ç¼€è·¨èŠ‚ç‚¹ï¼šæ‹¼å…¥ç¼“å†²ï¼ˆæœ€å¤š count å—节)
if (buffer.Length > count) buffer = buffer[..count];
return buffer[..pk.ReadBytes(buffer)];
}
/// <summary>在数æ®åŒ…链䏿Ÿ¥æ‰¾ç›®æ ‡å—节åºåˆ—,返回相对链头的全局åç§»</summary>
/// <param name="pk">æºæ•°æ®åŒ…,支æŒé“¾å¼</param>
/// <param name="data">ç›®æ ‡å—节åºåˆ—</param>
/// <returns>匹é…起点相对链头的å移;未找到返回 -1</returns>
/// <remarks>
/// 逿®µæ‰«æé›¶æ‹·è´ï¼šç›®æ ‡å¯èƒ½è¢«åˆ‡åœ¨æ®µä¸Žæ®µä¹‹é—´ï¼Œå› æ¤ä¿ç•™â€œå·²æ‰«æè¿‡çš„æœ€åŽ k-1 个å—节â€ï¼Œ
/// 与本段头部拼起æ¥å…ˆæŸ¥ä¸€æ¬¡ï¼ˆè·¨æ®µåŒ¹é…的起点必然早于本段内匹é…,先查æ‰èƒ½è¿”回全局最早匹é…),å†å𿮵内 span 快速查找;
/// ç›®æ ‡è·¨å¤šä¸ªçŸæ®µï¼ˆå«ç©ºæ®µï¼‰åŒæ ·èƒ½æŸ¥åˆ°ã€‚
/// </remarks>
public static Int32 IndexOf(this IPacket pk, ReadOnlySpan<Byte> data)
{
if (pk == null) return -1;
var k = data.Length;
if (k == 0 || pk.Total < k) return -1;
// 啿®µç›´æŽ¥æŸ¥æ‰¾ï¼ˆæœ€å¸¸è§è·¯å¾„)
if (pk.Next == null) return pk.GetSpan().IndexOf(data);
// å•å—èŠ‚ç›®æ ‡ï¼šé€æ®µæŸ¥æ‰¾ï¼Œä¸å¯èƒ½è·¨æ®µ
if (k == 1)
{
var p1 = 0;
for (var node = pk; node != null; node = node.Next)
{
var idx1 = node.GetSpan().IndexOf(data[0]);
if (idx1 >= 0) return p1 + idx1;
p1 += node.Length;
}
return -1;
}
var k1 = k - 1;
// å·²æ‰«ææ•°æ®çš„æœ€åŽ k-1 å—èŠ‚ï¼›ç›®æ ‡è¢«åˆ‡æ®µæ—¶ï¼Œå®ƒæä¾›åŒ¹é…起点所在的å‰ç¼€
var tail = k1 <= 256 ? stackalloc Byte[k1] : new Byte[k1];
var tailLen = 0;
// 拼接窗å£ï¼šå°¾éƒ¨ç¼“冲 + 本段头部,å„ä¸è¶…过 k-1 å—节
var winSize = k1 * 2;
var win = winSize <= 256 ? stackalloc Byte[winSize] : new Byte[winSize];
var pos = 0;
for (var node = pk; node != null; node = node.Next)
{
var span = node.GetSpan();
// è·¨æ®µæŸ¥æ‰¾ï¼šç›®æ ‡è¢«åˆ‡åœ¨æ®µé—´ï¼Œèµ·ç‚¹åœ¨â€œæœ€åŽ k-1 å—节â€å†…ã€ç»ˆç‚¹åœ¨æœ¬æ®µå¤´éƒ¨ã€‚
// 跨段匹é…的起点必然早于本段内的任何匹é…,必须先查,å¦åˆ™ä¼šè¿”回更晚的段内匹é…
if (tailLen > 0 && span.Length > 0)
{
var headLen = Math.Min(k1, span.Length);
var w = win[..(tailLen + headLen)];
tail[..tailLen].CopyTo(w);
span[..headLen].CopyTo(w[tailLen..]);
var j = w.IndexOf(data);
if (j >= 0) return pos - tailLen + j;
}
// 段内查找
var idx = span.IndexOf(data);
if (idx >= 0) return pos + idx;
// 滚动更新尾部缓冲:ä¿ç•™â€œå·²æ‰«ææ•°æ®â€çš„æœ€åŽ k-1 å—节
if (span.Length >= k1)
{
span[^k1..].CopyTo(tail);
tailLen = k1;
}
else if (span.Length > 0)
{
// 本段太çŸï¼Œå…ˆä¸¢æŽ‰å°¾éƒ¨ç¼“冲里过期的头部,å†è¿½åŠ æœ¬æ®µ
var move = tailLen + span.Length - k1;
if (move > 0)
{
tail[move..tailLen].CopyTo(tail);
tailLen -= move;
}
span.CopyTo(tail[tailLen..]);
tailLen += span.Length;
}
pos += span.Length;
}
return -1;
}
/// <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>
/// <returns>扩展åŽçš„æ•°æ®åŒ…(零拷è´ï¼‰</returns>
/// <remarks>
/// <para>组装内容时用 <c>new OwnerPacket(size, reserve)</c> 预留头部(å‚考 <see cref="Serialization.SpanSerializer.HeaderReserve"/>),下游å³å¯é›¶æ‹·è´å€Ÿä½å†™å…¥å议头。</para>
/// <para><b>所有æƒ</b>:结果与原包共享数æ®ï¼ˆæ‹¥æœ‰å¥æŸ„引用计数å„è‡ªé‡Šæ”¾ï¼‰ï¼ŒåŽŸå¥æŸ„ä¿æŒæœ‰æ•ˆï¼›æœªé¢„留空间时请改用 <see cref="PrepareHeader"/> 自动é™çº§ã€‚</para>
/// </remarks>
/// <exception cref="InvalidOperationException">å‰ç½®é¢„留空间ä¸è¶³ï¼Œæˆ–ç±»åž‹ä¸æ”¯æŒå€Ÿä½</exception>
public static IPacket ExpandHeader(this IPacket pk, Int32 size)
{
if (pk is OwnerPacket owner) return owner.ExpandHeader(size);
if (pk is ArrayPacket ap) return ap.ExpandHeader(size);
throw new InvalidOperationException($"类型 [{pk.GetType().Name}] 䏿”¯æŒå¤´éƒ¨å€Ÿä½æ‰©å±•;头部写入需è¦é¢„留空间的数æ®åŒ…(OwnerPacket/ArrayPacket)");
}
/// <summary>ä¸ºè´Ÿè½½å‡†å¤‡å¸§å¤´ç©ºé—´ã€‚å·²é¢„ç•™çš„æ‹¥æœ‰å¥æŸ„é›¶æ‹·è´å€Ÿä½ï¼Œå…¶ä½™æ–°å¤´èŠ‚ç‚¹æŒ‚æŽ¥è´Ÿè½½é“¾ï¼›åŽŸè´Ÿè½½å¥æŸ„始终有效</summary>
/// <param name="body">负载包(å¯ç©ºï¼‰</param>
/// <param name="size">帧头å—节数</param>
/// <returns>带头部空间的结果包(头部区未åˆå§‹åŒ–),调用方负责 Dispose</returns>
/// <remarks>
/// <para><b>两ç§ç–略(å‡ä¸ºé›¶æ‹·è´ï¼‰</b>:</para>
/// <list type="number">
/// <item>æ‹¥æœ‰å¥æŸ„且已预留(<see cref="OwnerPacket"/>ã€<see cref="IPacket.FreeHeader"/> 足够):å‘å‰å€Ÿä½å…±äº«ï¼Œå¸§å¤´è½åœ¨åŽŸç¼“å†²çš„é¢„ç•™åŒº</item>
/// <item>å…¶ä½™ï¼šæ–°å¤´èŠ‚ç‚¹æŒ‚æŽ¥è´Ÿè½½é“¾â€”â€”æ‹¥æœ‰å¥æŸ„先切片为独å 的共享链(引用计数å„è‡ªæŒæœ‰ï¼‰ï¼Œè§†å›¾æ— 所有æƒå¯ç›´æŽ¥æŒ‚接</item>
/// </list>
/// <para><b>所有æƒ</b>:两ç§ç–ç•¥éƒ½ä¸æ”¹åŠ¨åŽŸè´Ÿè½½å¥æŸ„(借ä½ä¸ºå…±äº«ã€é“¾å¼ä¸ºåªè¯»å¼•用),消æ¯è´Ÿè½½åœ¨æž„建åŽä¾ç„¶å¯ç”¨ã€‚</para>
/// <para><b>时效</b>:结果å¯èƒ½å¼•用原负载缓冲,在帧å‘é€å®Œæˆå‰ä¸å¾—å¤ç”¨æˆ–改写该缓冲。</para>
/// </remarks>
public static IOwnerPacket PrepareHeader(this IPacket? body, Int32 size)
{
// 负长度会让借ä½åˆ†æ”¯å˜æˆâ€œç¼©å°çª—å£â€è€Œéžæ‰©å±•ï¼Œå¿…é¡»æ˜¾å¼æ‹’ç»
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size));
if (body == null) return new OwnerPacket(size);
// æ‹¥æœ‰å¥æŸ„且已预留:å‘å‰å€Ÿä½å…±äº«ï¼ˆé›¶æ‹·è´ï¼‰ï¼ŒåŽŸå¥æŸ„ä¿æŒæœ‰æ•ˆ
if (body is OwnerPacket op && op.RefCount > 0 && op.FreeHeader >= size) return op.ExpandHeader(size);
// å…¶ä½™ï¼šæ–°å¤´èŠ‚ç‚¹æŒ‚æŽ¥è´Ÿè½½ã€‚æ‹¥æœ‰å¥æŸ„先切片为独å 共享链(引用计数å„è‡ªæŒæœ‰ï¼‰ï¼›
// è§†å›¾æ— æ‰€æœ‰æƒï¼Œå¯ç›´æŽ¥æŒ‚接(Dispose 为空æ“作,链ä¸ä¼šè¯¯æ”¾æºï¼‰
var payload = body is IOwnerPacket owner ? owner.Slice(0, -1) : body;
return new OwnerPacket(size) { Next = payload };
}
#endregion
}
|