7
7
namespace NewLife . Buffers ;
8
8
9
9
/// <summary>Span读取器</summary>
10
+ /// <remarks>
11
+ /// 引用结构的Span读取器确保高性能无GC读取。
12
+ /// 支持Stream扩展,当数据不足时,自动从数据流中读取,常用于解析Redis/MySql等协议。
13
+ /// </remarks>
10
14
public ref struct SpanReader
11
15
{
12
16
#region 属性
13
- private readonly ReadOnlySpan < Byte > _span ;
17
+ private ReadOnlySpan < Byte > _span ;
14
18
/// <summary>数据片段</summary>
15
19
public ReadOnlySpan < Byte > Span => _span ;
16
20
@@ -39,7 +43,43 @@ public ref struct SpanReader
39
43
40
44
/// <summary>实例化Span读取器</summary>
41
45
/// <param name="data"></param>
42
- public SpanReader ( IPacket data ) : this ( data . GetSpan ( ) ) { }
46
+ public SpanReader ( IPacket data )
47
+ {
48
+ _data = data ;
49
+ _span = data . GetSpan ( ) ;
50
+ _total = data . Total ;
51
+ }
52
+ #endregion
53
+
54
+ #region 扩容增强
55
+ /// <summary>最大容量。多次从数据流读取数据时,受限于此最大值</summary>
56
+ public Int32 MaxCapacity { get ; set ; }
57
+
58
+ private readonly Stream ? _stream ;
59
+ private readonly Int32 _bufferSize ;
60
+ private IPacket ? _data ;
61
+ private Int32 _total ;
62
+
63
+ /// <summary>实例化Span读取器。支持从数据流中读取更多数据,突破大小限制</summary>
64
+ /// <remarks>
65
+ /// 解析网络协议时,有时候数据帧较大,超过特定缓冲区大小,导致无法一次性读取完整数据帧。
66
+ /// 加入数据流参数后,在读取数据不足时,SpanReader会自动从数据流中读取一批数据。
67
+ /// </remarks>
68
+ /// <param name="stream">数据流。一般是网络流</param>
69
+ /// <param name="data"></param>
70
+ /// <param name="bufferSize"></param>
71
+ public SpanReader ( Stream stream , IPacket ? data = null , Int32 bufferSize = 8192 )
72
+ {
73
+ _stream = stream ;
74
+ _bufferSize = bufferSize ;
75
+
76
+ if ( data != null )
77
+ {
78
+ _data = data ;
79
+ _span = data . GetSpan ( ) ;
80
+ _total = data . Total ;
81
+ }
82
+ }
43
83
#endregion
44
84
45
85
#region 基础方法
@@ -69,8 +109,49 @@ public ReadOnlySpan<Byte> GetSpan(Int32 sizeHint = 0)
69
109
/// <summary>确保缓冲区中有足够的空间。</summary>
70
110
/// <param name="size">需要的字节数。</param>
71
111
/// <exception cref="InvalidOperationException"></exception>
72
- private void EnsureSpace ( Int32 size )
112
+ public void EnsureSpace ( Int32 size )
73
113
{
114
+ // 检查剩余空间大小,不足时,再从数据流中读取。此时需要注意,创建新的OwnerPacket后,需要先把之前剩余的一点数据拷贝过去,然后再读取Stream
115
+ var remain = FreeCapacity ;
116
+ if ( remain < size && _stream != null )
117
+ {
118
+ // 申请指定大小的数据包缓冲区,至少达到缓冲区大小,但不超过最大容量
119
+ var idx = 0 ;
120
+ var bsize = size ;
121
+ if ( bsize < _bufferSize ) bsize = _bufferSize ;
122
+ if ( MaxCapacity > 0 && bsize > MaxCapacity - _total ) bsize = MaxCapacity - _total ;
123
+ var pk = new OwnerPacket ( bsize ) ;
124
+ if ( _data != null && remain > 0 )
125
+ {
126
+ if ( ! _data . TryGetArray ( out var arr ) ) throw new NotSupportedException ( ) ;
127
+
128
+ arr . AsSpan ( _index , remain ) . CopyTo ( pk . Buffer ) ;
129
+ idx += remain ;
130
+ }
131
+
132
+ _data . TryDispose ( ) ;
133
+ _data = pk ;
134
+ _index = 0 ;
135
+
136
+ // 多次读取,直到满足需求
137
+ //var n = _stream.ReadExactly(pk.Buffer, pk.Offset + idx, pk.Length - idx);
138
+ while ( idx < size )
139
+ {
140
+ // 实际缓冲区大小可能大于申请大小,充分利用缓冲区,避免多次读取
141
+ var len = pk . Buffer . Length - pk . Offset ;
142
+ var n = _stream . Read ( pk . Buffer , pk . Offset + idx , len - idx ) ;
143
+ if ( n <= 0 ) break ;
144
+
145
+ idx += n ;
146
+ }
147
+ if ( idx < size )
148
+ throw new InvalidOperationException ( "Not enough data to read." ) ;
149
+ pk . Resize ( idx ) ;
150
+
151
+ _span = pk . GetSpan ( ) ;
152
+ _total += idx - remain ;
153
+ }
154
+
74
155
if ( _index + size > _span . Length )
75
156
throw new InvalidOperationException ( "Not enough data to read." ) ;
76
157
}
@@ -213,7 +294,6 @@ public String ReadString(Int32 length = 0, Encoding? encoding = null)
213
294
/// <summary>读取字节数组</summary>
214
295
/// <param name="length"></param>
215
296
/// <returns></returns>
216
- /// <exception cref="InvalidOperationException"></exception>
217
297
public ReadOnlySpan < Byte > ReadBytes ( Int32 length )
218
298
{
219
299
EnsureSpace ( length ) ;
@@ -223,6 +303,34 @@ public ReadOnlySpan<Byte> ReadBytes(Int32 length)
223
303
return result ;
224
304
}
225
305
306
+ /// <summary>读取字节数组</summary>
307
+ /// <param name="data"></param>
308
+ /// <returns></returns>
309
+ public Int32 Read ( Span < Byte > data )
310
+ {
311
+ var length = data . Length ;
312
+ EnsureSpace ( length ) ;
313
+
314
+ var result = _span . Slice ( _index , length ) ;
315
+ result . CopyTo ( data ) ;
316
+ _index += length ;
317
+ return length ;
318
+ }
319
+
320
+ /// <summary>读取数据包。直接对内部数据包进行切片</summary>
321
+ /// <param name="length"></param>
322
+ /// <returns></returns>
323
+ public IPacket ReadPacket ( Int32 length )
324
+ {
325
+ if ( _data == null ) throw new InvalidOperationException ( "No data stream to read!" ) ;
326
+
327
+ //EnsureSpace(length);
328
+
329
+ var result = _data . Slice ( _index , length ) ;
330
+ _index += length ;
331
+ return result ;
332
+ }
333
+
226
334
/// <summary>读取结构体</summary>
227
335
/// <typeparam name="T"></typeparam>
228
336
/// <returns></returns>
0 commit comments