11using Cysharp . Threading . Tasks ;
22using Insthync . SimpleNetworkManager . NET . Messages ;
33using System ;
4+ using System . Buffers ;
45using System . IO ;
56using System . Threading ;
67
@@ -11,134 +12,134 @@ public static class StreamExtensions
1112 /// <summary>
1213 /// Reads exactly the specified number of bytes from the stream
1314 /// </summary>
14- public static async UniTask < int > ReadExactAsync ( this Stream stream , byte [ ] buffer , int count , CancellationToken cancellationToken , int offset = 0 )
15+ public static async UniTask < int > ReadExactAsync ( this Stream stream , Memory < byte > buffer , int count , CancellationToken cancellationToken )
1516 {
16- if ( stream == null )
17- return 0 ;
18-
1917 int totalBytesRead = 0 ;
2018 while ( totalBytesRead < count && ! cancellationToken . IsCancellationRequested )
2119 {
22- var bytesRead = await stream . ReadAsync (
23- buffer , offset + totalBytesRead , count - totalBytesRead , cancellationToken ) ;
24-
20+ var slice = buffer . Slice ( totalBytesRead , count - totalBytesRead ) ;
21+ int bytesRead = await stream . ReadAsync ( slice , cancellationToken ) ;
2522 if ( bytesRead == 0 )
2623 break ; // Connection closed
27-
2824 totalBytesRead += bytesRead ;
2925 }
3026 return totalBytesRead ;
3127 }
3228
3329 /// <summary>
34- /// Read message from the stream
30+ /// Read message from the stream, message buffer should be returned to array pool after use
3531 /// </summary>
3632 /// <param name="stream"></param>
3733 /// <param name="cancellationToken"></param>
3834 /// <returns></returns>
3935 /// <exception cref="InvalidMessageSizeException"></exception>
40- public static async UniTask < byte [ ] ? > ReadMessageAsync ( this Stream stream , CancellationToken cancellationToken )
36+ public static async UniTask < ( byte [ ] buffer , int length ) ? > ReadMessageAsync ( this Stream stream , CancellationToken cancellationToken )
4137 {
4238 if ( stream == null )
4339 return null ;
4440
45- // Read message size (4 bytes)
46- var sizeBuffer = new byte [ 4 ] ;
47- var bytesRead = await stream . ReadExactAsync ( sizeBuffer , 4 , cancellationToken ) ;
48- if ( bytesRead == 0 || bytesRead < 4 )
49- return null ; // Connection closed
50-
51- var dataSize = BitConverter . ToInt32 ( sizeBuffer , 0 ) ;
52- int minSize = 8 ;
53- int maxSize = 1024 * 1024 ; // 1 MB
54- if ( dataSize < minSize || dataSize > maxSize )
41+ byte [ ] sizeBuffer = ArrayPool < byte > . Shared . Rent ( 4 ) ;
42+ try
5543 {
56- throw new InvalidMessageSizeException ( )
44+ var sizeMemory = sizeBuffer . AsMemory ( 0 , 4 ) ;
45+ int bytesRead = await stream . ReadExactAsync ( sizeMemory , 4 , cancellationToken ) ;
46+ if ( bytesRead < 4 )
47+ return null ;
48+
49+ int dataSize = BitConverter . ToInt32 ( sizeBuffer , 0 ) ;
50+ int minSize = 8 ;
51+ int maxSize = 1024 * 1024 ; // 1 MB limit
52+ if ( dataSize < minSize || dataSize > maxSize )
53+ throw new InvalidMessageSizeException ( ) { Size = dataSize , MinSize = minSize , MaxSize = maxSize } ;
54+
55+ // Rent buffer for the full message
56+ byte [ ] dataBuffer = ArrayPool < byte > . Shared . Rent ( dataSize ) ;
57+ Memory < byte > dataMemory = dataBuffer . AsMemory ( 0 , dataSize ) ;
58+
59+ // Copy size header into data buffer
60+ sizeMemory . Span . CopyTo ( dataMemory . Span ) ;
61+
62+ // Read the rest of the message body directly into the span
63+ int remainingBytes = dataSize - 4 ;
64+ bytesRead = await stream . ReadExactAsync ( dataMemory . Slice ( 4 , remainingBytes ) , remainingBytes , cancellationToken ) ;
65+ if ( bytesRead != remainingBytes )
5766 {
58- Size = dataSize ,
59- MinSize = minSize ,
60- MaxSize = maxSize
61- } ;
62- }
67+ ArrayPool < byte > . Shared . Return ( dataBuffer ) ;
68+ return null ; // Connection closed
69+ }
6370
64- // Read the complete message (including the size we already read)
65- var dataBuffer = new byte [ dataSize ] ;
66- sizeBuffer . CopyTo ( dataBuffer , 0 ) ;
67-
68- // Already read 4 bytes for message size, so decrease by 4
69- var remainingBytes = dataSize - 4 ;
70- // Read next bytes by remaining bytes, skip 4 bytes (message size which already copied above)
71- bytesRead = await stream . ReadExactAsync ( dataBuffer , remainingBytes , cancellationToken , 4 ) ;
72- if ( bytesRead != remainingBytes )
73- return null ; // Connection closed
74-
75- return dataBuffer ;
71+ return ( dataBuffer , dataSize ) ;
72+ }
73+ finally
74+ {
75+ ArrayPool < byte > . Shared . Return ( sizeBuffer ) ;
76+ }
7677 }
7778
7879 /// <summary>
7980 /// Reads exactly the specified number of bytes from the stream
8081 /// </summary>
81- public static int ReadExact ( this Stream stream , byte [ ] buffer , int count , int offset = 0 )
82+ public static int ReadExact ( this Stream stream , Span < byte > buffer , int count , CancellationToken cancellationToken )
8283 {
83- if ( stream == null )
84- return 0 ;
85-
8684 int totalBytesRead = 0 ;
87- while ( totalBytesRead < count )
85+ while ( totalBytesRead < count && ! cancellationToken . IsCancellationRequested )
8886 {
89- var bytesRead = stream . Read (
90- buffer , offset + totalBytesRead , count - totalBytesRead ) ;
91-
87+ int bytesRead = stream . Read ( buffer . Slice ( totalBytesRead , count - totalBytesRead ) ) ;
9288 if ( bytesRead == 0 )
9389 break ; // Connection closed
94-
9590 totalBytesRead += bytesRead ;
9691 }
9792 return totalBytesRead ;
9893 }
9994
10095 /// <summary>
101- /// Read message from the stream
96+ /// Read message from the stream, message buffer should be returned to array pool after use
10297 /// </summary>
10398 /// <param name="stream"></param>
99+ /// <param name="cancellationToken"></param>
104100 /// <returns></returns>
105101 /// <exception cref="InvalidMessageSizeException"></exception>
106- public static byte [ ] ? ReadMessage ( this Stream stream )
102+ public static ( byte [ ] buffer , int length ) ? ReadMessage ( this Stream stream , CancellationToken cancellationToken )
107103 {
108104 if ( stream == null )
109105 return null ;
110106
111- // Read message size (4 bytes)
112- var sizeBuffer = new byte [ 4 ] ;
113- var bytesRead = stream . ReadExact ( sizeBuffer , 4 ) ;
114- if ( bytesRead == 0 || bytesRead < 4 )
115- return null ; // Connection closed
116-
117- var dataSize = BitConverter . ToInt32 ( sizeBuffer , 0 ) ;
118- int minSize = 8 ;
119- int maxSize = 1024 * 1024 ; // 1 MB
120- if ( dataSize < minSize || dataSize > maxSize )
107+ byte [ ] sizeBuffer = ArrayPool < byte > . Shared . Rent ( 4 ) ;
108+ try
121109 {
122- throw new InvalidMessageSizeException ( )
110+ Span < byte > sizeSpan = sizeBuffer . AsSpan ( 0 , 4 ) ;
111+ int bytesRead = stream . ReadExact ( sizeSpan , 4 , cancellationToken ) ;
112+ if ( bytesRead < 4 )
113+ return null ; // Connection closed
114+
115+ int dataSize = BitConverter . ToInt32 ( sizeBuffer , 0 ) ;
116+ const int minSize = 8 ;
117+ const int maxSize = 1024 * 1024 ; // 1 MB limit
118+ if ( dataSize < minSize || dataSize > maxSize )
119+ throw new InvalidMessageSizeException ( ) { Size = dataSize , MinSize = minSize , MaxSize = maxSize } ;
120+
121+ // Rent buffer for the full message
122+ byte [ ] dataBuffer = ArrayPool < byte > . Shared . Rent ( dataSize ) ;
123+ Span < byte > dataSpan = dataBuffer . AsSpan ( 0 , dataSize ) ;
124+
125+ // Copy size header into data buffer
126+ sizeSpan . CopyTo ( dataSpan ) ;
127+
128+ // Read the rest of the message body directly into the span
129+ int remainingBytes = dataSize - 4 ;
130+ bytesRead = stream . ReadExact ( dataSpan . Slice ( 4 , remainingBytes ) , remainingBytes , cancellationToken ) ;
131+ if ( bytesRead != remainingBytes )
123132 {
124- Size = dataSize ,
125- MinSize = minSize ,
126- MaxSize = maxSize
127- } ;
128- }
129-
130- // Read the complete message (including the size we already read)
131- var dataBuffer = new byte [ dataSize ] ;
132- sizeBuffer . CopyTo ( dataBuffer , 0 ) ;
133-
134- // Already read 4 bytes for message size, so decrease by 4
135- var remainingBytes = dataSize - 4 ;
136- // Read next bytes by remaining bytes, skip 4 bytes (message size which already copied above)
137- bytesRead = stream . ReadExact ( dataBuffer , remainingBytes , 4 ) ;
138- if ( bytesRead != remainingBytes )
139- return null ; // Connection closed
133+ ArrayPool < byte > . Shared . Return ( dataBuffer ) ;
134+ return null ; // Connection closed
135+ }
140136
141- return dataBuffer ;
137+ return ( dataBuffer , dataSize ) ;
138+ }
139+ finally
140+ {
141+ ArrayPool < byte > . Shared . Return ( sizeBuffer ) ;
142+ }
142143 }
143144
144145 public static async UniTask WriteMessageAsync < T > ( this Stream stream , T message , CancellationToken cancellationToken )
0 commit comments