IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
ValueStringBuilder.cs
Go to the documentation of this file.
1using System.Buffers;
2using System.Diagnostics;
4using System.Runtime.InteropServices;
5
6namespace System.Text
7{
8
12 internal ref partial struct ValueStringBuilder
13 {
14
15 char[]? _arrayToReturnToPool;
16 Span<char> _chars;
17 int _pos;
18
23 public ValueStringBuilder(Span<char> initialBuffer)
24 {
25 _arrayToReturnToPool = null;
26 _chars = initialBuffer;
27 _pos = 0;
28 }
29
34 public ValueStringBuilder(int initialCapacity)
35 {
36 _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
37 _chars = _arrayToReturnToPool;
38 _pos = 0;
39 }
40
44 public int Length
45 {
46 readonly get => _pos;
47 set
48 {
49 Debug.Assert(value >= 0);
50 Debug.Assert(value <= _chars.Length);
51 _pos = value;
52 }
53 }
54
58 public readonly int Capacity => _chars.Length;
59
64 public void EnsureCapacity(int capacity)
65 {
66 // This is not expected to be called this with negative capacity
67 Debug.Assert(capacity >= 0);
68
69 // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception.
70 if ((uint)capacity > (uint)_chars.Length)
71 Grow(capacity - _pos);
72 }
73
80 public ref char GetPinnableReference()
81 {
82 return ref MemoryMarshal.GetReference(_chars);
83 }
84
89 public ref char GetPinnableReference(bool terminate)
90 {
91 if (terminate)
92 {
93 EnsureCapacity(Length + 1);
94 _chars[Length] = '\0';
95 }
96
97 return ref MemoryMarshal.GetReference(_chars);
98 }
99
105 public ref char this[int index]
106 {
107 get
108 {
109 Debug.Assert(index < _pos);
110 return ref _chars[index];
111 }
112 }
113
118 public override string ToString()
119 {
120 var s = _chars.Slice(0, _pos).ToString();
121 Dispose();
122 return s;
123 }
124
126 public readonly Span<char> RawChars => _chars;
127
132 public ReadOnlySpan<char> AsSpan(bool terminate)
133 {
134 if (terminate)
135 {
136 EnsureCapacity(Length + 1);
137 _chars[Length] = '\0';
138 }
139
140 return _chars.Slice(0, _pos);
141 }
142
147 public readonly ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
148
154 public readonly ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start);
155
162 public readonly ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length);
163
170 public bool TryCopyTo(Span<char> destination, out int charsWritten)
171 {
172 if (_chars.Slice(0, _pos).TryCopyTo(destination))
173 {
174 charsWritten = _pos;
175 Dispose();
176 return true;
177 }
178 else
179 {
180 charsWritten = 0;
181 Dispose();
182 return false;
183 }
184 }
185
192 public void Insert(int index, char value, int count)
193 {
194 if (_pos > _chars.Length - count)
195 {
196 Grow(count);
197 }
198
199 int remaining = _pos - index;
200 _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
201 _chars.Slice(index, count).Fill(value);
202 _pos += count;
203 }
204
210 public void Insert(int index, string? s)
211 {
212 if (s == null)
213 return;
214
215 int count = s.Length;
216
217 if (_pos > _chars.Length - count)
218 Grow(count);
219
220 int remaining = _pos - index;
221 _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
222 s
223#if !NET
224 .AsSpan()
225#endif
226 .CopyTo(_chars.Slice(index));
227 _pos += count;
228 }
229
234 [MethodImpl(MethodImplOptions.AggressiveInlining)]
235 public void Append(char c)
236 {
237 var pos = _pos;
238 var chars = _chars;
239 if ((uint)pos < (uint)chars.Length)
240 {
241 chars[pos] = c;
242 _pos = pos + 1;
243 }
244 else
245 {
246 GrowAndAppend(c);
247 }
248 }
249
254 [MethodImpl(MethodImplOptions.AggressiveInlining)]
255 public void Append(string? s)
256 {
257 if (s == null)
258 {
259 return;
260 }
261
262 int pos = _pos;
263 if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
264 {
265 _chars[pos] = s[0];
266 _pos = pos + 1;
267 }
268 else
269 {
270 AppendSlow(s);
271 }
272 }
273
278 void AppendSlow(string s)
279 {
280 int pos = _pos;
281 if (pos > _chars.Length - s.Length)
282 {
283 Grow(s.Length);
284 }
285
286 s
287#if !NET
288 .AsSpan()
289#endif
290 .CopyTo(_chars.Slice(pos));
291 _pos += s.Length;
292 }
293
299 public void Append(char c, int count)
300 {
301 if (_pos > _chars.Length - count)
302 Grow(count);
303
304 var dst = _chars.Slice(_pos, count);
305 for (int i = 0; i < dst.Length; i++)
306 dst[i] = c;
307
308 _pos += count;
309 }
310
316 public unsafe void Append(char* value, int length)
317 {
318 var pos = _pos;
319 if (pos > _chars.Length - length)
320 Grow(length);
321
322 var dst = _chars.Slice(_pos, length);
323 for (int i = 0; i < dst.Length; i++)
324 dst[i] = *value++;
325
326 _pos += length;
327 }
328
333 public void Append(scoped ReadOnlySpan<char> value)
334 {
335 var pos = _pos;
336 if (pos > _chars.Length - value.Length)
337 Grow(value.Length);
338
339 value.CopyTo(_chars.Slice(_pos));
340 _pos += value.Length;
341 }
342
348 [MethodImpl(MethodImplOptions.AggressiveInlining)]
349 public Span<char> AppendSpan(int length)
350 {
351 int origPos = _pos;
352 if (origPos > _chars.Length - length)
353 Grow(length);
354
355 _pos = origPos + length;
356 return _chars.Slice(origPos, length);
357 }
358
363 [MethodImpl(MethodImplOptions.NoInlining)]
364 void GrowAndAppend(char c)
365 {
366 Grow(1);
367 Append(c);
368 }
369
376 public void Remove(int startIndex, int length)
377 {
378 if (startIndex < 0)
379 throw new ArgumentOutOfRangeException(nameof(startIndex));
380 if (length < 0)
381 throw new ArgumentOutOfRangeException(nameof(length));
382 if (length > Length - startIndex)
383 throw new ArgumentOutOfRangeException(nameof(length));
384
385 if (Length == length && startIndex == 0)
386 {
387 Length = 0;
388 return;
389 }
390
391 if (length > 0)
392 {
393 var src = _chars.Slice(startIndex + length);
394 var dst = _chars.Slice(startIndex);
395 src.CopyTo(dst);
396 Length -= length;
397 }
398 }
399
408 [MethodImpl(MethodImplOptions.NoInlining)]
409 private void Grow(int additionalCapacityBeyondPos)
410 {
411 Debug.Assert(additionalCapacityBeyondPos > 0);
412 Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed.");
413
414 const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
415
416 // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
417 // to double the size if possible, bounding the doubling to not go beyond the max array length.
418 int newCapacity = (int)Math.Max(
419 (uint)(_pos + additionalCapacityBeyondPos),
420 Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
421
422 // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
423 // This could also go negative if the actual required length wraps around.
424 var poolArray = ArrayPool<char>.Shared.Rent(newCapacity);
425
426 _chars.Slice(0, _pos).CopyTo(poolArray);
427
428 var toReturn = _arrayToReturnToPool;
429 _chars = _arrayToReturnToPool = poolArray;
430 if (toReturn != null)
431 ArrayPool<char>.Shared.Return(toReturn);
432 }
433
437 [MethodImpl(MethodImplOptions.AggressiveInlining)]
438 public void Dispose()
439 {
440 var toReturn = _arrayToReturnToPool;
441 this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
442 if (toReturn != null)
443 ArrayPool<char>.Shared.Return(toReturn);
444 }
445
446 }
447
448}