using System.Buffers;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
#if DEBUG
using NewLife.Log;
#endif
namespace NewLife.Data;
/// <summary>æ± åŒ–ç¼“å†²å¼•ç”¨è®¡æ•°ã€‚å¤šä¸ªæ•°æ®åŒ…共享åŒä¸€ç¼“冲区时统一记录引用数é‡ï¼Œæœ€åŽä¸€ä¸ªå¼•ç”¨é‡Šæ”¾æ—¶å½’è¿˜å†…å˜æ± </summary>
/// <remarks>
/// <para>切片共享通过 <see cref="AddRef"/> 递增引用计数;æ¯ä¸ªå¥æŸ„å„自 <see cref="Release"/>ï¼Œå½’é›¶æ—¶å½’è¿˜å†…å˜æ± 。</para>
/// <para>引用计数归零时æ‰å½’è¿˜ç¼“å†²åŒºï¼Œå› æ¤å¤šä¸ªå¥æŸ„å¯ä»¥ç‹¬ç«‹ä½¿ç”¨ã€å„自释放,互ä¸å½±å“。</para>
/// <para><b>必须为 class</b>ï¼šè®¡æ•°å™¨è¢«å¤šä¸ªå¥æŸ„共享,引用åŒä¸€æ€§æ˜¯å¼•用计数æˆç«‹çš„å‰æï¼›ç»“æž„ä½“ä¼šå› å€¼æ‹·è´å¯¼è‡´å„奿Ÿ„儿Œä¸€ä»½è®¡æ•°ï¼Œå¼•用计数完全失效。</para>
/// </remarks>
/// <remarks>实例化,引用计数åˆå§‹ä¸º 1</remarks>
/// <param name="buffer">缓冲数组</param>
/// <param name="returnToPool">引用归零时是å¦å½’è¿˜å†…å˜æ± </param>
internal sealed class ArrayOwner(Byte[] buffer, Boolean returnToPool)
{
#region 属性
/// <summary>缓冲数组</summary>
public Byte[] Buffer { get; private set; } = buffer;
/// <summary>引用归零时是å¦å½’è¿˜å†…å˜æ± </summary>
public Boolean ReturnToPool { get; } = returnToPool;
private Int32 _refCount = 1;
/// <summary>当å‰å¼•用数é‡ã€‚供观测与决ç–(如接收层判æ–缓冲是å¦å¯å¤ç”¨ï¼‰</summary>
public Int32 RefCount => Volatile.Read(ref _refCount);
#endregion
#region 方法
/// <summary>新增一个引用</summary>
public void AddRef() => Interlocked.Increment(ref _refCount);
/// <summary>é‡Šæ”¾ä¸€ä¸ªå¼•ç”¨ï¼Œå½’é›¶æ—¶å½’è¿˜å†…å˜æ± </summary>
public void Release()
{
if (Interlocked.Decrement(ref _refCount) == 0 && ReturnToPool)
ArrayPool<Byte>.Shared.Return(Buffer);
}
#endregion
}
/// <summary>所有æƒå†…å˜åŒ…。基于 ArrayPool 的引用计数内å˜ç®¡ç†ï¼Œæ”¯æŒé“¾å¼ç»“æž„ã€é›¶æ‹·è´åˆ‡ç‰‡ä¸Žå…±äº«ä½¿ç”¨</summary>
/// <remarks>
/// <para><b>æ ¸å¿ƒç‰¹æ€§</b>:</para>
/// <list type="bullet">
/// <item>å†…å˜æ± å¤ç”¨ï¼šä½¿ç”¨ <see cref="ArrayPool{T}.Shared"/> å‡å°‘ GC 压力,引用计数归零时统一归还</item>
/// <item>引用计数共享:<see cref="Slice(Int32, Int32)"/> è¿”å›žç‹¬ç«‹å¥æŸ„ï¼Œå¤šä¸ªå¥æŸ„åŒæ—¶å¼•用åŒä¸€ç¼“冲区,å„自独立读å–与释放</item>
/// <item>é›¶æ‹·è´åˆ‡ç‰‡ï¼šå…±äº«åº•层缓冲区,é¿å…ä¸å¿…è¦çš„内å˜åˆ†é…</item>
/// <item>链å¼ç»“构:支æŒå¤šæ®µæ•°æ®åŒ…è¿žæŽ¥ï¼Œé€æ˜Žå¤„ç†è·¨æ®µè®¿é—®</item>
/// </list>
/// <para><b>所有æƒè¯ä¹‰</b>:</para>
/// <list type="number">
/// <item>共享切片:<see cref="Slice(Int32, Int32)"/> æŒ‰æ®µé€’å¢žå¼•ç”¨è®¡æ•°ï¼Œè¿”å›žç‹¬ç«‹å¥æŸ„(具体类型 <see cref="OwnerPacket"/>,å¯ç›´æŽ¥ <c>using</c> é‡Šæ”¾ï¼‰ï¼›åŒæ–¹ï¼ˆæˆ–多方)å‡å¯ç»§ç»ä½¿ç”¨ï¼Œå„自 <see cref="Dispose"/>,最åŽä¸€ä¸ªé‡Šæ”¾æ—¶æ‰å½’è¿˜å†…å˜æ± </item>
/// <item>ç‹¬å æ¢çª—:<see cref="OwnerPacket(OwnerPacket, Int32)"/> å¤´éƒ¨æ‰©å±•æž„é€ ï¼ŒæŽ¥ç®¡æºå®žä¾‹çš„引用与链,æºå®žä¾‹æ•´ä½“作废(仅æ¤ä¸€å¤„ä¿ç•™æŽ¥ç®¡è¯ä¹‰ï¼Œè°ƒç”¨æ–¹éœ€è‡ªè¡Œç¡®ä¿æ— å…¶å®ƒå…±äº«å¥æŸ„)</item>
/// <item>接收层轮末è£å†³ï¼šä¼šè¯ç§æœ‰å¥æŸ„éšç¼“冲借出时创建并挂到接收槽,轮末按 <see cref="RefCount"/> åˆ¤å®šâ€”â€”æ— äººæŒæœ‰ï¼ˆä¸º 1)时回挂接收槽,下一轮 <see cref="Resize"/> é‡è®¾çª—å£å¤ç”¨ï¼›å˜åœ¨å…±äº«å¼•ç”¨æ—¶é‡Šæ”¾æœ¬å¼•ç”¨å¹¶æ¢æ–°ç¼“å†²ï¼ˆæ–°ç¼“å†²åŒæ ·å³åˆ»åŒ…è£…æŒ‚æ§½ï¼‰ã€‚æŠŠæ•°æ®æŠ•é€’ç»™å…¥ç«™ç®¡é“也计入共享引用(管é“切出共享切片),ä¿è¯è½®æœ«ä¸ä¼šå¤ç”¨ä»åœ¨æ¶ˆè´¹çš„缓冲。会è¯å…³é—æ—¶é‡Šæ”¾æ§½å¥æŸ„å³å®Œæˆå½’è¿˜ï¼šå¼•ç”¨å½’é›¶åˆ™å›žæ± ï¼Œä»æœ‰å…±äº«åˆ™å‡æ•°ã€ç”±æœ€åŽä¸€ä¸ªæŒæœ‰è€…归还</item>
/// </list>
/// <para><b>生命周期管ç†</b>:æ¯ä¸ªæŒæœ‰å¼•ç”¨çš„å¥æŸ„都必须调用 <see cref="Dispose"/>ï¼›å¼•ç”¨è®¡æ•°å½’é›¶æ—¶ç¼“å†²åŒºå½’è¿˜å†…å˜æ± ã€‚æœªé‡Šæ”¾çš„å¥æŸ„ä¼šè®©ç¼“å†²åŒºæ— æ³•å›žæ± ï¼Œè¿™æ˜¯ä½¿ç”¨æœ¬ç±»åž‹å”¯ä¸€çš„çºªå¾‹è¦æ±‚。
/// 开呿œŸï¼ˆDEBUGï¼‰ç”±æžæž„函数兜底释放æ¼é‡Šæ”¾çš„奿Ÿ„并输出 XTrace 告è¦ï¼›å‘布版默认ä¸ç¼–è¯‘æžæž„,ä¸äº§ç”Ÿç»ˆç»“队列登记与终结器调度开销,如需生产兜底å¯å®šä¹‰ç¼–è¯‘ç¬¦å· OWNERPACKET_FINALIZER å¼€å¯ã€‚</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>
/// <item><b>æˆæœ¬åŸºçº¿ï¼ˆåŸºå‡†å®žæµ‹ï¼‰</b>:所有æƒç®¡ç†å¼€é”€æ’定ã€ä¸Žæ•°æ®å¤§å°æ— å…³â€”â€”æž„é€ +释放约 19nsã€å…±äº«åˆ‡ç‰‡ï¼ˆåŒæ–¹å„释放一次)约 42nsï¼›
/// 对照拷è´éšå¤§å°çº¿æ€§å¢žé•¿ï¼ˆ64B≈1.4nsã€4KB≈45nsã€64KB≈1.05µsã€128KB≈2.8µs),故 2–4KB 是“å°å¸§æ‹·è´ã€å¤§å¸§åˆ‡ç‰‡â€çš„ç»éªŒåˆ†ç•Œï¼ˆâ‰¤512B æ‹·è´æ›´ä¾¿å®œï¼Œâ‰¥4KB 切片å 优)。
/// æ¯ä¸ªæ‹¥æœ‰å¥æŸ„固定多 32B 引用计数对象(ArrayOwner);DEBUG æž„å»ºçš„æžæž„兜底å¦åŠ çº¦ 30~43ns/奿Ÿ„,Release 默认ä¸ç¼–译(è§ã€Šå†…å˜åˆ†é…ä¸Žæ‹·è´æˆæœ¬æŠ¥å‘Šã€‹ï¼‰ã€‚</item>
/// </list>
/// </remarks>
public sealed class OwnerPacket : IPacket, IOwnerPacket
{
#region å—æ®µä¸Žå±žæ€§
private static readonly Byte[] _empty = [];
private Byte[]? _buffer;
private Int32 _offset;
private Int32 _length;
private ArrayOwner? _owner;
/// <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>当å‰ç¼“å†²åŒºçš„å¼•ç”¨å¥æŸ„æ•°ï¼ˆåŒ…å«æœ¬å¥æŸ„ï¼‰ã€‚è§†å›¾èŠ‚ç‚¹ï¼ˆä¸æŒå¼•用)返回 0</summary>
/// <remarks>仅供观测与决ç–(如接收层判æ–缓冲能å¦å¤ç”¨ï¼‰ï¼Œä¸è¦ç”¨äºŽåŒæ¥æŽ§åˆ¶ã€‚</remarks>
public Int32 RefCount => _owner?.RefCount ?? 0;
/// <summary>头部å¯å€Ÿä½ç©ºé—´ã€‚ç‰äºŽåˆ†é…时预留的å—节数,供下游å‘å‰å€Ÿä½å†™å…¥å议头</summary>
public Int32 FreeHeader => _offset;
/// <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 => _buffer == null
? throw new ObjectDisposedException(nameof(OwnerPacket))
: 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
{
if (_buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
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>
/// <param name="reserve">å‰ç½®é¢„留头部å—节数,供下游å‘å‰å€Ÿä½å†™å…¥å议头(å‚考 <see cref="Serialization.SpanSerializer.HeaderReserve"/>)。
/// <b>æ— é»˜è®¤å€¼</b>:ä¸éœ€è¦é¢„留时请用å•å‚é‡è½½ <see cref="OwnerPacket(Int32)"/></param>
/// <exception cref="ArgumentOutOfRangeException">长度为负数或预留为负数</exception>
/// <remarks>实际分é…的缓冲区å¯èƒ½å¤§äºŽè¯·æ±‚长度,以适é…å†…å˜æ± 的分片ç–ç•¥ï¼›é¢„ç•™ç©ºé—´ç» <see cref="FreeHeader"/> 查询</remarks>
public OwnerPacket(Int32 length, Int32 reserve)
{
if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative.");
if (reserve < 0) throw new ArgumentOutOfRangeException(nameof(reserve), "Reserve must be non-negative.");
var buffer = ArrayPool<Byte>.Shared.Rent(length + reserve);
_buffer = buffer;
_offset = reserve;
_length = length;
_owner = new ArrayOwner(buffer, true);
}
/// <summary>创建指定长度的内å˜åŒ…ï¼Œä»Žå…±äº«å†…å˜æ± 借用缓冲区。<b>æ— </b>å‰ç½®é¢„留头部空间</summary>
/// <param name="length">æ•°æ®åŒºé•¿åº¦</param>
/// <remarks>
/// <para>ç‰ä»·äºŽ <c>new OwnerPacket(length, 0)</c>;需è¦å议头借ä½ç©ºé—´æ—¶è¯·æ˜¾å¼ä½¿ç”¨ <see cref="OwnerPacket(Int32, Int32)"/> 指定预留é‡ã€‚</para>
/// <para>与åŒå‚é‡è½½å¹¶å˜ï¼šå•å‚调用总是绑定到本é‡è½½ï¼ˆC# è§„åˆ™â€”â€”å…¨éƒ¨å‚æ•°å‡æœ‰å®žå‚者优于需è¦å¯é€‰å‚数填充者),
/// å› æ¤åŒå‚é‡è½½ä¸è®¾é»˜è®¤å€¼ï¼Œé¿å…“å•å‚调用åƒé»˜è®¤å€¼â€çš„éšæ€§è¯ä¹‰ã€‚</para>
/// </remarks>
public OwnerPacket(Int32 length) : this(length, 0) { }
/// <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;
_owner = hasOwner ? new ArrayOwner(buffer, true) : null;
}
/// <summary>å†…éƒ¨æž„é€ ï¼šèŠ‚ç‚¹ç›´æŽ¥æŒæœ‰ç¼“冲区与引用计数对象(引用计数已完æˆå¤„ç†ï¼‰</summary>
private OwnerPacket(Byte[] buffer, Int32 offset, Int32 length, ArrayOwner? owner)
{
_buffer = buffer;
_offset = offset;
_length = length;
_owner = owner;
}
/// <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._buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket), "Source packet has been disposed.");
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;
// 引用与链整体移交;æºå®žä¾‹ä½œåºŸï¼ˆå¼•用由新实例接管,ä¸é‡Šæ”¾ï¼‰
_owner = owner._owner;
DetachNode(owner, release: 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;
_owner = null;
return;
}
#endif
}
// ä»Žæ± é‡Œå€Ÿå—èŠ‚æ•°ç»„ï¼Œå˜æ”¾æ•°æ®æµæ‹·è´å‡ºæ¥çš„æ•°æ®
var size = (Int32)(stream.Length - stream.Position);
var buffer = ArrayPool<Byte>.Shared.Rent(reserve + size);
_buffer = buffer;
var count = stream.Read(buffer, reserve, size);
// æ•°æ®ä»Ž reserve 处开始写入,窗å£å¿…须从 reserve 开始:å¦åˆ™å¯¹å¤–å‰ reserve å—èŠ‚æ˜¯æ± åŒ–è„æ•°æ®ã€çœŸæ•°æ®è¢«æˆªæŽ‰
_offset = reserve;
_length = count;
_owner = new ArrayOwner(buffer, true);
// ç¡®ä¿æ•°æ®æµä½ç½®ä¸å˜
if (count > 0) stream.Seek(-count, SeekOrigin.Current);
}
#endregion
#region 内å˜ç®¡ç†
/// <summary>é‡Šæ”¾æœ¬å¥æŸ„æŒæœ‰çš„ç¼“å†²åŒºå¼•ç”¨ã€‚å¼•ç”¨è®¡æ•°å½’é›¶æ—¶å½’è¿˜æ± åŒ–ç¼“å†²åŒº</summary>
/// <remarks>
/// <para>Dispose 幂ç‰ï¼›å€Ÿç”¨è§†å›¾ï¼ˆä¸æŒæœ‰å¼•ç”¨ï¼‰è‡ªèº«æ— æ“ä½œï¼Œä½†åŒæ ·é‡Šæ”¾é“¾å¼åŽç»èŠ‚ç‚¹ã€‚</para>
/// <para>若缓冲区å˜åœ¨å…¶å®ƒå…±äº«å¥æŸ„,本调用ä¸ä¼šå½’è¿˜ç¼“å†²åŒºï¼Œå…¶å®ƒå¥æŸ„ä»å¯ç»§ç»ä½¿ç”¨ã€‚</para>
/// </remarks>
public void Dispose()
{
// 已显å¼é‡Šæ”¾ï¼ŒæŠ‘åˆ¶æžæž„兜底
GC.SuppressFinalize(this);
// 先摘除链å¼åŽç»èŠ‚ç‚¹ï¼šå€Ÿç”¨è§†å›¾å¤´ï¼ˆ_owner ä¸ºç©ºï¼‰åŒæ ·å¯èƒ½æŒ‚ç€æ‹¥æœ‰å¼•用的尾链
var next = Next;
Next = null;
var owner = _owner;
if (owner != null)
{
_owner = null;
_buffer = null; // 释放åŽç¦æ¢å†è¯»ï¼Œé˜²æ¢è¯¯ç”¨å·²å½’还的缓冲区
_length = 0;
owner.Release();
}
// 安全释放链å¼åŽç»èŠ‚ç‚¹
next.TryDispose();
}
#if DEBUG || OWNERPACKET_FINALIZER
/// <summary>æžæž„。兜底释放未 Dispose çš„å¥æŸ„引用,é¿å…æ± åŒ–ç¼“å†²åŒºæ°¸è¿œæ— æ³•å½’è¿˜</summary>
/// <remarks>
/// <para>默认仅在 DEBUG æž„å»ºï¼ˆå¼€å‘æœŸï¼‰ç¼–译本方法;å‘布版ä¸å«æžæž„,ä¸äº§ç”Ÿç»ˆç»“队列登记与终结器调度开销。如需生产兜底,å¯å®šä¹‰ç¼–è¯‘ç¬¦å· OWNERPACKET_FINALIZER å¼€å¯ã€‚</para>
/// <para>æ˜¾å¼ Dispose ä¼šæŠ‘åˆ¶æžæž„ï¼›åªæœ‰æ¼é‡Šæ”¾çš„奿Ÿ„æ‰ä¼šåœ¨ GC æ—¶èµ°æ¤è·¯å¾„,仅释放本节点自身引用。</para>
/// <para>链å¼åŽç»èŠ‚ç‚¹å„è‡ªç”±è‡ªèº«çš„æžæž„释放引用,ä¸åœ¨æ¤å¤„递归触碰,é¿å…终结器线程访问其它托管对象。</para>
/// </remarks>
~OwnerPacket()
{
var owner = _owner;
if (owner != null)
{
_owner = null;
owner.Release();
#if DEBUG
// 走到这里说明å˜åœ¨æ¼é‡Šæ”¾ï¼šå¼€å‘期直接告è¦ï¼Œä¾¿äºŽå®šä½ä¸Žä¿®å¤ï¼›ç»ˆç»“器线程内日志异常ä¸å¾—外抛
try
{
XTrace.WriteLine("[OwnerPacket] æ¼é‡Šæ”¾å¥æŸ„(Offset={0},Length={1}ï¼‰ï¼Œå·²ç”±æžæž„兜底归还缓冲区引用;请检查调用方是å¦é—æ¼ Dispose。", _offset, _length);
}
catch { }
#endif
}
}
#endif
/// <summary>è„±æ‰‹ï¼šæ”¾å¼ƒæœ¬å¥æŸ„的引用,但<strong>ä¸å½’还</strong>æ± åŒ–ç¼“å†²åŒºï¼Œç¼“å†²ä¿æŒâ€œå·²å€Ÿå‡ºâ€çжæ€äº¤ç»™è°ƒç”¨æ–¹ç»§ç»ä½¿ç”¨</summary>
/// <remarks>
/// <para>用于缓冲借用状æ€è½¬äº¤è°ƒç”¨æ–¹è‡ªç®¡çš„场景:脱手åŽç¼“å†²ä¿æŒâ€œå·²å€Ÿå‡ºâ€çжæ€ï¼Œå½’还责任éšè„±æ‰‹è½¬äº¤è°ƒç”¨æ–¹ï¼ˆç”±å…¶ç»§ç»ä½¿ç”¨æˆ–è‡ªè¡Œå½’è¿˜æ± ï¼‰ã€‚
/// 其余情况一律走 <see cref="Dispose"/>â€”â€”æŽ¥æ”¶çŽ¯çš„æ§½å¥æŸ„éšç¼“冲借出时创建,
/// æ—¥å¸¸è½®æœ«æŠŠå¥æŸ„回挂接收槽ã€ä¸‹ä¸€è½®ç”¨ <see cref="Resize"/> é‡è®¾çª—å£å¤ç”¨ï¼Œä¼šè¯å…³é—æ—¶é‡Šæ”¾æ§½å¥æŸ„å³å½’è¿˜ï¼ˆå¼•ç”¨å½’é›¶å›žæ± ï¼Œæœ‰å…±äº«åˆ™å‡æ•°ã€ç”±æœ€åŽä¸€ä¸ªæŒæœ‰è€…归还)。</para>
/// <para>æ•°æ®æ”¯æ’‘ï¼ˆåŸºå‡†å®žæµ‹ï¼‰ï¼šæ± åŒ– Rent+Return åˆè®¡çº¦ 8nsã€ä¸Žå¤§å°æ— 关,但接收环æ¯è½®å¿…ç»ï¼›
/// è½®æœ«å¥æŸ„回挂å¤ç”¨æŠŠâ€œå½’还+å†å€Ÿâ€çš„æˆå¯¹å¼€é”€çœä¸ºé›¶ï¼Œç¼“冲叏驻䏿¢æ–°ã€‚</para>
/// <para>与 <see cref="Dispose"/> 的区别:Dispose 释放引用并å¯èƒ½å½’è¿˜æ± ï¼›è„±æ‰‹åªåºŸå¼ƒå¥æŸ„,ä¿ç•™ç¼“冲的借用状æ€ã€‚</para>
/// <para>仿œ‰å…¶å®ƒæŒæœ‰è€…(<see cref="RefCount"/> 大于 1)时抛出异常:它们还在读这å—缓冲,脱手会让调用方误以为缓冲å¯ä»¥ç‹¬å å¤ç”¨ã€‚</para>
/// <para>调用åŽå®žä¾‹ä½œåºŸï¼Œé‡å¤è°ƒç”¨æ— æ“ä½œï¼›å€Ÿç”¨è§†å›¾ï¼ˆä¸æŒæœ‰å¼•ç”¨ï¼‰è°ƒç”¨æ— æ“ä½œã€‚ä»…ç”¨äºŽæ— é“¾å¼åŽç»èŠ‚ç‚¹çš„ç‹¬å 奿Ÿ„。</para>
/// </remarks>
/// <exception cref="InvalidOperationException">仿œ‰å…¶å®ƒå¥æŸ„æŒæœ‰ç¼“冲区</exception>
public void Detach()
{
var owner = _owner;
if (owner == null) return;
// è¿˜æœ‰åˆ«çš„å¥æŸ„在用这å—缓冲时ä¸èƒ½è„±æ‰‹ï¼šå®ƒä»¬é‡Šæ”¾åŽç¼“å†²ä¼šå½’è¿˜æ± ï¼Œè€Œè°ƒç”¨æ–¹è¿˜åœ¨ç”¨åŒä¸€å—缓冲
if (owner.RefCount > 1)
throw new InvalidOperationException($"Cannot detach while {owner.RefCount - 1} other handle(s) still hold the buffer.");
// 链å¼åŽç»èŠ‚ç‚¹ä¼šåœ¨æœ¬å¥æŸ„脱手时丢失引用(链上缓å˜çš„归还责任éšä¹‹æ¶ˆå¤±ï¼‰ï¼Œè°ƒç”¨æ–¹ç¡®è®¤æ— 链æ‰å¯è„±æ‰‹
if (Next != null) throw new InvalidOperationException("Cannot detach while the packet has a Next segment; dispose the chain first.");
// æŠ‘åˆ¶æžæž„å…œåº•ï¼Œé˜²æ¢ GC 时误把ä»åœ¨å€Ÿç”¨ä¸çš„缓冲归还æ±
GC.SuppressFinalize(this);
_owner = null;
_buffer = null;
_length = 0;
Next = null;
}
#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>
/// <para>接收层å¤ç”¨ï¼šä¼šè¯æŠŠè½®æœ«æ— å¤–éƒ¨æŒæœ‰çš„è½®å¥æŸ„回挂接收槽,下一轮用本方法把窗å£é‡è®¾åˆ°æ–°æ”¶åˆ°çš„æ•°æ®æ®µï¼Œ
/// å¤ç”¨åŒä¸€ä¸ªæ‹¥æœ‰å¥æŸ„(缓冲与引用计数对象都ä¸é‡å»ºï¼‰ï¼ŒæŽ¥æ”¶çŽ¯å› æ¤åšåˆ°æ¯è½®é›¶åˆ†é…。</para>
/// </remarks>
public OwnerPacket Resize(Int32 size)
{
if (size < 0) throw new ArgumentOutOfRangeException(nameof(size), "Size must be non-negative.");
if (Next == null)
{
// ä¸Šé™æŒ‰â€œä»Žå½“å‰å移起å¯ç”¨å®¹é‡â€ç®—:带å移的视图若按整个缓冲区长度放行,
// åŽç» GetSpan/GetMemory 会越窗(offset+length 超出数组长度)
if (size > Buffer.Length - _offset)
throw new ArgumentOutOfRangeException(nameof(size),
$"Size {size} exceeds available space {Buffer.Length - _offset}");
_length = size;
}
else
{
if (size >= _length)
throw new NotSupportedException("Cannot increase size when Next segment exists");
_length = size;
}
return this;
}
/// <summary>原地å‰ç§»æ•°æ®çª—å£ï¼Œä¸¢å¼ƒå¼€å¤´ len å—节</summary>
/// <param name="len">å‰ç§»å—节数,ä¸èƒ½è¶…è¿‡å½“å‰æ®µé•¿åº¦</param>
/// <returns>当å‰å®žä¾‹ï¼Œæ”¯æŒé“¾å¼è°ƒç”¨</returns>
/// <exception cref="ArgumentOutOfRangeException">å‰ç§»é•¿åº¦è¶…å‡ºå½“å‰æ®µé•¿åº¦</exception>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
/// <remarks>
/// <para>用于拆帧残片å‰ç§»ç‰åœºæ™¯ï¼Œä¸æ‹·è´ã€ä¸åˆ†é…ï¼Œåªæœ‰çª—壿Œ‡é’ˆè°ƒæ•´çš„å¾®å°å¼€é”€ã€‚</para>
/// <para>åªå½±å“æœ¬å¥æŸ„的窗å£è§†å›¾ï¼Œå…±äº«åŒä¸€åº•å±‚ç¼“å†²åŒºçš„å…¶å®ƒå¥æŸ„视图ä¸å—å½±å“;仅对独å 奿Ÿ„使用。</para>
/// </remarks>
public OwnerPacket Skip(Int32 len)
{
if (_buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
if (len < 0 || len > _length) throw new ArgumentOutOfRangeException(nameof(len), $"Skip {len} exceeds current length {_length}");
_offset += len;
_length -= len;
return this;
}
#endregion
#region 切片æ“作
/// <summary>切片得到新数æ®åŒ…(三å‚é‡è½½ï¼Œä»…为兼容旧二进制ä¿ç•™ï¼‰</summary>
/// <param name="offset">相对当å‰åŒ…èµ·å§‹åç§»</param>
/// <param name="count">个数。默认 -1 表示到末尾</param>
/// <param name="transferOwner">是å¦è½¬ç§»æ‰€æœ‰æƒã€‚<b>已忽略</b></param>
/// <returns>å…±äº«åº•å±‚ç¼“å†²åŒºçš„æ‹¥æœ‰å¥æŸ„,请使用 <c>using</c> 释放</returns>
/// <remarks>切片è¯ä¹‰å·²ç»Ÿä¸€ä¸ºå¼•用计数共享,<paramref name="transferOwner"/> 傿•°è¢«å¿½ç•¥ï¼›è¯¦è§ <see cref="IPacket.Slice(Int32, Int32, Boolean)"/>。</remarks>
[Obsolete("请改用 Slice(offset, count);切片已统一为引用计数共享è¯ä¹‰")]
public IPacket Slice(Int32 offset, Int32 count, Boolean transferOwner) => Slice(offset, count);
/// <summary>åˆ‡ç‰‡ç”Ÿæˆæ–°æ•°æ®åŒ…ï¼Œå…±äº«åº•å±‚ç¼“å†²åŒºï¼ˆå¼•ç”¨è®¡æ•°ï¼‰ã€‚è¿”å›žå…·ä½“ç±»åž‹å¥æŸ„,å¯ç›´æŽ¥ <c>using</c> 释放</summary>
/// <param name="offset">相对当å‰åŒ…的起始åç§»</param>
/// <param name="count">切片长度,-1 表示到末尾</param>
/// <returns>æ–°çš„ç‹¬ç«‹å¥æŸ„(具体类型 <see cref="OwnerPacket"/>ï¼‰ï¼Œä¸ŽåŽŸåŒ…åŒæ—¶å¯ç”¨ï¼›å„自 Dispose,最åŽä¸€ä¸ªé‡Šæ”¾æ—¶å½’è¿˜å†…å˜æ± </returns>
/// <exception cref="ArgumentOutOfRangeException">åç§»é‡æˆ–长度超出有效范围</exception>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
/// <remarks>
/// <para>åˆ‡ç‰‡å…±äº«åº•å±‚ç¼“å†²åŒºï¼Œä¸æ‹·è´æ•°æ®ã€‚支æŒè·¨æ®µåˆ‡ç‰‡ï¼Œè‡ªåЍ处ç†è¾¹ç•Œï¼›çª—å£è¦†ç›–的段å„递增一次引用计数。</para>
/// <para>æˆæœ¬ï¼ˆåŸºå‡†å®žæµ‹ï¼‰ï¼šå…±äº«åˆ‡ç‰‡åŒæ–¹åˆè®¡çº¦ 42ns,与数æ®å¤§å°æ— 关;≥4KB 帧优于拷è´ï¼ˆ64KB æ‹·è´çº¦ 1.05µs),
/// ≤512B å°å¸§ç›´æŽ¥æ‹·è´æ›´ä¾¿å®œâ€”—å°å¸§ä¸è¦ä¸ºå…±äº«è€Œå…±äº«ã€‚</para>
/// <para>æœ¬æ–¹æ³•ä¸æ”¹å˜åŽŸå®žä¾‹ï¼ŒåŒæ–¹ï¼ˆæˆ–多方)å‡å¯ç»§ç»è¯»å–ï¼›æ¯ä¸ªå¥æŸ„å„自负责 <see cref="Dispose"/>。
/// çº¿æ€§äº¤æŽ¥åœºæ™¯ç›´æŽ¥ä¼ é€’å¥æŸ„本身å³å¯ï¼Œæ— 需切片。</para>
/// <para><see cref="IPacket.Slice(Int32, Int32)"/> 与 <see cref="IOwnerPacket.Slice(Int32, Int32)"/> æ˜¯æœ¬æ–¹æ³•çš„æ˜¾å¼æŽ¥å£å®žçŽ°ï¼Œç»æŽ¥å£è®¿é—®æ—¶åˆ†åˆ«è¿”回å„自声明类型。</para>
/// </remarks>
public OwnerPacket Slice(Int32 offset, Int32 count = -1)
{
if (_buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be negative.");
var total = Total;
if (count > total - offset)
throw new ArgumentOutOfRangeException(nameof(count),
$"Count {count} with offset {offset} exceeds total length {total}");
if (count < 0) count = total - offset;
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(offset), $"Offset {offset} exceeds total length {total}");
OwnerPacket? head = null;
OwnerPacket? tail = null;
var skip = offset;
var take = count;
IPacket? node = this;
while (node != null && skip >= node.Length)
{
skip -= node.Length;
node = node.Next;
}
while (node != null && take > 0)
{
var next = node.Next;
var startInNode = skip;
var takeInNode = Math.Min(node.Length - startInNode, take);
skip = 0;
var item = BuildShareNode(node, startInNode, takeInNode);
if (head == null)
head = item;
else
tail!.Next = item;
tail = item;
take -= takeInNode;
node = next;
}
return head ?? new OwnerPacket(_empty, 0, 0, null);
}
/// <summary>切片得到新数æ®åŒ…(<see cref="IPacket"/> 接å£å®žçŽ°ï¼‰ï¼Œå…±äº«åº•å±‚ç¼“å†²åŒº</summary>
/// <param name="offset">相对当å‰åŒ…的起始åç§»</param>
/// <param name="count">切片长度,-1 表示到末尾</param>
/// <returns>æ–°çš„ç‹¬ç«‹å¥æŸ„</returns>
IPacket IPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
/// <summary>切片得到新数æ®åŒ…ï¼ˆæ‹¥æœ‰å¥æŸ„接å£å®žçŽ°ï¼‰ï¼Œå…±äº«åº•å±‚ç¼“å†²åŒºã€‚è¿”å›žæ‹¥æœ‰å¥æŸ„ï¼Œå¯ <c>using</c> 释放</summary>
/// <param name="offset">相对当å‰åŒ…的起始åç§»</param>
/// <param name="count">切片长度,-1 表示到末尾</param>
/// <returns>æ–°çš„ç‹¬ç«‹æ‹¥æœ‰å¥æŸ„</returns>
/// <remarks>与公开方法åŒä¸€å®žçŽ°ï¼Œåˆ‡ç‰‡ç»“æžœå§‹ç»ˆä¸º <see cref="OwnerPacket"/> 奿Ÿ„。</remarks>
IOwnerPacket IOwnerPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
/// <summary>å‘剿‰©å±•头部空间(借ä½å…±äº«ï¼‰ã€‚è¦æ±‚åˆ†é…æ—¶å·²é¢„留足够头部空间</summary>
/// <param name="size">å‘剿‰©å±•çš„å—节数</param>
/// <returns>扩展åŽçš„æ–°å¥æŸ„,调用方负责 Dispose</returns>
/// <remarks>
/// <para>é›¶æ‹·è´å€Ÿä½ï¼šæ–°å¥æŸ„ä¸ŽåŽŸå¥æŸ„共享åŒä¸€ç¼“冲区(引用计数å„自释放),<b>åŽŸå¥æŸ„ä¿æŒæœ‰æ•ˆ</b>,å¯ç»§ç»è¯»å–æˆ–å†æ¬¡æž„建。</para>
/// <para>另链奿Ÿ„会先切片为独å 的共享节点链(节点å„è‡ªæŒæœ‰å¼•用)å†å‰ç§»é“¾å¤´ï¼Œæºå¥æŸ„便—§ä¸å—å½±å“。</para>
/// </remarks>
/// <exception cref="ObjectDisposedException">实例已释放</exception>
/// <exception cref="InvalidOperationException">å‰ç½®é¢„留空间ä¸è¶³</exception>
public OwnerPacket ExpandHeader(Int32 size)
{
if (_buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
if (_offset < size) throw new InvalidOperationException($"未预留 {size} å—èŠ‚å¤´éƒ¨ç©ºé—´ï¼ˆå½“å‰ {_offset}ï¼‰ï¼›è¯·åœ¨åˆ†é…æ—¶é¢„留,例如 new OwnerPacket(size, reserve)");
// 带链:先切片æˆç‹¬å 的共享节点链(引用计数å„è‡ªæŒæœ‰ï¼‰ï¼Œé“¾å¤´éšå³å¯å®‰å…¨å‰ç§»ï¼›æºå¥æŸ„ä¸å—å½±å“
if (Next != null)
{
// 首节点为空时切片会跳过它,预留区(挂在首节点上)éšä¹‹ä¸¢å¤±å¹¶è®©é“¾å¤´çª—å£é”™ä½å‰ç§»ï¼Œæ¤å¤„ä¿ç•™é¦–节点自身å‰ç§»çª—å£
if (_length == 0)
{
var chain = Slice(0, -1);
var own = _owner;
own?.AddRef();
return new OwnerPacket(_buffer, _offset - size, size, own) { Next = chain };
}
var head = Slice(0, -1);
head._offset -= size;
head._length += size;
return head;
}
// å•节点:共享借ä½ï¼ŒåŽŸå¥æŸ„ä¿æŒæœ‰æ•ˆ
var owner = _owner;
owner?.AddRef();
return new OwnerPacket(_buffer, _offset - size, _length + size, owner);
}
#endregion
#region 切片辅助
/// <summary>ä¸ºå…±äº«çª—å£æž„建新节点,对æºèŠ‚ç‚¹æŒæœ‰å¼•用的段递增引用计数</summary>
private static OwnerPacket BuildShareNode(IPacket source, Int32 offset, Int32 count)
{
if (source is OwnerPacket op)
{
if (op._buffer == null) throw new ObjectDisposedException(nameof(OwnerPacket));
var owner = op._owner;
owner?.AddRef();
return new OwnerPacket(op._buffer, op._offset + offset, count, owner);
}
if (source.TryGetArray(out var segment))
return new OwnerPacket(segment.Array!, segment.Offset + offset, count, null);
throw new NotSupportedException($"Cannot share chain node of type {source.GetType().Name}");
}
/// <summary>释放节点引用并作废节点(链å¼é历用)。release 为 false 表示引用已移交给接管方</summary>
private static void DetachNode(IPacket node, Boolean release)
{
if (node is not OwnerPacket op) return;
var owner = op._owner;
op._owner = null;
if (release && owner != null) owner.Release();
op._buffer = null;
op._length = 0;
op.Next = null;
}
#endregion
#region å—符串表示
/// <summary>返回数æ®åŒ…çš„å—符串表示形å¼</summary>
/// <returns>包å«ç¼“冲区大å°ã€åç§»é‡ã€é•¿åº¦å’Œæ€»é•¿åº¦çš„æ ¼å¼åŒ–å—符串</returns>
public override String ToString() => $"OwnerPacket[{_buffer?.Length ?? 0}]({_offset}, {_length})<{Total}>";
#endregion
}
|