Skip to content

Commit a2708a8

Browse files
Azure.Core: Small request content performance tweaks (Azure#52488)
* Remove CanSeek check since that is not needed to due ctor check * Array Content span based * MemoryContent span based * ReadOnlySequence content span based * Cleanup * StringRequestContent buffer * Private string content * Cleanup * Fix boundaries * Only return to pool based on TFM * Rename as per review --------- Co-authored-by: Daniel Marbach <[email protected]>
1 parent a6d2ee1 commit a2708a8

File tree

2 files changed

+93
-18
lines changed

2 files changed

+93
-18
lines changed

sdk/core/Azure.Core/src/RequestContent.cs

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public abstract class RequestContent : IDisposable
6767
/// <param name="content">The <see cref="string"/> to use.</param>
6868
/// <returns>An instance of <see cref="RequestContent"/> that wraps a <see cref="string"/>.</returns>
6969
/// <remarks>The returned content represents the UTF-8 Encoding of the given string.</remarks>
70-
public static RequestContent Create(string content) => Create(s_UTF8NoBomEncoding.GetBytes(content));
70+
public static RequestContent Create(string content) => new CustomStringContent(content, s_UTF8NoBomEncoding);
7171

7272
/// <summary>
7373
/// Creates an instance of <see cref="RequestContent"/> that wraps a <see cref="BinaryData"/>.
@@ -221,13 +221,8 @@ public override void WriteTo(Stream stream, CancellationToken cancellationToken)
221221

222222
public override bool TryComputeLength(out long length)
223223
{
224-
if (_stream.CanSeek)
225-
{
226-
length = _stream.Length - _origin;
227-
return true;
228-
}
229-
length = 0;
230-
return false;
224+
length = _stream.Length - _origin;
225+
return true;
231226
}
232227

233228
public override async Task WriteToAsync(Stream stream, CancellationToken cancellation)
@@ -261,7 +256,11 @@ public override void Dispose() { }
261256

262257
public override void WriteTo(Stream stream, CancellationToken cancellation)
263258
{
259+
#if NET6_0_OR_GREATER
260+
stream.Write(_bytes.AsSpan(_contentStart, _contentLength));
261+
#else
264262
stream.Write(_bytes, _contentStart, _contentLength);
263+
#endif
265264
}
266265

267266
public override bool TryComputeLength(out long length)
@@ -272,9 +271,11 @@ public override bool TryComputeLength(out long length)
272271

273272
public override async Task WriteToAsync(Stream stream, CancellationToken cancellation)
274273
{
275-
#pragma warning disable CA1835 // WriteAsync(Memory<>) overload is not available in all targets
274+
#if NET6_0_OR_GREATER
275+
await stream.WriteAsync(_bytes.AsMemory(_contentStart, _contentLength), cancellation).ConfigureAwait(false);
276+
#else
276277
await stream.WriteAsync(_bytes, _contentStart, _contentLength, cancellation).ConfigureAwait(false);
277-
#pragma warning restore // WriteAsync(Memory<>) overload is not available in all targets
278+
#endif
278279
}
279280
}
280281

@@ -289,8 +290,13 @@ public override void Dispose() { }
289290

290291
public override void WriteTo(Stream stream, CancellationToken cancellation)
291292
{
293+
#if NET6_0_OR_GREATER
294+
stream.Write(_bytes.Span);
295+
#else
296+
292297
byte[] buffer = _bytes.ToArray();
293298
stream.Write(buffer, 0, buffer.Length);
299+
#endif
294300
}
295301

296302
public override bool TryComputeLength(out long length)
@@ -316,8 +322,15 @@ public override void Dispose() { }
316322

317323
public override void WriteTo(Stream stream, CancellationToken cancellation)
318324
{
325+
#if NET6_0_OR_GREATER
326+
foreach (var memory in _bytes)
327+
{
328+
stream.Write(memory.Span);
329+
}
330+
#else
319331
byte[] buffer = _bytes.ToArray();
320332
stream.Write(buffer, 0, buffer.Length);
333+
#endif
321334
}
322335

323336
public override bool TryComputeLength(out long length)
@@ -332,6 +345,56 @@ public override async Task WriteToAsync(Stream stream, CancellationToken cancell
332345
}
333346
}
334347

