-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Avoid O(N) string[] allocations for N header values in GetCommaSeparatedValues #54808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Buffers; | ||
using System.Diagnostics; | ||
using System.Runtime.CompilerServices; | ||
|
||
// Copied from https://github.com/dotnet/runtime/blob/a9ed4168626c14b4d74db0d8c205c69e56fc45ed/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs | ||
amcasey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// with unused members removed. | ||
|
||
namespace System.Collections.Generic; | ||
|
||
internal ref partial struct ValueListBuilder<T> | ||
{ | ||
private Span<T> _span; | ||
private T[]? _arrayFromPool; | ||
private int _pos; | ||
|
||
public ValueListBuilder(Span<T> initialSpan) | ||
{ | ||
_span = initialSpan; | ||
_arrayFromPool = null; | ||
_pos = 0; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public void Add(T item) | ||
{ | ||
int pos = _pos; | ||
|
||
// Workaround for https://github.com/dotnet/runtime/issues/72004 | ||
Span<T> span = _span; | ||
if ((uint)pos < (uint)span.Length) | ||
{ | ||
span[pos] = item; | ||
_pos = pos + 1; | ||
} | ||
else | ||
{ | ||
AddWithResize(item); | ||
} | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private void AddWithResize(T item) | ||
{ | ||
Debug.Assert(_pos == _span.Length); | ||
int pos = _pos; | ||
Grow(1); | ||
_span[pos] = item; | ||
_pos = pos + 1; | ||
} | ||
|
||
public ReadOnlySpan<T> AsSpan() => _span.Slice(0, _pos); | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
public void Dispose() | ||
{ | ||
T[]? toReturn = _arrayFromPool; | ||
if (toReturn != null) | ||
{ | ||
_arrayFromPool = null; | ||
ArrayPool<T>.Shared.Return(toReturn); | ||
} | ||
} | ||
|
||
// Note that consuming implementations depend on the list only growing if it's absolutely | ||
// required. If the list is already large enough to hold the additional items be added, | ||
// it must not grow. The list is used in a number of places where the reference is checked | ||
// and it's expected to match the initial reference provided to the constructor if that | ||
// span was sufficiently large. | ||
private void Grow(int additionalCapacityRequired = 1) | ||
{ | ||
const int ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength | ||
|
||
// Double the size of the span. If it's currently empty, default to size 4, | ||
// although it'll be increased in Rent to the pool's minimum bucket size. | ||
int nextCapacity = Math.Max(_span.Length != 0 ? _span.Length * 2 : 4, _span.Length + additionalCapacityRequired); | ||
|
||
// If the computed doubled capacity exceeds the possible length of an array, then we | ||
// want to downgrade to either the maximum array length if that's large enough to hold | ||
// an additional item, or the current length + 1 if it's larger than the max length, in | ||
// which case it'll result in an OOM when calling Rent below. In the exceedingly rare | ||
// case where _span.Length is already int.MaxValue (in which case it couldn't be a managed | ||
// array), just use that same value again and let it OOM in Rent as well. | ||
if ((uint)nextCapacity > ArrayMaxLength) | ||
{ | ||
nextCapacity = Math.Max(Math.Max(_span.Length + 1, ArrayMaxLength), _span.Length); | ||
} | ||
|
||
T[] array = ArrayPool<T>.Shared.Rent(nextCapacity); | ||
_span.CopyTo(array); | ||
|
||
T[]? toReturn = _arrayFromPool; | ||
_span = _arrayFromPool = array; | ||
if (toReturn != null) | ||
{ | ||
ArrayPool<T>.Shared.Return(toReturn); | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.