8000 Use Span<T>-based methods in System.Net.Mail (#115111) · dotnet/runtime@43955cb · GitHub
[go: up one dir, main page]

Skip to content

Commit 43955cb

Browse files
authored
Use Span<T>-based methods in System.Net.Mail (#115111)
* Span-based IByteEncoder * IEncodableStream.DecodeBytes * IEncodableStream.EncodeBytes * DelegatedStream and derived implementations. * Minor changes * Update src/libraries/System.Net.Mail/src/System/Net/Base64Stream.cs * Apply suggestions from code review * Code review feedback * Removed TODO * Minor changes * Consistent exceptions
1 parent 5586307 commit 43955cb

16 files changed

+384
-400
lines changed

src/libraries/System.Net.Mail/src/System/Net/Base64Stream.cs

Lines changed: 55 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Text;
88
using System.Threading;
99
using System.Threading.Tasks;
10+
using System.Buffers;
1011

1112
namespace System.Net
1213
{
@@ -20,7 +21,7 @@ internal sealed class Base64Stream : DelegatedStream, IEncodableStream
2021
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, // 2
2122
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, // 3
2223
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 4
23-
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // 5
24+
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // 5
2425
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 6
2526
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, // 7
2627
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 8
@@ -52,6 +53,9 @@ internal Base64Stream(Base64WriteStateInfo writeStateInfo) : base(new MemoryStre
5253
_encoder = new Base64Encoder(_writeState, writeStateInfo.MaxLineLength);
5354
}
5455

56+
public override bool CanRead => BaseStream.CanRead;
57+
public override bool CanWrite => BaseStream.CanWrite;
58+
5559
private ReadStateInfo ReadState => _readState ??= new ReadStateInfo();
5660

5761
internal WriteStateInfoBase WriteState
@@ -63,12 +67,6 @@ internal WriteStateInfoBase WriteState
6367
}
6468
}
6569

66-
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) =>
67-
TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state);
68-
69-
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) =>
70-
TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
71-
7270
public override void Close()
7371
{
7472
if (_writeState != null && WriteState.Length > 0)
@@ -80,14 +78,14 @@ public override void Close()
8078
base.Close();
8179
}
8280

