< Summary

Information
Line coverage
0%
Covered lines: 0
Uncovered lines: 93
Coverable lines: 93
Total lines: 288
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
.ctor(...)100%110%
ToString()100%110%
AsSpan()100%110%
Append(...)0%220%
Append(...)0%660%
AppendSlow(...)0%220%
Append(...)0%220%
GrowAndAppend(...)100%110%
Grow(...)0%220%
Dispose()0%220%

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\Common\src\System\Text\ValueStringBuilder.cs

#LineLine coverage
 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
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Runtime.CompilerServices;
 7using System.Runtime.InteropServices;
 8
 9#nullable enable
 10
 11namespace System.Text
 12{
 13    [DebuggerDisplay("{DebuggerDisplay,nq}")]
 14    internal ref partial struct ValueStringBuilder
 15    {
 16        private char[]? _arrayToReturnToPool;
 17        private Span<char> _chars;
 18        private int _pos;
 19
 20        public ValueStringBuilder(Span<char> initialBuffer)
 021        {
 022            _arrayToReturnToPool = null;
 023            _chars = initialBuffer;
 024            _pos = 0;
 025        }
 26
 27        public ValueStringBuilder(int initialCapacity)
 028        {
 029            _arrayToReturnToPool = ArrayPool<char>.Shared.Rent(initialCapacity);
 030            _chars = _arrayToReturnToPool;
 031            _pos = 0;
 032        }
 33
 34        public int Length
 35        {
 036            get => _pos;
 37            set
 38            {
 39                Debug.Assert(value >= 0);
 40                Debug.Assert(value <= _chars.Length);
 41                _pos = value;
 42            }
 43        }
 44
 45        public int Capacity => _chars.Length;
 46
 47        public void EnsureCapacity(int capacity)
 48        {
 49            // This is not expected to be called this with negative capacity
 50            Debug.Assert(capacity >= 0);
 51
 52            // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an excepti
 53            if ((uint)capacity > (uint)_chars.Length)
 54                Grow(capacity - _pos);
 55        }
 56
 57        /// <summary>
 58        /// Ensures that the builder is terminated with a NUL character.
 59        /// </summary>
 60        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 61        public void NullTerminate()
 62        {
 63            EnsureCapacity(_pos + 1);
 64            _chars[_pos] = '\0';
 65        }
 66
 67        /// <summary>
 68        /// Get a pinnable reference to the builder.
 69        /// Does not ensure there is a null char after <see cref="Length"/>
 70        /// This overload is pattern matched in the C# 7.3+ compiler so you can omit
 71        /// the explicit method call, and write eg "fixed (char* c = builder)"
 72        /// </summary>
 73        public ref char GetPinnableReference()
 74        {
 75            return ref MemoryMarshal.GetReference(_chars);
 76        }
 77
 78        public ref char this[int index]
 79        {
 80            get
 81            {
 82                Debug.Assert(index < _pos);
 83                return ref _chars[index];
 84            }
 85        }
 86
 87        // ToString() clears the builder, so we need a side-effect free debugger display.
 88        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 089        private string DebuggerDisplay => AsSpan().ToString();
 90
 91        public override string ToString()
 092        {
 093            string s = _chars.Slice(0, _pos).ToString();
 094            Dispose();
 095            return s;
 096        }
 97
 98        /// <summary>Returns the underlying storage of the builder.</summary>
 99        public Span<char> RawChars => _chars;
 100
 0101        public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
 102        public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start);
 103        public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length);
 104
 105        public void Insert(int index, char value, int count)
 106        {
 107            if (_pos > _chars.Length - count)
 108            {
 109                Grow(count);
 110            }
 111
 112            int remaining = _pos - index;
 113            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
 114            _chars.Slice(index, count).Fill(value);
 115            _pos += count;
 116        }
 117
 118        public void Insert(int index, string? s)
 119        {
 120            if (s == null)
 121            {
 122                return;
 123            }
 124
 125            int count = s.Length;
 126
 127            if (_pos > (_chars.Length - count))
 128            {
 129                Grow(count);
 130            }
 131
 132            int remaining = _pos - index;
 133            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
 134            s
 135#if !NET
 136                .AsSpan()
 137#endif
 138                .CopyTo(_chars.Slice(index));
 139            _pos += count;
 140        }
 141
 142        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 143        public void Append(char c)
 0144        {
 0145            int pos = _pos;
 0146            Span<char> chars = _chars;
 0147            if ((uint)pos < (uint)chars.Length)
 0148            {
 0149                chars[pos] = c;
 0150                _pos = pos + 1;
 0151            }
 152            else
 0153            {
 0154                GrowAndAppend(c);
 0155            }
 0156        }
 157
 158        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 159        public void Append(string? s)
 0160        {
 0161            if (s == null)
 0162            {
 0163                return;
 164            }
 165
 0166            int pos = _pos;
 0167            if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from Numbe
 0168            {
 0169                _chars[pos] = s[0];
 0170                _pos = pos + 1;
 0171            }
 172            else
 0173            {
 0174                AppendSlow(s);
 0175            }
 0176        }
 177
 178        private void AppendSlow(string s)
 0179        {
 0180            int pos = _pos;
 0181            if (pos > _chars.Length - s.Length)
 0182            {
 0183                Grow(s.Length);
 0184            }
 185
 0186            s
 0187#if !NET
 0188                .AsSpan()
 0189#endif
 0190                .CopyTo(_chars.Slice(pos));
 0191            _pos += s.Length;
 0192        }
 193
 194        public void Append(char c, int count)
 195        {
 196            if (_pos > _chars.Length - count)
 197            {
 198                Grow(count);
 199            }
 200
 201            Span<char> dst = _chars.Slice(_pos, count);
 202            for (int i = 0; i < dst.Length; i++)
 203            {
 204                dst[i] = c;
 205            }
 206            _pos += count;
 207        }
 208
 209        public void Append(scoped ReadOnlySpan<char> value)
 0210        {
 0211            int pos = _pos;
 0212            if (pos > _chars.Length - value.Length)
 0213            {
 0214                Grow(value.Length);
 0215            }
 216
 0217            value.CopyTo(_chars.Slice(_pos));
 0218            _pos += value.Length;
 0219        }
 220
 221        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 222        public Span<char> AppendSpan(int length)
 223        {
 224            int origPos = _pos;
 225            if (origPos > _chars.Length - length)
 226            {
 227                Grow(length);
 228            }
 229
 230            _pos = origPos + length;
 231            return _chars.Slice(origPos, length);
 232        }
 233
 234        [MethodImpl(MethodImplOptions.NoInlining)]
 235        private void GrowAndAppend(char c)
 0236        {
 0237            Grow(1);
 0238            Append(c);
 0239        }
 240
 241        /// <summary>
 242        /// Resize the internal buffer either by doubling current buffer size or
 243        /// by adding <paramref name="additionalCapacityBeyondPos"/> to
 244        /// <see cref="_pos"/> whichever is greater.
 245        /// </summary>
 246        /// <param name="additionalCapacityBeyondPos">
 247        /// Number of chars requested beyond current position.
 248        /// </param>
 249        [MethodImpl(MethodImplOptions.NoInlining)]
 250        private void Grow(int additionalCapacityBeyondPos)
 0251        {
 0252            Debug.Assert(additionalCapacityBeyondPos > 0);
 0253            Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is need
 254
 255            const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
 256
 257            // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try
 258            // to double the size if possible, bounding the doubling to not go beyond the max array length.
 0259            int newCapacity = (int)Math.Max(
 0260                (uint)(_pos + additionalCapacityBeyondPos),
 0261                Math.Min((uint)_chars.Length * 2, ArrayMaxLength));
 262
 263            // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative.
 264            // This could also go negative if the actual required length wraps around.
 0265            char[] poolArray = ArrayPool<char>.Shared.Rent(newCapacity);
 266
 0267            _chars.Slice(0, _pos).CopyTo(poolArray);
 268
 0269            char[]? toReturn = _arrayToReturnToPool;
 0270            _chars = _arrayToReturnToPool = poolArray;
 0271            if (toReturn != null)
 0272            {
 0273                ArrayPool<char>.Shared.Return(toReturn);
 0274            }
 0275        }
 276
 277        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 278        public void Dispose()
 0279        {
 0280            char[]? toReturn = _arrayToReturnToPool;
 0281            this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
 0282            if (toReturn != null)
 0283            {
 0284                ArrayPool<char>.Shared.Return(toReturn);
 0285            }
 0286        }
 287    }
 288}