@@ -59,28 +59,57 @@ public bool IsEndOfData
5959 }
6060 }
6161
62- #if ! NET
63- private void Write ( ReadOnlySpan < byte > buffer )
62+ // Because this type derives from MemoryStream, the base Write(ReadOnlySpan) chooses
63+ // to rent an array, copy the data in and delegate to Write(byte[], int, int) for
64+ // backwards compatibility.
65+ // With a bit of extra ceremony, we can instead allow the various Write methods here
66+ // to write directly into the underlying buffer without the need for any intermediate
67+ // arrays (rented or otherwise).
68+
69+ #if NET9_0_OR_GREATER
70+ /// <inheritdoc/>
71+ public override void Write ( ReadOnlySpan < byte > buffer )
6472 {
65- var sharedBuffer = System . Buffers . ArrayPool < byte > . Shared . Rent ( buffer . Length ) ;
73+ Write ( buffer , buffer . Length , static ( span , buffer ) => buffer . CopyTo ( span ) ) ;
74+ }
75+ #endif
76+
77+ private delegate void WriteAction < in TArg > ( Span < byte > span , TArg arg )
78+ #if NET9_0_OR_GREATER
79+ where TArg : allows ref struct
80+ #endif
81+ ;
6682
67- buffer . CopyTo ( sharedBuffer ) ;
83+ private void Write < TArg > ( TArg arg , int numBytesToWrite , WriteAction < TArg > writeAction )
84+ #if NET9_0_OR_GREATER
85+ where TArg : allows ref struct
86+ #endif
87+ {
88+ var endPosition = Position + numBytesToWrite ;
6889
69- Write ( sharedBuffer , 0 , buffer . Length ) ;
90+ if ( Capacity < endPosition )
91+ {
92+ var newCapacity = Math . Max ( endPosition , Math . Min ( 2 * ( uint ) Capacity , Array . MaxLength ) ) ;
93+ Capacity = checked ( ( int ) newCapacity ) ;
94+ }
7095
71- System . Buffers . ArrayPool < byte > . Shared . Return ( sharedBuffer ) ;
96+ if ( endPosition > Length )
97+ {
98+ SetLength ( endPosition ) ;
99+ }
100+
101+ writeAction ( GetRemainingBuffer ( ) . AsSpan ( 0 , numBytesToWrite ) , arg ) ;
102+
103+ Position = endPosition ;
72104 }
73- #endif
74105
75106 /// <summary>
76107 /// Writes an <see cref="uint"/> to the SSH data stream.
77108 /// </summary>
78109 /// <param name="value"><see cref="uint"/> data to write.</param>
79110 public void Write ( uint value )
80111 {
81- Span < byte > bytes = stackalloc byte [ 4 ] ;
82- BinaryPrimitives . WriteUInt32BigEndian ( bytes , value ) ;
83- Write ( bytes ) ;
112+ Write ( value , 4 , static ( span , value ) => BinaryPrimitives . WriteUInt32BigEndian ( span , value ) ) ;
84113 }
85114
86115 /// <summary>
@@ -89,9 +118,7 @@ public void Write(uint value)
89118 /// <param name="value"><see cref="ulong"/> data to write.</param>
90119 public void Write ( ulong value )
91120 {
92- Span < byte > bytes = stackalloc byte [ 8 ] ;
93- BinaryPrimitives . WriteUInt64BigEndian ( bytes , value ) ;
94- Write ( bytes ) ;
121+ Write ( value , 8 , static ( span , value ) => BinaryPrimitives . WriteUInt64BigEndian ( span , value ) ) ;
95122 }
96123
97124 /// <summary>
@@ -100,9 +127,22 @@ public void Write(ulong value)
100127 /// <param name="data">The <see cref="BigInteger" /> to write.</param>
101128 public void Write ( BigInteger data )
102129 {
130+ #if NET
131+ var byteCount = data . GetByteCount ( ) ;
132+
133+ Write ( ( data , byteCount ) , 4 + byteCount , static ( span , args ) =>
134+ {
135+ BinaryPrimitives . WriteUInt32BigEndian ( span , ( uint ) args . byteCount ) ;
136+
137+ var success = args . data . TryWriteBytes ( span . Slice ( 4 ) , out var bytesWritten , isBigEndian : true ) ;
138+
139+ Debug . Assert ( success && bytesWritten == span . Length - 4 ) ;
140+ } ) ;
141+ #else
103142 var bytes = data . ToByteArray ( isBigEndian : true ) ;
104143
105144 WriteBinary ( bytes , 0 , bytes . Length ) ;
145+ #endif
106146 }
107147
108148 /// <summary>
@@ -129,16 +169,26 @@ public void Write(string s, Encoding encoding)
129169 ArgumentNullException . ThrowIfNull ( s ) ;
130170 ArgumentNullException . ThrowIfNull ( encoding ) ;
131171
172+ var byteCount = encoding . GetByteCount ( s ) ;
132173#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 ) ;
174+ Write ( ( s , byteCount , encoding ) , 4 + byteCount , static ( span , args ) =>
175+ {
176+ BinaryPrimitives . WriteUInt32BigEndian ( span , ( uint ) args . byteCount ) ;
177+
178+ var bytesWritten = args . encoding . GetBytes ( args . s , span . Slice ( 4 ) ) ;
179+
180+ Debug . Assert ( bytesWritten == span . Length - 4 ) ;
181+ } ) ;
139182#else
140- var bytes = encoding . GetBytes ( s ) ;
141- WriteBinary ( bytes , 0 , bytes . Length ) ;
183+ var rentedBuffer = System . Buffers . ArrayPool < byte > . Shared . Rent ( byteCount ) ;
184+
185+ var bytesWritten = encoding . GetBytes ( s , 0 , s . Length , rentedBuffer , 0 ) ;
186+
187+ Debug . Assert ( bytesWritten == byteCount ) ;
188+
189+ WriteBinary ( rentedBuffer , 0 , bytesWritten ) ;
190+
191+ System . Buffers . ArrayPool < byte > . Shared . Return ( rentedBuffer ) ;
142192#endif
143193 }
144194
0 commit comments