83-
public unsafe int DecodeBytes(byte[] buffer, int offset, int count)
81+
public unsafe int DecodeBytes(Span<byte> buffer)
8482
{
8583
fixed (byte* pBuffer = buffer)
8684
{
87-
byte* start = pBuffer + offset;
85+
byte* start = pBuffer;
8886
byte* source = start;
8987
byte* dest = start;
90-
byte* end = start + count;
88+
byte* end = start + buffer.Length;
9189

9290
while (source < end)
9391
{
@@ -133,24 +131,18 @@ public unsafe int DecodeBytes(byte[] buffer, int offset, int count)
133131
}
134132
}
135133

136-
public int EncodeBytes(byte[] buffer, int offset, int count) =>
137-
EncodeBytes(buffer, offset, count, true, true);
134+
public int EncodeBytes(ReadOnlySpan<byte> buffer) =>
135+
_encoder.EncodeBytes(buffer, true, true);
138136

139-
internal int EncodeBytes(byte[] buffer, int offset, int count, bool dontDeferFinalBytes, bool shouldAppendSpaceToCRLF)
137+
internal int EncodeBytes(ReadOnlySpan<byte> buffer, bool dontDeferFinalBytes, bool shouldAppendSpaceToCRLF)
140138
{
141-
return _encoder.EncodeBytes(buffer, offset, count, dontDeferFinalBytes, shouldAppendSpaceToCRLF);
139+
return _encoder.EncodeBytes(buffer, dontDeferFinalBytes, shouldAppendSpaceToCRLF);
142140
}
143141

144142
public int EncodeString(string value, Encoding encoding) => _encoder.EncodeString(value, encoding);
145143

146144
public string GetEncodedString() => _encoder.GetEncodedString();
147145

148-
public override int EndRead(IAsyncResult asyncResult) =>
149-
TaskToAsyncResult.End<int>(asyncResult);
150-
151-
public override void EndWrite(IAsyncResult asyncResult) =>
152-
TaskToAsyncResult.End(asyncResult);
153-
154146
public override void Flush()
155147
{
156148
if (_writeState != null && WriteState.Length > 0)
@@ -163,90 +155,78 @@ public override void Flush()
< F42D /div>
163155

164156
public override async Task FlushAsync(CancellationToken cancellationToken)
165157
{
166-
if (_writeState != null && WriteState.Length > 0)
167-
{
168-
await base.WriteAsync(WriteState.Buffer.AsMemory(0, WriteState.Length), cancellationToken).ConfigureAwait(false);
169-
WriteState.Reset();
170-
}
171-
158+
await FlushInternalAsync(cancellationToken).ConfigureAwait(false);
172159
await base.FlushAsync(cancellationToken).ConfigureAwait(false);
173160
}
174161

175162
private void FlushInternal()
176163
{
177-
base.Write(WriteState.Buffer, 0, WriteState.Length);
164+
BaseStream.Write(WriteState.Buffer.AsSpan(0, WriteState.Length));
178165
WriteState.Reset();
179166
}
180167

181-
public override int Read(byte[] buffer, int offset, int count)
168+
private async ValueTask FlushInternalAsync(CancellationToken cancellationToken)
182169
{
183-
ValidateBufferArguments(buffer, offset, count);
170+
await BaseStream.WriteAsync(WriteState.Buffer.AsMemory(0, WriteState.Length), cancellationToken).ConfigureAwait(false);
171+
WriteState.Reset();
172+
}
184173

174+
protected override int ReadInternal(Span<byte> buffer)
175+
{
185176
while (true)
186177
{
187178
// read data from the underlying stream
188-
int read = base.Read(buffer, offset, count);
179+
int read = BaseStream.Read(buffer);
189180

190181
// if the underlying stream returns 0 then there
191-
// is no more data - ust return 0.
182+
// is no more data - just return 0.
192183
if (read == 0)
193184
{
194185
return 0;
195186
}
196187

197-
// while decoding, we may end up not having
198-
// any bytes to return pending additional data
199-
// from the underlying stream.
200-
read = DecodeBytes(buffer, offset, read);
188+
// Decode the read bytes and update the input buffer with decoded bytes
189+
read = DecodeBytes(buffer.Slice(0, read));
201190
if (read > 0)
202191
{
203192
return read;
204193
}
205194
}
206195
}
207-
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
208-
{
209-
ValidateBufferArguments(buffer, offset, count);
210-
return ReadAsyncCore(buffer, offset, count, cancellationToken);
211196

212-
async Task<int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
197+
protected override async ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, CancellationToken cancellationToken = default)
198+
{
199+
while (true)
213200
{
214-
while (true)
215-
{
216-
// read data from the underlying stream
217-
int read = await base.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
201+
// read data from the underlying stream
202+
int read = await BaseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
218203

219-
// if the underlying stream returns 0 then there
220-
// is no more data - ust return 0.
221-
if (read == 0)
222-
{
223-
return 0;
224-
}
204+
// if the underlying stream returns 0 then there
205+
// is no more data - just return 0.
206+
if (read == 0)
207+
{
208+
return 0;
209+
}
225210

226-
// while decoding, we may end up not having
227-
// any bytes to return pending additional data
228-
// from the underlying stream.
229-
read = DecodeBytes(buffer, offset, read);
230-
if (read > 0)
231-
{
232-
return read;
233-
}
211+
// Decode the read bytes and update the input buffer with decoded bytes
212+
read = DecodeBytes(buffer.Span.Slice(0, read));
213+
if (read > 0)
214+
{
215+
return read;
234216
}
235217
}
236218
}
237219

238-
public override void Write(byte[] buffer, int offset, int count)
220+
protected override void WriteInternal(ReadOnlySpan<byte> buffer)
239221
{
240-
ValidateBufferArguments(buffer, offset, count);
241-
242222
int written = 0;
243223

244224
// do not append a space when writing from a stream since this means
245225
// it's writing the email body
246226
while (true)
247227
{
248-
written += EncodeBytes(buffer, offset + written, count - written, false, false);
249-
if (written < count)
228+
written += EncodeBytes(buffer.Slice(written), false, false);
229+
if (written < buffer.Length)
250230
{
251231
FlushInternal();
252232
}
@@ -257,28 +237,22 @@ public override void Write(byte[] buffer, int offset, int count)
257237
}
258238
}
259239

260-
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
240+
protected override async ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
261241
{
262-
ValidateBufferArguments(buffer, offset, count);
263-
return WriteAsyncCore(buffer, offset, count, cancellationToken);
242+
int written = 0;
264243

265-
async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
244+
// do not append a space when writing from a stream since this means
245+
// it's writing the email body
246+
while (true)
266247
{
267-
int written = 0;
268-
269-
// do not append a space when writing from a stream since this means
270-
// it's writing the email body
271-
while (true)
248+
written += EncodeBytes(buffer.Span.Slice(written), false, false);
249+
if (written < buffer.Length)
272250
{
273-
written += EncodeBytes(buffer, offset + written, count - written, false, false);
274-
if (written < count)
275-
{
276-
await FlushAsync(cancellationToken).ConfigureAwait(false);
277-
}
278-
else
279-
{
280-
break;
281-
}
251+
await FlushInternalAsync(cancellationToken).ConfigureAwait(false);
252+
}
253+
else
254+
{
255+
break;
282256
}
283257
}
284258
}

src/libraries/System.Net.Mail/src/System/Net/BufferedReadStream.cs

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Threading;
66
using System.Threading.Tasks;
7+
using System.Buffers;
78

89
namespace System.Net
910
{
@@ -23,96 +24,76 @@ internal BufferedReadStream(Stream stream, bool readMore) : base(stream)
2324
_readMore = readMore;
2425
}
2526

26-
public override bool CanWrite
27-
{
28-
get
29-
{
30-
return false;
31-
}
32-
}
27+
public override bool CanWrite => false;
28+
public override bool CanRead => BaseStream.CanRead;
3329

34-
public override bool CanSeek
35-
{
36-
get
37-
{
38-
return false;
39-
}
40-
}
41-
42-
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) =>
43-
TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state);
30+
public override bool CanSeek => false;
4431

45-
public override int EndRead(IAsyncResult asyncResult) =>
46-
TaskToAsyncResult.End<int>(asyncResult);
47-
48-
public override int Read(byte[] buffer, int offset, int count)
32+
protected override int ReadInternal(Span<byte> buffer)
4933
{
50-
int read = 0;
5134
if (_storedOffset < _storedLength)
5235
{
53-
read = Math.Min(count, _storedLength - _storedOffset);
54-
Buffer.BlockCopy(_storedBuffer!, _storedOffset, buffer, offset, read);
36+
int read = Math.Min(buffer.Length, _storedLength - _storedOffset);
37+
_storedBuffer.AsSpan(_storedOffset, read).CopyTo(buffer);
5538
_storedOffset += read;
56-
if (read == count || !_readMore)
39+
if (read == buffer.Length || !_readMore)
5740
{
5841
return read;
5942
}
6043

61-
offset += read;
62-
count -= read;
44+
// Need to read more from the underlying stream
45+
return read + BaseStream.Read(buffer.Slice(read));
6346
}
64-
return read + base.Read(buffer, offset, count);
47+
48+
return BaseStream.Read(buffer);
6549
}
6650

67-
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
51+
protected override ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, CancellationToken cancellationToken = default)
6852
{
69-
int read;
7053
if (_storedOffset >= _storedLength)
7154
{
72-
return base.ReadAsync(buffer, offset, count, cancellationToken);
55+
return BaseStream.ReadAsync(buffer, cancellationToken);
7356
}
7457

75-
read = Math.Min(count, _storedLength - _storedOffset);
76-
Buffer.BlockCopy(_storedBuffer!, _storedOffset, buffer, offset, read);
58+
int read = Math.Min(buffer.Length, _storedLength - _storedOffset);
59+
_storedBuffer.AsMemory(_storedOffset, read).CopyTo(buffer);
7760
_storedOffset += read;
78-
if (read == count || !_readMore)
61+
if (read == buffer.Length || !_readMore)
7962
{
80-
return Task.FromResult<int>(read);
63+
return new ValueTask<int>(read);
8164
}
8265

83-
offset += read;
84-
count -= read;
85-
86-
return ReadMoreAsync(read, buffer, offset, count, cancellationToken);
66+
// Need to read more from the underlying stream
67+
return ReadMoreAsync(read, buffer.Slice(read), cancellationToken);
8768
}
8869

