< Summary

Line coverage
53%
Covered lines: 94
Uncovered lines: 82
Coverable lines: 176
Total lines: 310
Line coverage: 53.4%
Branch coverage
50%
Covered branches: 17
Total branches: 34
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/System/Text/ValueStringBuilder.AppendSpanFormattable.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
 4namespace System.Text
 5{
 6    internal ref partial struct ValueStringBuilder
 7    {
 8        internal void AppendSpanFormattable<T>(T value, string? format = null, IFormatProvider? provider = null) where T
 10649        {
 106410            if (value.TryFormat(_chars.Slice(_pos), out int charsWritten, format, provider))
 102811            {
 102812                _pos += charsWritten;
 102813            }
 14            else
 3615            {
 3616                Append(value.ToString(format, provider));
 3617            }
 106418        }
 19    }
 20}
 21

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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)
 5835621        {
 5835622            _arrayToReturnToPool = null;
 5835623            _chars = initialBuffer;
 5835624            _pos = 0;
 5835625        }
 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        {
 5270436            get => _pos;
 37            set
 038            {
 039                Debug.Assert(value >= 0);
 040                Debug.Assert(value <= _chars.Length);
 041                _pos = value;
 042            }
 43        }
 44
 045        public int Capacity => _chars.Length;
 46
 47        public void EnsureCapacity(int capacity)
 048        {
 49            // This is not expected to be called this with negative capacity
 050            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
 053            if ((uint)capacity > (uint)_chars.Length)
 054                Grow(capacity - _pos);
 055        }
 56
 57        /// <summary>
 58        /// Ensures that the builder is terminated with a NUL character.
 59        /// </summary>
 60        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 61        public void NullTerminate()
 062        {
 063            EnsureCapacity(_pos + 1);
 064            _chars[_pos] = '\0';
 065        }
 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()
 074        {
 075            return ref MemoryMarshal.GetReference(_chars);
 076        }
 77
 78        public ref char this[int index]
 79        {
 80            get
 5668881            {
 5668882                Debug.Assert(index < _pos);
 5668883                return ref _chars[index];
 5668884            }
 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()
 494092        {
 494093            string s = _chars.Slice(0, _pos).ToString();
 494094            Dispose();
 494095            return s;
 494096        }
 97
 98        /// <summary>Returns the underlying storage of the builder.</summary>
 099        public Span<char> RawChars => _chars;
 100
 0101        public ReadOnlySpan<char> AsSpan() => _chars.Slice(0, _pos);
 0102        public ReadOnlySpan<char> AsSpan(int start) => _chars.Slice(start, _pos - start);
 52704103        public ReadOnlySpan<char> AsSpan(int start, int length) => _chars.Slice(start, length);
 104
 105        public void Insert(int index, char value, int count)
 0106        {
 0107            if (_pos > _chars.Length - count)
 0108            {
 0109                Grow(count);
 0110            }
 111
 0112            int remaining = _pos - index;
 0113            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
 0114            _chars.Slice(index, count).Fill(value);
 0115            _pos += count;
 0116        }
 117
 118        public void Insert(int index, string? s)
 0119        {
 0120            if (s == null)
 0121            {
 0122                return;
 123            }
 124
 0125            int count = s.Length;
 126
 0127            if (_pos > (_chars.Length - count))
 0128            {
 0129                Grow(count);
 0130            }
 131
 0132            int remaining = _pos - index;
 0133            _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count));
 0134            s
 0135#if !NET
 0136                .AsSpan()
 0137#endif
 0138                .CopyTo(_chars.Slice(index));
 0139            _pos += count;
 0140        }
 141
 142        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 143        public void Append(char c)
 1398436144        {
 1398436145            int pos = _pos;
 1398436146            Span<char> chars = _chars;
 1398436147            if ((uint)pos < (uint)chars.Length)
 1388900148            {
 1388900149                chars[pos] = c;
 1388900150                _pos = pos + 1;
 1388900151            }
 152            else
 9536153            {
 9536154                GrowAndAppend(c);
 9536155            }
 1398436156        }
 157
 158        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 159        public void Append(string? s)
 105568160        {
 105568161            if (s == null)
 0162            {
 0163                return;
 164            }
 165
 105568166            int pos = _pos;
 105568167            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
 105568173            {
 105568174                AppendSlow(s);
 105568175            }
 105568176        }
 177
 178        private void AppendSlow(string s)
 105568179        {
 105568180            int pos = _pos;
 105568181            if (pos > _chars.Length - s.Length)
 636182            {
 636183                Grow(s.Length);
 636184            }
 185
 105568186            s
 105568187#if !NET
 105568188                .AsSpan()
 105568189#endif
 105568190                .CopyTo(_chars.Slice(pos));
 105568191            _pos += s.Length;
 105568192        }
 193
 194        public void Append(char c, int count)
 0195        {
 0196            if (_pos > _chars.Length - count)
 0197            {
 0198                Grow(count);
 0199            }
 200
 0201            Span<char> dst = _chars.Slice(_pos, count);
 0202            for (int i = 0; i < dst.Length; i++)
 0203            {
 0204                dst[i] = c;
 0205            }
 0206            _pos += count;
 0207        }
 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)
 692223        {
 692224            int origPos = _pos;
 692225            if (origPos > _chars.Length - length)
 132226            {
 132227                Grow(length);
 132228            }
 229
 692230            _pos = origPos + length;
 692231            return _chars.Slice(origPos, length);
 692232        }
 233
 234        [MethodImpl(MethodImplOptions.NoInlining)]
 235        private void GrowAndAppend(char c)
 9536236        {
 9536237            Grow(1);
 9536238            Append(c);
 9536239        }
 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)
 10304251        {
 10304252            Debug.Assert(additionalCapacityBeyondPos > 0);
 10304253            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.
 10304259            int newCapacity = (int)Math.Max(
 10304260                (uint)(_pos + additionalCapacityBeyondPos),
 10304261                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.
 10304265            char[] poolArray = ArrayPool<char>.Shared.Rent(newCapacity);
 266
 10304267            _chars.Slice(0, _pos).CopyTo(poolArray);
 268
 10304269            char[]? toReturn = _arrayToReturnToPool;
 10304270            _chars = _arrayToReturnToPool = poolArray;
 10304271            if (toReturn != null)
 3860272            {
 3860273                ArrayPool<char>.Shared.Return(toReturn);
 3860274            }
 10304275        }
 276
 277        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 278        public void Dispose()
 58356279        {
 58356280            char[]? toReturn = _arrayToReturnToPool;
 58356281            this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again
 58356282            if (toReturn != null)
 6444283            {
 6444284                ArrayPool<char>.Shared.Return(toReturn);
 6444285            }
 58356286        }
 287    }
 288}
 289