348+
private sealed class CustomStringContent : RequestContent
349+
{
350+
private readonly byte[] _buffer;
351+
private readonly int _actualByteCount;
352+
353+
public CustomStringContent(string value, Encoding? encoding = null)
354+
{
355+
encoding ??= Encoding.UTF8;
356+
#if NET6_0_OR_GREATER
357+
var byteCount = encoding.GetMaxByteCount(value.Length);
358+
_buffer = ArrayPool<byte>.Shared.Rent(byteCount);
359+
_actualByteCount = encoding.GetBytes(value, _buffer);
360+
#else
361+
_buffer = encoding.GetBytes(value);
362+
_actualByteCount = _buffer.Length;
363+
#endif
364+
}
365+
366+
public override async Task WriteToAsync(Stream stream, CancellationToken cancellation)
367+
{
368+
#if NET6_0_OR_GREATER
369+
await stream.WriteAsync(_buffer.AsMemory(0, _actualByteCount), cancellation).ConfigureAwait(false);
370+
#else
371+
await stream.WriteAsync(_buffer, 0, _actualByteCount, cancellation).ConfigureAwait(false);
372+
#endif
373+
}
374+
375+
public override void WriteTo(Stream stream, CancellationToken cancellation)
376+
{
377+
#if NET6_0_OR_GREATER
378+
stream.Write(_buffer.AsSpan(0, _actualByteCount));
379+
#else
380+
stream.Write(_buffer, 0, _actualByteCount);
381+
#endif
382+
}
383+
384+
public override bool TryComputeLength(out long length)
385+
{
386+
length = _actualByteCount;
387+
return true;
388+
}
389+
390+
public override void Dispose()
391+
{
392+
#if NET6_0_OR_GREATER
393+
ArrayPool<byte>.Shared.Return(_buffer, clearArray: true);
394+
#endif
395+
}
396+
}
397+
335398
private sealed class DynamicDataContent : RequestContent
336399
{
337400
private readonly DynamicData _data;
@@ -350,7 +413,7 @@ public override void WriteTo(Stream stream, CancellationToken cancellation)
350413

351414
public override bool TryComputeLength(out long length)
352415
{
353-
length = default;
416+
length = 0;
354417
return false;
355418
}
356419

sdk/core/Azure.Core/src/Shared/StringRequestContent.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Buffers;
56
using System.IO;
67
using System.Text;
78
using System.Threading;
@@ -11,39 +12,50 @@ namespace Azure.Core
1112
{
1213
internal class StringRequestContent : RequestContent
1314
{
14-
private readonly byte[] _bytes;
15+
private readonly byte[] _buffer;
16+
private readonly int _actualByteCount;
1517

1618
public StringRequestContent(string value)
1719
{
18-
_bytes = Encoding.UTF8.GetBytes(value);
20+
#if NET6_0_OR_GREATER
21+
var byteCount = Encoding.UTF8.GetMaxByteCount(value.Length);
22+
_buffer = ArrayPool<byte>.Shared.Rent(byteCount);
23+
_actualByteCount = Encoding.UTF8.GetBytes(value, _buffer);
24+
#else
25+
_buffer = Encoding.UTF8.GetBytes(value);
26+
_actualByteCount = _buffer.Length;
27+
#endif
1928
}
2029

2130
public override async Task WriteToAsync(Stream stream, CancellationToken cancellation)
2231
{
2332
#if NET6_0_OR_GREATER
24-
await stream.WriteAsync(_bytes.AsMemory(), cancellation).ConfigureAwait(false);
33+
await stream.WriteAsync(_buffer.AsMemory(0, _actualByteCount), cancellation).ConfigureAwait(false);
2534
#else
26-
await stream.WriteAsync(_bytes, 0, _bytes.Length, cancellation).ConfigureAwait(false);
35+
await stream.WriteAsync(_buffer, 0, _actualByteCount, cancellation).ConfigureAwait(false);
2736
#endif
2837
}
2938

3039
public override void WriteTo(Stream stream, CancellationToken cancellation)
3140
{
3241
#if NET6_0_OR_GREATER
33-
stream.Write(_bytes.AsSpan());
42+
stream.Write(_buffer.AsSpan(0, _actualByteCount));
3443
#else
35-
stream.Write(_bytes, 0, _bytes.Length);
44+
stream.Write(_buffer, 0, _actualByteCount);
3645
#endif
3746
}
3847

3948
public override bool TryComputeLength(out long length)
4049
{
41-
length = _bytes.Length;
50+
length = _actualByteCount;
4251
return true;
4352
}
4453

4554
public override void Dispose()
4655
{
56+
#if NET6_0_OR_GREATER
57+
ArrayPool<byte>.Shared.Return(_buffer, clearArray: true);
58+
#endif
4759
}
4860
}
4961
}

0 commit comments

Comments
 (0)