< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 64
Coverable lines: 64
Total lines: 256
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 14
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%
Append(...)0%220%
AddWithResize(...)100%110%
AsSpan()100%110%
Dispose()0%440%
Grow(...)0%880%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/Common/src/System/Collections/Generic/ValueListBuilder.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;
 7
 8namespace System.Collections.Generic
 9{
 10    internal ref partial struct ValueListBuilder<T>
 11    {
 12        private Span<T> _span;
 13        private T[]? _arrayFromPool;
 14        private int _pos;
 15
 16        public ValueListBuilder(Span<T?> scratchBuffer)
 17        {
 18            _span = scratchBuffer!;
 19        }
 20
 21        public ValueListBuilder(int capacity)
 022        {
 023            Grow(capacity);
 024        }
 25
 26        public int Length
 27        {
 028            get => _pos;
 29            set
 30            {
 31                Debug.Assert(value >= 0);
 32                Debug.Assert(value <= _span.Length);
 33                _pos = value;
 34            }
 35        }
 36
 37        public ref T this[int index]
 38        {
 39            get
 040            {
 041                Debug.Assert(index < _pos);
 042                return ref _span[index];
 043            }
 44        }
 45
 46        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 47        public void Append(T item)
 048        {
 049            int pos = _pos;
 50
 51            // Workaround for https://github.com/dotnet/runtime/issues/72004
 052            Span<T> span = _span;
 053            if ((uint)pos < (uint)span.Length)
 054            {
 055                span[pos] = item;
 056                _pos = pos + 1;
 057            }
 58            else
 059            {
 060                AddWithResize(item);
 061            }
 062        }
 63
 64        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 65        public void Append(scoped ReadOnlySpan<T> source)
 66        {
 67            int pos = _pos;
 68            Span<T> span = _span;
 69            if (source.Length == 1 && (uint)pos < (uint)span.Length)
 70            {
 71                span[pos] = source[0];
 72                _pos = pos + 1;
 73            }
 74            else
 75            {
 76                AppendMultiChar(source);
 77            }
 78        }
 79
 80        [MethodImpl(MethodImplOptions.NoInlining)]
 81        private void AppendMultiChar(scoped ReadOnlySpan<T> source)
 82        {
 83            if ((uint)(_pos + source.Length) > (uint)_span.Length)
 84            {
 85                Grow(source.Length);
 86            }
 87
 88            source.CopyTo(_span.Slice(_pos));
 89            _pos += source.Length;
 90        }
 91
 92        public void Insert(int index, scoped ReadOnlySpan<T> source)
 93        {
 94            Debug.Assert(index == 0, "Implementation currently only supports index == 0");
 95
 96            if ((uint)(_pos + source.Length) > (uint)_span.Length)
 97            {
 98                Grow(source.Length);
 99            }
 100
 101            _span.Slice(0, _pos).CopyTo(_span.Slice(source.Length));
 102            source.CopyTo(_span);
 103            _pos += source.Length;
 104        }
 105
 106        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 107        public Span<T> AppendSpan(int length)
 108        {
 109            Debug.Assert(length >= 0);
 110
 111            int pos = _pos;
 112            Span<T> span = _span;
 113            if ((uint)(pos + length) <= (uint)span.Length)
 114            {
 115                _pos = pos + length;
 116                return span.Slice(pos, length);
 117            }
 118            else
 119            {
 120                return AppendSpanWithGrow(length);
 121            }
 122        }
 123
 124        [MethodImpl(MethodImplOptions.NoInlining)]
 125        private Span<T> AppendSpanWithGrow(int length)
 126        {
 127            int pos = _pos;
 128            Grow(length);
 129            _pos += length;
 130            return _span.Slice(pos, length);
 131        }
 132
 133        // Hide uncommon path
 134        [MethodImpl(MethodImplOptions.NoInlining)]
 135        private void AddWithResize(T item)
 0136        {
 0137            Debug.Assert(_pos == _span.Length);
 0138            int pos = _pos;
 0139            Grow(1);
 0140            _span[pos] = item;
 0141            _pos = pos + 1;
 0142        }
 143
 144        public ReadOnlySpan<T> AsSpan()
 0145        {
 0146            return _span.Slice(0, _pos);
 0147        }
 148
 149        public bool TryCopyTo(Span<T> destination, out int itemsWritten)
 150        {
 151            if (_span.Slice(0, _pos).TryCopyTo(destination))
 152            {
 153                itemsWritten = _pos;
 154                return true;
 155            }
 156
 157            itemsWritten = 0;
 158            return false;
 159        }
 160
 161        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 162        public void Dispose()
 0163        {
 0164            int pos = _pos;
 0165            T[]? toReturn = _arrayFromPool;
 166
 0167            this = default;
 168
 0169            if (toReturn != null)
 0170            {
 171#if SYSTEM_PRIVATE_CORELIB
 172                if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 173                {
 174                    ArrayPool<T>.Shared.Return(toReturn, pos);
 175                }
 176                else
 177                {
 178                    ArrayPool<T>.Shared.Return(toReturn);
 179                }
 180#else
 0181                if (!typeof(T).IsPrimitive)
 0182                {
 0183                    Array.Clear(toReturn, 0, pos);
 0184                }
 185
 0186                ArrayPool<T>.Shared.Return(toReturn);
 187#endif
 0188            }
 0189        }
 190
 191        /// <summary>
 192        /// Resize the internal buffer either by doubling current buffer size or
 193        /// by adding <paramref name="additionalCapacityBeyondPos"/> to
 194        /// <see cref="_pos"/> whichever is greater.
 195        /// </summary>
 196        /// <param name="additionalCapacityBeyondPos">
 197        /// Number of chars requested beyond current position.
 198        /// </param>
 199        /// <remarks>
 200        /// Note that consuming implementations depend on the list only growing if it's absolutely
 201        /// required.  If the list is already large enough to hold the additional items be added,
 202        /// it must not grow. The list is used in a number of places where the reference is checked
 203        /// and it's expected to match the initial reference provided to the constructor if that
 204        /// span was sufficiently large.
 205        /// </remarks>
 206        private void Grow(int additionalCapacityBeyondPos)
 0207        {
 0208            Debug.Assert(additionalCapacityBeyondPos > 0);
 0209            Debug.Assert(_pos > _span.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is neede
 210
 211            const int ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength
 212
 213            // Double the size of the span.  If it's currently empty, default to size 4,
 214            // although it'll be increased in Rent to the pool's minimum bucket size.
 0215            int nextCapacity = Math.Max(_span.Length != 0 ? _span.Length * 2 : 4, _pos + additionalCapacityBeyondPos);
 216
 217            // If the computed doubled capacity exceeds the possible length of an array, then we
 218            // want to downgrade to either the maximum array length if that's large enough to hold
 219            // an additional item, or the current length + 1 if it's larger than the max length, in
 220            // which case it'll result in an OOM when calling Rent below.  In the exceedingly rare
 221            // case where _span.Length is already int.MaxValue (in which case it couldn't be a managed
 222            // array), just use that same value again and let it OOM in Rent as well.
 0223            if ((uint)nextCapacity > ArrayMaxLength)
 0224            {
 0225                nextCapacity = Math.Max(Math.Max(_span.Length + 1, ArrayMaxLength), _span.Length);
 0226            }
 227
 0228            T[] array = ArrayPool<T>.Shared.Rent(nextCapacity);
 0229            _span.CopyTo(array);
 230
 0231            T[]? toReturn = _arrayFromPool;
 0232            _span = _arrayFromPool = array;
 0233            if (toReturn != null)
 0234            {
 235#if SYSTEM_PRIVATE_CORELIB
 236                if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
 237                {
 238                    ArrayPool<T>.Shared.Return(toReturn, _pos);
 239                }
 240                else
 241                {
 242                    ArrayPool<T>.Shared.Return(toReturn);
 243                }
 244#else
 0245                if (!typeof(T).IsPrimitive)
 0246                {
 0247                    Array.Clear(toReturn, 0, _pos);
 0248                }
 249
 0250                ArrayPool<T>.Shared.Return(toReturn);
 251#endif
 0252            }
 0253        }
 254    }
 255}
 256