Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Commit bd60507

Browse files
committed
#553 Use System.Buffers for temporary arrays
1 parent 1b71748 commit bd60507

File tree

11 files changed

+210
-113
lines changed

11 files changed

+210
-113
lines changed

src/Microsoft.AspNetCore.Http/project.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"Microsoft.AspNetCore.Http.Abstractions": "1.0.0-*",
2222
"Microsoft.AspNetCore.WebUtilities": "1.0.0-*",
2323
"Microsoft.Extensions.ObjectPool": "1.0.0-*",
24-
"Microsoft.Net.Http.Headers": "1.0.0-*"
24+
"Microsoft.Net.Http.Headers": "1.0.0-*",
25+
"System.Buffers": "4.0.0-*"
2526
},
2627
"frameworks": {
2728
"net451": { },

src/Microsoft.AspNetCore.Owin/WebSockets/OwinWebSocketAdapter.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Collections.Generic;
67
using System.Threading;
78
using System.Threading.Tasks;
@@ -33,6 +34,7 @@ namespace Microsoft.AspNetCore.Owin
3334

3435
public class OwinWebSocketAdapter : WebSocket
3536
{
37+
private const int _rentedBufferSize = 1024;
3638
private IDictionary<string, object> _websocketContext;
3739
private WebSocketSendAsync _sendAsync;
3840
private WebSocketReceiveAsync _receiveAsync;
@@ -126,11 +128,18 @@ public override async Task CloseAsync(WebSocketCloseStatus closeStatus, string s
126128
await CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
127129
}
128130

129-
byte[] buffer = new byte[1024];
130-
while (State == WebSocketState.CloseSent)
131+
var buffer = ArrayPool<byte>.Shared.Rent(_rentedBufferSize);
132+
try
131133
{
132-
// Drain until close received
133-
await ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
134+
while (State == WebSocketState.CloseSent)
135+
{
136+
// Drain until close received
137+
await ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
138+
}
139+
}
140+
finally
141+
{
142+
ArrayPool<byte>.Shared.Return(buffer);
134143
}
135144
}
136145

src/Microsoft.AspNetCore.WebUtilities/BufferedReadStream.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.IO;
67
using System.Text;
78
using System.Threading;
@@ -16,19 +17,26 @@ internal class BufferedReadStream : Stream
1617

1718
private readonly Stream _inner;
1819
private readonly byte[] _buffer;
20+
private readonly ArrayPool<byte> _bytePool;
1921
private int _bufferOffset = 0;
2022
private int _bufferCount = 0;
2123
private bool _disposed;
2224

2325
public BufferedReadStream(Stream inner, int bufferSize)
26+
: this(inner, bufferSize, ArrayPool<byte>.Shared)
27+
{
28+
}
29+
30+
public BufferedReadStream(Stream inner, int bufferSize, ArrayPool<byte> bytePool)
2431
{
2532
if (inner == null)
2633
{
2734
throw new ArgumentNullException(nameof(inner));
2835
}
2936

3037
_inner = inner;
31-
_buffer = new byte[bufferSize];
38+
_bytePool = bytePool;
39+
_buffer = bytePool.Rent(bufferSize);
3240
}
3341

3442
public ArraySegment<byte> BufferedData
@@ -128,10 +136,15 @@ public override void SetLength(long value)
128136

129137
protected override void Dispose(bool disposing)
130138
{
131-
_disposed = true;
132-
if (disposing)
139+
if (!_disposed)
133140
{
134-
_inner.Dispose();
141+
_disposed = true;
142+
_bytePool.Return(_buffer);
143+
144+
if (disposing)
145+
{
146+
_inner.Dispose();
147+
}
135148
}
136149
}
137150

src/Microsoft.AspNetCore.WebUtilities/FileBufferingReadStream.cs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Diagnostics;
67
using System.IO;
78
using System.Threading;
@@ -16,12 +17,15 @@ namespace Microsoft.AspNetCore.WebUtilities
1617
/// </summary>
1718
public class FileBufferingReadStream : Stream
1819
{
20+
private const int _maxRentedBufferSize = 1024 * 1024; // 1MB
1921
private readonly Stream _inner;
22+
private readonly ArrayPool<byte> _bytePool;
2023
private readonly int _memoryThreshold;
2124
private string _tempFileDirectory;
2225
private readonly Func<string> _tempFileDirectoryAccessor;
2326

24-
private Stream _buffer = new MemoryStream(); // TODO: We could have a more efficiently expanding buffer stream.
27+
private Stream _buffer;
28+
private byte[] _rentedBuffer;
2529
private bool _inMemory = true;
2630
private bool _completelyBuffered;
2731

@@ -32,6 +36,15 @@ public FileBufferingReadStream(
3236
Stream inner,
3337
int memoryThreshold,
3438
Func<string> tempFileDirectoryAccessor)
39+
: this(inner, memoryThreshold, tempFileDirectoryAccessor, ArrayPool<byte>.Shared)
40+
{
41+
}
42+
43+
public FileBufferingReadStream(
44+
Stream inner,
45+
int memoryThreshold,
46+
Func<string> tempFileDirectoryAccessor,
47+
ArrayPool<byte> bytePool)
3548
{
3649
if (inner == null)
3750
{
@@ -43,13 +56,34 @@ public FileBufferingReadStream(
4356
throw new ArgumentNullException(nameof(tempFileDirectoryAccessor));
4457
}
4558

59+
_bytePool = bytePool;
60+
if (memoryThreshold < _maxRentedBufferSize)
61+
{
62+
_rentedBuffer = bytePool.Rent(memoryThreshold);
63+
_buffer = new MemoryStream(_rentedBuffer);
64+
_buffer.SetLength(0);
65+
}
66+
else
67+
{
68+
_buffer = new MemoryStream();
69+
}
70+
4671
_inner = inner;
4772
_memoryThreshold = memoryThreshold;
4873
_tempFileDirectoryAccessor = tempFileDirectoryAccessor;
4974
}
5075

5176
// TODO: allow for an optional buffer size limit to prevent filling hard disks. 1gb?
5277
public FileBufferingReadStream(Stream inner, int memoryThreshold, string tempFileDirectory)
78+
: this(inner, memoryThreshold, tempFileDirectory, ArrayPool<byte>.Shared)
79+
{
80+
}
81+
82+
public FileBufferingReadStream(
83+
Stream inner,
84+
int memoryThreshold,
85+
string tempFileDirectory,
86+
ArrayPool<byte> bytePool)
5387
{
5488
if (inner == null)
5589
{
@@ -61,6 +95,18 @@ public FileBufferingReadStream(Stream inner, int memoryThreshold, string tempFil
6195
throw new ArgumentNullException(nameof(tempFileDirectory));
6296
}
6397

98+
_bytePool = bytePool;
99+
if (memoryThreshold < _maxRentedBufferSize)
100+
{
101+
_rentedBuffer = bytePool.Rent(memoryThreshold);
102+
_buffer = new MemoryStream(_rentedBuffer);
103+
_buffer.SetLength(0);
104+
}
105+
else
106+
{
107+
_buffer = new MemoryStream();
108+
}
109+
64110
_inner = inner;
65111
_memoryThreshold = memoryThreshold;
66112
_tempFileDirectory = tempFileDirectory;
@@ -145,11 +191,11 @@ public override int Read(byte[] buffer, int offset, int count)
145191

146192
if (_inMemory && _buffer.Length + read > _memoryThreshold)
147193
{
148-
var oldBuffer = _buffer;
149-
_buffer = CreateTempFile();
150194
_inMemory = false;
151-
oldBuffer.Position = 0;
152-
oldBuffer.CopyTo(_buffer, 1024 * 16);
195+
_buffer = CreateTempFile();
196+
_buffer.Write(_rentedBuffer, 0, (int)_buffer.Length);
197+
_bytePool.Return(_rentedBuffer);
198+
_rentedBuffer = null;
153199
}
154200

155201
if (read > 0)
@@ -216,11 +262,11 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
216262

217263
if (_inMemory && _buffer.Length + read > _memoryThreshold)
218264
{
219-
var oldBuffer = _buffer;
220-
_buffer = CreateTempFile();
221265
_inMemory = false;
222-
oldBuffer.Position = 0;
223-
await oldBuffer.CopyToAsync(_buffer, 1024 * 16, cancellationToken);
266+
_buffer = CreateTempFile();
267+
await _buffer.WriteAsync(_rentedBuffer, 0, (int)_buffer.Length, cancellationToken);
268+
_bytePool.Return(_rentedBuffer);
269+
_rentedBuffer = null;
224270
}
225271

226272
if (read > 0)
@@ -270,6 +316,11 @@ protected override void Dispose(bool disposing)
270316
if (!_disposed)
271317
{
272318
_disposed = true;
319+
if (_rentedBuffer != null)
320+
{
321+
_bytePool.Return(_rentedBuffer);
322+
}
323+
273324
if (disposing)
274325
{
275326
_buffer.Dispose();
@@ -285,4 +336,4 @@ private void ThrowIfDisposed()
285336
}
286337
}
287338
}
288-
}
339+
}

src/Microsoft.AspNetCore.WebUtilities/FormReader.cs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Collections.Generic;
67
using System.IO;
78
using System.Text;
@@ -14,25 +15,40 @@ namespace Microsoft.AspNetCore.WebUtilities
1415
/// <summary>
1516
/// Used to read an 'application/x-www-form-urlencoded' form.
1617
/// </summary>
17-
public class FormReader
18+
public class FormReader : IDisposable
1819
{
20+
private const int _rentedCharPoolLength = 8192;
1921
private readonly TextReader _reader;
20-
private readonly char[] _buffer = new char[1024];
22+
private readonly char[] _buffer;
23+
private readonly ArrayPool<char> _charPool;
2124
private readonly StringBuilder _builder = new StringBuilder();
2225
private int _bufferOffset;
2326
private int _bufferCount;
27+
private bool _disposed;
2428

2529
public FormReader(string data)
30+
: this(data, ArrayPool<char>.Shared)
31+
{
32+
}
33+
34+
public FormReader(string data, ArrayPool<char> charPool)
2635
{
2736
if (data == null)
2837
{
2938
throw new ArgumentNullException(nameof(data));
3039
}
3140

41+
_buffer = charPool.Rent(_rentedCharPoolLength);
42+
_charPool = charPool;
3243
_reader = new StringReader(data);
3344
}
3445

3546
public FormReader(Stream stream, Encoding encoding)
47+
: this(stream, encoding, ArrayPool<char>.Shared)
48+
{
49+
}
50+
51+
public FormReader(Stream stream, Encoding encoding, ArrayPool<char> charPool)
3652
{
3753
if (stream == null)
3854
{
@@ -44,6 +60,8 @@ public FormReader(Stream stream, Encoding encoding)
4460
throw new ArgumentNullException(nameof(encoding));
4561
}
4662

63+
_buffer = charPool.Rent(_rentedCharPoolLength);
64+
_charPool = charPool;
4765
_reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024 * 2, leaveOpen: true);
4866
}
4967

@@ -167,17 +185,18 @@ private async Task BufferAsync(CancellationToken cancellationToken)
167185
/// <returns>The collection containing the parsed HTTP form body.</returns>
168186
public static Dictionary<string, StringValues> ReadForm(string text)
169187
{
170-
var reader = new FormReader(text);
171-
172-
var accumulator = new KeyValueAccumulator();
173-
var pair = reader.ReadNextPair();
174-
while (pair.HasValue)
188+
using (var reader = new FormReader(text))
175189
{
176-
accumulator.Append(pair.Value.Key, pair.Value.Value);
177-
pair = reader.ReadNextPair();
178-
}
190+
var accumulator = new KeyValueAccumulator();
191+
var pair = reader.ReadNextPair();
192+
while (pair.HasValue)
193+
{
194+
accumulator.Append(pair.Value.Key, pair.Value.Value);
195+
pair = reader.ReadNextPair();
196+
}
179197

180-
return accumulator.GetResults();
198+
return accumulator.GetResults();
199+
}
181200
}
182201

183202
/// <summary>
@@ -200,17 +219,27 @@ public static Dictionary<string, StringValues> ReadForm(string text)
200219
/// <returns>The collection containing the parsed HTTP form body.</returns>
201220
public static async Task<Dictionary<string, StringValues>> ReadFormAsync(Stream stream, Encoding encoding, CancellationToken cancellationToken = new CancellationToken())
202221
{
203-
var reader = new FormReader(stream, encoding);
204-
205-
var accumulator = new KeyValueAccumulator();
206-
var pair = await reader.ReadNextPairAsync(cancellationToken);
207-
while (pair.HasValue)
222+
using (var reader = new FormReader(stream, encoding))
208223
{
209-
accumulator.Append(pair.Value.Key, pair.Value.Value);
210-
pair = await reader.ReadNextPairAsync(cancellationToken);
224+
var accumulator = new KeyValueAccumulator();
225+
var pair = await reader.ReadNextPairAsync(cancellationToken);
226+
while (pair.HasValue)
227+
{
228+
accumulator.Append(pair.Value.Key, pair.Value.Value);
229+
pair = await reader.ReadNextPairAsync(cancellationToken);
230+
}
231+
232+
return accumulator.GetResults();
211233
}
234+
}
212235

213-
return accumulator.GetResults();
236+
public void Dispose()
237+
{
238+
if (!_disposed)
239+
{
240+
_disposed = true;
241+
_charPool.Return(_buffer);
242+
}
214243
}
215244
}
216245
}

0 commit comments

Comments
 (0)