diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/MemoryBuilder`1.cs b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/MemoryBuilder`1.cs new file mode 100644 index 0000000000..de2497e5c1 --- /dev/null +++ b/src/Shared/Microsoft.AspNetCore.Razor.Utilities.Shared/MemoryBuilder`1.cs @@ -0,0 +1,90 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Microsoft.AspNetCore.Razor; + +/// +/// Temporary builder that uses to back a . +/// +internal ref struct MemoryBuilder +{ + private Memory _memory; + private T[]? _arrayFromPool; + private int _index; + + public MemoryBuilder(int initialCapacity = 1) + { + ArgHelper.ThrowIfNegativeOrZero(initialCapacity); + _arrayFromPool = ArrayPool.Shared.Rent(initialCapacity); + _memory = _arrayFromPool; + } + + public void Dispose() + { + var toReturn = _arrayFromPool; + if (toReturn is not null) + { + _arrayFromPool = null; + ArrayPool.Shared.Return(toReturn); + } + } + + public readonly ReadOnlyMemory AsMemory() + => _memory[.._index]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(T item) + { + var index = _index; + + var memory = _memory; + if ((uint)index < (uint)memory.Length) + { + memory.Span[index] = item; + _index = index + 1; + } + else + { + AppendWithResize(item); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendWithResize(T item) + { + Debug.Assert(_index == _memory.Length); + var index = _index; + Grow(1); + _memory.Span[index] = item; + _index = index + 1; + } + + private void Grow(int additionalCapacityRequired = 1) + { + const int ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength + + var nextCapacity = Math.Max(_memory.Length != 0 ? _memory.Length * 2 : 4, _memory.Length + additionalCapacityRequired); + + if ((uint)nextCapacity > ArrayMaxLength) + { + nextCapacity = Math.Max(Math.Max(_memory.Length + 1, ArrayMaxLength), _memory.Length); + } + + var newArray = ArrayPool.Shared.Rent(nextCapacity); + _memory.Span.CopyTo(newArray); + + var toReturn = _arrayFromPool; + _memory = newArray; + _arrayFromPool = newArray; + + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } +}