1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System
.Diagnostics
;
7 namespace System
.Buffers
10 /// Represents a heap-based, array-backed output sink into which <typeparam name="T"/> data can be written.
12 public sealed class ArrayBufferWriter
<T
> : IBufferWriter
<T
>
17 private const int DefaultInitialBufferSize
= 256;
20 /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
21 /// with the default initial capacity.
23 public ArrayBufferWriter()
25 _buffer
= Array
.Empty
<T
>();
30 /// Creates an instance of an <see cref="ArrayBufferWriter{T}"/>, in which data can be written to,
31 /// with an initial capacity specified.
33 /// <param name="initialCapacity">The minimum capacity with which to initialize the underlying buffer.</param>
34 /// <exception cref="ArgumentException">
35 /// Thrown when <paramref name="initialCapacity"/> is not positive (i.e. less than or equal to 0).
37 public ArrayBufferWriter(int initialCapacity
)
39 if (initialCapacity
<= 0)
40 throw new ArgumentException(nameof(initialCapacity
));
42 _buffer
= new T
[initialCapacity
];
47 /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlyMemory{T}"/>.
49 public ReadOnlyMemory
<T
> WrittenMemory
=> _buffer
.AsMemory(0, _index
);
52 /// Returns the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
54 public ReadOnlySpan
<T
> WrittenSpan
=> _buffer
.AsSpan(0, _index
);
57 /// Returns the amount of data written to the underlying buffer so far.
59 public int WrittenCount
=> _index
;
62 /// Returns the total amount of space within the underlying buffer.
64 public int Capacity
=> _buffer
.Length
;
67 /// Returns the amount of space available that can still be written into without forcing the underlying buffer to grow.
69 public int FreeCapacity
=> _buffer
.Length
- _index
;
72 /// Clears the data written to the underlying buffer.
75 /// You must clear the <see cref="ArrayBufferWriter{T}"/> before trying to re-use it.
79 Debug
.Assert(_buffer
.Length
>= _index
);
80 _buffer
.AsSpan(0, _index
).Clear();
85 /// Notifies <see cref="IBufferWriter{T}"/> that <paramref name="count"/> amount of data was written to the output <see cref="Span{T}"/>/<see cref="Memory{T}"/>
87 /// <exception cref="ArgumentException">
88 /// Thrown when <paramref name="count"/> is negative.
90 /// <exception cref="InvalidOperationException">
91 /// Thrown when attempting to advance past the end of the underlying buffer.
94 /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
96 public void Advance(int count
)
99 throw new ArgumentException(nameof(count
));
101 if (_index
> _buffer
.Length
- count
)
102 ThrowInvalidOperationException_AdvancedTooFar(_buffer
.Length
);
108 /// Returns a <see cref="Memory{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
109 /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
111 /// <exception cref="ArgumentException">
112 /// Thrown when <paramref name="sizeHint"/> is negative.
115 /// This will never return an empty <see cref="Memory{T}"/>.
118 /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
121 /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
123 public Memory
<T
> GetMemory(int sizeHint
= 0)
125 CheckAndResizeBuffer(sizeHint
);
126 Debug
.Assert(_buffer
.Length
> _index
);
127 return _buffer
.AsMemory(_index
);
131 /// Returns a <see cref="Span{T}"/> to write to that is at least the requested length (specified by <paramref name="sizeHint"/>).
132 /// If no <paramref name="sizeHint"/> is provided (or it's equal to <code>0</code>), some non-empty buffer is returned.
134 /// <exception cref="ArgumentException">
135 /// Thrown when <paramref name="sizeHint"/> is negative.
138 /// This will never return an empty <see cref="Span{T}"/>.
141 /// There is no guarantee that successive calls will return the same buffer or the same-sized buffer.
144 /// You must request a new buffer after calling Advance to continue writing more data and cannot write to a previously acquired buffer.
146 public Span
<T
> GetSpan(int sizeHint
= 0)
148 CheckAndResizeBuffer(sizeHint
);
149 Debug
.Assert(_buffer
.Length
> _index
);
150 return _buffer
.AsSpan(_index
);
153 private void CheckAndResizeBuffer(int sizeHint
)
156 throw new ArgumentException(nameof(sizeHint
));
163 if (sizeHint
> FreeCapacity
)
165 int growBy
= Math
.Max(sizeHint
, _buffer
.Length
);
167 if (_buffer
.Length
== 0)
169 growBy
= Math
.Max(growBy
, DefaultInitialBufferSize
);
172 int newSize
= checked(_buffer
.Length
+ growBy
);
174 Array
.Resize(ref _buffer
, newSize
);
177 Debug
.Assert(FreeCapacity
> 0 && FreeCapacity
>= sizeHint
);
180 private static void ThrowInvalidOperationException_AdvancedTooFar(int capacity
)
182 throw new InvalidOperationException(SR
.Format(SR
.BufferWriterAdvancedTooFar
, capacity
));