Skip to content

Commit 07fdef8

Browse files
committed
Ported the ArrayWriter changes.
1 parent b80e7f8 commit 07fdef8

File tree

1 file changed

+152
-12
lines changed

1 file changed

+152
-12
lines changed

src/HotChocolate/Utilities/src/Utilities/ArrayWriter.cs

Lines changed: 152 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,223 @@
33

44
namespace HotChocolate.Utilities
55
{
6-
internal sealed class ArrayWriter
7-
: IBufferWriter<byte>
8-
, IDisposable
6+
/// <summary>
7+
/// A <see cref="IBufferWriter{T}"/> that writes to a rented buffer.
8+
/// </summary>
9+
internal sealed class ArrayWriter : IBufferWriter<byte>, IDisposable
910
{
1011
private const int _initialBufferSize = 512;
1112
private byte[] _buffer;
1213
private int _capacity;
1314
private int _start;
1415
private bool _disposed;
15-
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="ArrayWriter"/> class.
19+
/// </summary>
1620
public ArrayWriter()
1721
{
1822
_buffer = ArrayPool<byte>.Shared.Rent(_initialBufferSize);
1923
_capacity = _buffer.Length;
2024
_start = 0;
2125
}
2226

27+
/// <summary>
28+
/// Gets the number of bytes written to the buffer.
29+
/// </summary>
2330
public int Length => _start;
2431

32+
/// <summary>
33+
/// Gets the underlying buffer.
34+
/// </summary>
35+
/// <returns>
36+
/// The underlying buffer.
37+
/// </returns>
38+
/// <remarks>
39+
/// Accessing the underlying buffer directly is not recommended.
40+
/// If possible use <see cref="GetWrittenMemory"/> or <see cref="GetWrittenSpan"/>.
41+
/// </remarks>
42+
public byte[] GetInternalBuffer() => _buffer;
43+
44+
/// <summary>
45+
/// Gets the part of the buffer that has been written to.
46+
/// </summary>
47+
/// <returns>
48+
/// A <see cref="ReadOnlyMemory{T}"/> of the written portion of the buffer.
49+
/// </returns>
50+
public ReadOnlyMemory<byte> GetWrittenMemory()
51+
=> _buffer.AsMemory().Slice(0, _start);
52+
2553
public ReadOnlyMemory<byte> Body => _buffer.AsMemory().Slice(0, _start);
2654

27-
public byte[] GetInternalBuffer() => _buffer;
55+
/// <summary>
56+
/// Gets the part of the buffer that has been written to.
57+
/// </summary>
58+
/// <returns>
59+
/// A <see cref="ReadOnlySpan{T}"/> of the written portion of the buffer.
60+
/// </returns>
61+
public ReadOnlySpan<byte> GetWrittenSpan()
62+
=> _buffer.AsSpan().Slice(0, _start);
2863

64+
/// <summary>
65+
/// Advances the writer by the specified number of bytes.
66+
/// </summary>
67+
/// <param name="count">
68+
/// The number of bytes to advance the writer by.
69+
/// </param>
70+
/// <exception cref="ArgumentOutOfRangeException">
71+
/// Thrown if <paramref name="count"/> is negative or
72+
/// if <paramref name="count"/> is greater than the
73+
/// available capacity on the internal buffer.
74+
/// </exception>
2975
public void Advance(int count)
3076
{
77+
if (_disposed)
78+
{
79+
throw new ObjectDisposedException(nameof(ArrayWriter));
80+
}
81+
82+
if (count < 0)
83+
{
84+
throw new ArgumentOutOfRangeException(nameof(count));
85+
}
86+
87+
if (count > _capacity)
88+
{
89+
throw new ArgumentOutOfRangeException(
90+
nameof(count),
91+
count,
92+
"ArrayWriter_Advance_BufferOverflow");
93+
}
94+
3195
_start += count;
3296
_capacity -= count;
3397
}
3498

99+
/// <summary>
100+
/// Gets a <see cref="Memory{T}"/> to write to.
101+
/// </summary>
102+
/// <param name="sizeHint">
103+
/// The minimum size of the returned <see cref="Memory{T}"/>.
104+
/// </param>
105+
/// <returns>
106+
/// A <see cref="Memory{T}"/> to write to.
107+
/// </returns>
108+
/// <exception cref="ArgumentOutOfRangeException">
109+
/// Thrown if <paramref name="sizeHint"/> is negative.
110+
/// </exception>
35111
public Memory<byte> GetMemory(int sizeHint = 0)
36112
{
37-
var size = sizeHint < 1 ? _initialBufferSize : sizeHint;
113+
if (_disposed)
114+
{
115+
throw new ObjectDisposedException(nameof(ArrayWriter));
116+
}
117+
118+
if (sizeHint < 0)
119+
{
120+
throw new ArgumentOutOfRangeException(nameof(sizeHint));
121+
}
122+
123+
var size = sizeHint < 1
124+
? _initialBufferSize
125+
: sizeHint;
38126
EnsureBufferCapacity(size);
39127
return _buffer.AsMemory().Slice(_start, size);
40128
}
41129

130+
/// <summary>
131+
/// Gets a <see cref="Span{T}"/> to write to.
132+
/// </summary>
133+
/// <param name="sizeHint">
134+
/// The minimum size of the returned <see cref="Span{T}"/>.
135+
/// </param>
136+
/// <returns>
137+
/// A <see cref="Span{T}"/> to write to.
138+
/// </returns>
139+
/// <exception cref="ArgumentOutOfRangeException">
140+
/// Thrown if <paramref name="sizeHint"/> is negative.
141+
/// </exception>
42142
public Span<byte> GetSpan(int sizeHint = 0)
43143
{
44-
var size = sizeHint < 1 ? _initialBufferSize : sizeHint;
144+
if (_disposed)
145+
{
146+
throw new ObjectDisposedException(nameof(ArrayWriter));
147+
}
148+
149+
if (sizeHint < 0)
150+
{
151+
throw new ArgumentOutOfRangeException(nameof(sizeHint));
152+
}
153+
154+
var size = sizeHint < 1
155+
? _initialBufferSize
156+
: sizeHint;
45157
EnsureBufferCapacity(size);
46158
return _buffer.AsSpan().Slice(_start, size);
47159
}
48160

161+
/// <summary>
162+
/// Gets the buffer as an <see cref="ArraySegment{T}"/>
163+
/// </summary>
164+
/// <returns></returns>
165+
public ArraySegment<byte> ToArraySegment() => new(_buffer, 0, _start);
166+
167+
/// <summary>
168+
/// Ensures that the internal buffer has the needed capacity.
169+
/// </summary>
170+
/// <param name="neededCapacity">
171+
/// The needed capacity on the internal buffer.
172+
/// </param>
49173
private void EnsureBufferCapacity(int neededCapacity)
50174
{
175+
// check if we have enough capacity available on the buffer.
51176
if (_capacity < neededCapacity)
52177
{
53-
byte[] buffer = _buffer;
178+
// if we need to expand the buffer we first capture the original buffer.
179+
var buffer = _buffer;
180+
181+
// next we determine the new size of the buffer, we at least double the size to avoid
182+
// expanding the buffer too often.
183+
var newSize = buffer.Length * 2;
54184

55-
int newSize = buffer.Length * 2;
56-
if (neededCapacity > buffer.Length)
185+
// if that new buffer size is not enough to satisfy the needed capacity
186+
// we add the needed capacity to the doubled buffer capacity.
187+
if (neededCapacity > newSize - _start)
57188
{
58189
newSize += neededCapacity;
59190
}
60191

192+
// next we will rent a new array from the array pool that supports
193+
// the new capacity requirements.
61194
_buffer = ArrayPool<byte>.Shared.Rent(newSize);
195+
196+
// the rented array might have a larger size than the needed capacity,
197+
// so we will take the buffer length and calculate from that the free capacity.
62198
_capacity += _buffer.Length - buffer.Length;
63199

200+
// finally we copy the data from the original buffer to the new buffer.
64201
buffer.AsSpan().CopyTo(_buffer);
202+
203+
// last but not least we return the original buffer to the array pool.
65204
ArrayPool<byte>.Shared.Return(buffer);
66205
}
67206
}
68207

69208
public void Clear()
70209
{
71-
ArrayPool<byte>.Shared.Return(_buffer);
72-
_buffer = ArrayPool<byte>.Shared.Rent(_initialBufferSize);
73210
_capacity = _buffer.Length;
74211
_start = 0;
75212
}
76213

214+
/// <inheritdoc/>
77215
public void Dispose()
78216
{
79217
if (!_disposed)
80218
{
81219
ArrayPool<byte>.Shared.Return(_buffer);
82220
_buffer = Array.Empty<byte>();
221+
_capacity = 0;
222+
_start = 0;
83223
_disposed = true;
84224
}
85225
}

0 commit comments

Comments
 (0)