11using System ;
2+ using System . Buffers ;
23using System . Buffers . Binary ;
34using System . Diagnostics ;
45using System . Globalization ;
@@ -59,28 +60,57 @@ public bool IsEndOfData
5960 }
6061 }
6162
62- #if ! NET
63- private void Write ( ReadOnlySpan < byte > buffer )
63+ // Because this type derives from MemoryStream, the base Write(ReadOnlySpan) chooses
64+ // to rent an array, copy the data in and delegate to Write(byte[], int, int) for
65+ // backwards compatibility.
66+ // With a bit of extra ceremony, we can instead allow the various Write methods here
67+ // to write directly into the underlying buffer without the need for any intermediate
68+ // arrays (rented or otherwise).
69+
70+ #if NET9_0_OR_GREATER
71+ /// <inheritdoc/>
72+ public override void Write ( ReadOnlySpan < byte > buffer )
6473 {
65- var sharedBuffer = System . Buffers . ArrayPool < byte > . Shared . Rent ( buffer . Length ) ;
74+ Write ( buffer , buffer . Length , static ( span , buffer ) => buffer . CopyTo ( span ) ) ;
75+ }
76+ #endif
6677
67- buffer . CopyTo ( sharedBuffer ) ;
78+ private delegate void WriteAction < in TArg > ( Span < byte > span , TArg arg )
79+ #if NET9_0_OR_GREATER
80+ where TArg : allows ref struct
81+ #endif
82+ ;
6883
69- Write ( sharedBuffer , 0 , buffer . Length ) ;
84+ private void Write < TArg > ( TArg arg , int numBytesToWrite , WriteAction < TArg > writeAction )
85+ #if NET9_0_OR_GREATER
86+ where TArg : allows ref struct
87+ #endif
88+ {
89+ var endPosition = Position + numBytesToWrite ;
7090
71- System . Buffers . ArrayPool < byte > . Shared . Return ( sharedBuffer ) ;
91+ if ( Capacity < endPosition )
92+ {
93+ var newCapacity = Math . Max ( endPosition , Math . Min ( 2 * ( uint ) Capacity , Array . MaxLength ) ) ;
94+ Capacity = checked ( ( int ) newCapacity ) ;
95+ }
96+
97+ if ( endPosition > Length )
98+ {
99+ SetLength ( endPosition ) ;
100+ }
101+
102+ writeAction ( GetRemainingBuffer ( ) . AsSpan ( 0 , numBytesToWrite ) , arg ) ;
103+
104+ Position = endPosition ;
72105 }
73- #endif
74106
75107 /// <summary>
76108 /// Writes an <see cref="uint"/> to the SSH data stream.
77109 /// </summary>
78110 /// <param name="value"><see cref="uint"/> data to write.</param>
79111 public void Write ( uint value )
80112 {
81- Span < byte > bytes = stackalloc byte [ 4 ] ;
82- BinaryPrimitives . WriteUInt32BigEndian ( bytes , value ) ;
83- Write ( bytes ) ;
113+ Write ( value , 4 , static ( span , value ) => BinaryPrimitives . WriteUInt32BigEndian ( span , value ) ) ;
84114 }
85115
86116 /// <summary>
@@ -89,9 +119,7 @@ public void Write(uint value)
89119 /// <param name="value"><see cref="ulong"/> data to write.</param>
90120 public void Write ( ulong value )
91121 {
92- Span < byte > bytes = stackalloc byte [ 8 ] ;
93- BinaryPrimitives . WriteUInt64BigEndian ( bytes , value ) ;
94- Write ( bytes ) ;
122+ Write ( value , 8 , static ( span , value ) => BinaryPrimitives . WriteUInt64BigEndian ( span , value ) ) ;
95123 }
96124
97125 /// <summary>
@@ -100,9 +128,22 @@ public void Write(ulong value)
100128 /// <param name="data">The <see cref="BigInteger" /> to write.</param>
101129 public void Write ( BigInteger data )
102130 {
131+ #if NET
132+ var byteCount = data . GetByteCount ( ) ;
133+
134+ Write ( ( data , byteCount ) , 4 + byteCount , static ( span , args ) =>
135+ {
136+ BinaryPrimitives . WriteUInt32BigEndian ( span , ( uint ) args . byteCount ) ;
137+
138+ var success = args . data . TryWriteBytes ( span . Slice ( 4 ) , out var bytesWritten , isBigEndian : true ) ;
139+
140+ Debug . Assert ( success && bytesWritten == span . Length - 4 ) ;
141+ } ) ;
142+ #else
103143 var bytes = data . ToByteArray ( isBigEndian : true ) ;
104144
105145 WriteBinary ( bytes , 0 , bytes . Length ) ;
146+ #endif
106147 }
107148
108149 /// <summary>
@@ -129,16 +170,26 @@ public void Write(string s, Encoding encoding)
129170 ArgumentNullException . ThrowIfNull ( s ) ;
130171 ArgumentNullException . ThrowIfNull ( encoding ) ;
131172
173+ var byteCount = encoding . GetByteCount ( s ) ;
132174#if NET
133- ReadOnlySpan < char > value = s ;
134- var count = encoding . GetByteCount ( value ) ;
135- var bytes = count <= 256 ? stackalloc byte [ count ] : new byte [ count ] ;
136- encoding . GetBytes ( value , bytes ) ;
137- Write ( ( uint ) count ) ;
138- Write ( bytes ) ;
175+ Write ( ( s , byteCount , encoding ) , 4 + byteCount , static ( span , args ) =>
176+ {
177+ BinaryPrimitives . WriteUInt32BigEndian ( span , ( uint ) args . byteCount ) ;
178+
179+ var bytesWritten = args . encoding . GetBytes ( args . s , span . Slice ( 4 ) ) ;
180+
181+ Debug . Assert ( bytesWritten == span . Length - 4 ) ;
182+ } ) ;
139183#else
140- var bytes = encoding . GetBytes ( s ) ;
141- WriteBinary ( bytes , 0 , bytes . Length ) ;
184+ var rentedBuffer = ArrayPool < byte > . Shared . Rent ( byteCount ) ;
185+
186+ var bytesWritten = encoding . GetBytes ( s , 0 , s . Length , rentedBuffer , 0 ) ;
187+
188+ Debug . Assert ( bytesWritten == byteCount ) ;
189+
190+ WriteBinary ( rentedBuffer , 0 , bytesWritten ) ;
191+
192+ ArrayPool < byte > . Shared . Return ( rentedBuffer ) ;
142193#endif
143194 }
144195
0 commit comments