Remove redundant unsafe contexts (dotnet/coreclr#21826)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Text / ValueStringBuilder.cs
blob963e45825c88b9527b2a9af7d30a35e674222b4b
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.Buffers;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
10 namespace System.Text
12 internal ref partial struct ValueStringBuilder
14 private char[] _arrayToReturnToPool;
15 private Span<char> _chars;
16 private int _pos;
18 public ValueStringBuilder(Span<char> initialBuffer)
20 _arrayToReturnToPool = null;
21 _chars = initialBuffer;
22 _pos = 0;
25 public ValueStringBuilder(int initialCapacity)
27 _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
28 _chars = _arrayToReturnToPool;
29 _pos = 0;
32 public int Length
34 get => _pos;
35 set
37 Debug.Assert(value >= 0);
38 Debug.Assert(value <= _chars.Length);
39 _pos = value;
43 public int Capacity => _chars.Length;
45 public void EnsureCapacity(int capacity)
47 if (capacity > _chars.Length)
48 Grow(capacity - _chars.Length);
51 /// <summary>
52 /// Get a pinnable reference to the builder.
53 /// Does not ensure there is a null char after <see cref="Length"/>
54 /// This overload is pattern matched in the C# 7.3+ compiler so you can omit
55 /// the explicit method call, and write eg "fixed (char* c = builder)"
56 /// </summary>
57 public ref char GetPinnableReference()
59 return ref MemoryMarshal.GetReference(_chars);
62 /// <summary>
63 /// Get a pinnable reference to the builder.
64 /// </summary>
65 /// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
66 public ref char GetPinnableReference(bool terminate)
68 if (terminate)
70 EnsureCapacity(Length + 1);
71 _chars[Length] = '\0';
73 return ref MemoryMarshal.GetReference(_chars);
76 public ref char this[int index]
78 get
80 Debug.Assert(index < _pos);
81 return ref _chars[index];
85 public override string ToString()
87 var s = _chars.Slice(0, _pos).ToString();
88 Dispose();
89 return s;
92 /// <summary>Returns the underlying storage of the builder.</summary>
93 public Span<char> RawChars => _chars;
95 /// <summary>
96 /// Returns a span around the contents of the builder.
97 /// </summary>
98 /// <param name="terminate">Ensures that the builder has a null char after <see cref="Length"/></param>
99 public ReadOnlySpan<char> AsSpan(bool terminate)
101 if (terminate)
103 EnsureCapacity(Length + 1);
104 _chars[Length] = '\0';
106 return _chars.Slice(0, _pos);
109 public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
110 public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start);
111 public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length);
113 public bool TryCopyTo(Span<char> destination, out int charsWritten)
115 if (_chars.Slice(0, _pos).TryCopyTo(destination))
117 charsWritten = _pos;
118 Dispose();
119 return true;
121 else
123 charsWritten = 0;
124 Dispose();
125 return false;
129 public void Insert(int index, char value, int count)
131 if (_pos > _chars.Length - count)
133 Grow(count);
136 int remaining = _pos - index;
137 _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
138 _chars.Slice(index, count).Fill(value);
139 _pos += count;
142 public void Insert(int index, string s)
144 int count = s.Length;
146 if (_pos > (_chars.Length - count))
148 Grow(count);
151 int remaining = _pos - index;
152 _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
153 s.AsSpan().CopyTo(_chars.Slice(index));
154 _pos += count;
157 [MethodImpl(MethodImplOptions.AggressiveInlining)]
158 public void Append(char c)
160 int pos = _pos;
161 if ((uint)pos < (uint)_chars.Length)
163 _chars[pos] = c;
164 _pos = pos + 1;
166 else
168 GrowAndAppend(c);
172 [MethodImpl(MethodImplOptions.AggressiveInlining)]
173 public void Append(string s)
175 int pos = _pos;
176 if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
178 _chars[pos] = s[0];
179 _pos = pos + 1;
181 else
183 AppendSlow(s);
187 private void AppendSlow(string s)
189 int pos = _pos;
190 if (pos > _chars.Length - s.Length)
192 Grow(s.Length);
195 s.AsSpan().CopyTo(_chars.Slice(pos));
196 _pos += s.Length;
199 public void Append(char c, int count)
201 if (_pos > _chars.Length - count)
203 Grow(count);
206 Span<char> dst = _chars.Slice(_pos, count);
207 for (int i = 0; i < dst.Length; i++)
209 dst[i] = c;
211 _pos += count;
214 public unsafe void Append(char* value, int length)
216 int pos = _pos;
217 if (pos > _chars.Length - length)
219 Grow(length);
222 Span<char> dst = _chars.Slice(_pos, length);
223 for (int i = 0; i < dst.Length; i++)
225 dst[i] = *value++;
227 _pos += length;
230 public void Append(ReadOnlySpan<char> value)
232 int pos = _pos;
233 if (pos > _chars.Length - value.Length)
235 Grow(value.Length);
238 value.CopyTo(_chars.Slice(_pos));
239 _pos += value.Length;
242 [MethodImpl(MethodImplOptions.AggressiveInlining)]
243 public Span<char> AppendSpan(int length)
245 int origPos = _pos;
246 if (origPos > _chars.Length - length)
248 Grow(length);
251 _pos = origPos + length;
252 return _chars.Slice(origPos, length);
255 [MethodImpl(MethodImplOptions.NoInlining)]
256 private void GrowAndAppend(char c)
258 Grow(1);
259 Append(c);
262 [MethodImpl(MethodImplOptions.NoInlining)]
263 private void Grow(int requiredAdditionalCapacity)
265 Debug.Assert(requiredAdditionalCapacity > 0);
267 char[] poolArray = ArrayPool<char>.Shared.Rent(Math.Max(_pos + requiredAdditionalCapacity, _chars.Length * 2));
269 _chars.CopyTo(poolArray);
271 char[] toReturn = _arrayToReturnToPool;
272 _chars = _arrayToReturnToPool = poolArray;
273 if (toReturn != null)
275 ArrayPool<char>.Shared.Return(toReturn);
279 [MethodImpl(MethodImplOptions.AggressiveInlining)]
280 public void Dispose()
282 char[] toReturn = _arrayToReturnToPool;
283 this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
284 if (toReturn != null)
286 ArrayPool<char>.Shared.Return(toReturn);