89-
private async Task<int> ReadMoreAsync(int bytesAlreadyRead, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
70+
private async ValueTask<int> ReadMoreAsync(int bytesAlreadyRead, Memory<byte> buffer, CancellationToken cancellationToken)
9071
{
91-
int returnValue = await base.ReadAsync(buffer.AsMemory(offset, count), cancellationToken).ConfigureAwait(false);
72+
int returnValue = await BaseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
9273
return bytesAlreadyRead + returnValue;
9374
}
9475

95-
public override int ReadByte()
76+
protected override void WriteInternal(ReadOnlySpan<byte> buffer)
9677
{
97-
if (_storedOffset < _storedLength)
98-
{
99-
return _storedBuffer![_storedOffset++];
100-
}
101-
else
102-
{
103-
return base.ReadByte();
104-
}
78+
throw new NotImplementedException();
79+
}
80+
81+
protected override ValueTask WriteAsyncInternal(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
82+
{
83+
throw new NotImplementedException();
10584
}
10685

10786
// adds additional content to the beginning of the buffer
10887
// so the layout of the storedBuffer will be
10988
// <buffer><existingBuffer>
11089
// after calling push
111-
internal void Push(byte[] buffer, int offset, int count)
90+
internal void Push(ReadOnlySpan<byte> buffer)
11291
{
113-
if (count == 0)
92+
if (buffer.Length == 0)
11493
return;
11594

95+
int count = buffer.Length;
96+
11697
if (_storedOffset == _storedLength)
11798
{
11899
if (_storedBuffer == null || _storedBuffer.Length < count)
@@ -146,7 +127,7 @@ internal void Push(byte[] buffer, int offset, int count)
146127
}
147128
}
148129

149-
Buffer.BlockCopy(buffer, offset, _storedBuffer!, _storedOffset, count);
130+
buffer.CopyTo(_storedBuffer.AsSpan(_storedOffset));
150131
}
151132
}
152133
}

0 commit comments

Comments
 (0)
0