-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Add initial Span<T> access to creation of ImmutableArray<T> #53154
Comments
Tagging subscribers to this area: @eiriktsarpalis Issue DetailsBackground and MotivationAs .NET increasingly leverages Currently, I use an unsafe workaround to enable all my return Unsafe.As<byte[], ImmutableArray<byte>>(ref blob); Proposed APIThere is already an excellent template for one way to accomplish this: the namespace System.Collections.Immutable
{
public static class ImmutableArray
{
+ public static ImmutableArray<T> Create<T, TState>(int length, TState state, SpanAction<T, TState> action) => throw null;
}
} The Usage ExamplesThe usage reflects var encodedBytes = File.ReadAllBytes("base64.txt");
var decodedLength = Base64.GetMaxDecodedFromUtf8Length(encodedbytes.Length); // Just assume this lines up.
var decodedBytes = ImmutableArray.Create<byte, byte[]>(
decodedLength,
encodedBytes,
(span, state) =>
{
Base64.DecodeFromUtf8(state, span, out _, out _);
}); Alternative DesignsAn unfortunate aspect of the proposal above is that, unlike namespace System.Collections.Immutable
{
public struct ImmutableArray<T>
{
public sealed class Builder
{
+ public void SetValues<TState>(TState state, SpanAction<T, TState> action) => throw null;
}
}
} This would also be an acceptable solution. var encodedBytes = File.ReadAllBytes("base64.txt");
var decodedLength = Base64.GetMaxDecodedFromUtf8Length(encodedbytes.Length); // Just assume this lines up.
var builder = ImmutableArray.CreateBuilder<byte>(decodedLength);
builder.SetValues(
encodedBytes,
(span, state) =>
{
Base64.DecodeFromUtf8(state, span, out _, out _);
});
var decodedBytes = builder.MoveToImmutable(); It adds a step, but being able to mutate the data en mass inside a builder could have greater potential. RisksThe only risk that really comes to mind is the further encouragement of immutable collections, but it is hopefully seen as an upside for most. I can't think of any neighboring APIs or extensions this would bump into or cause issues with.
|
What if this was a Though it would be inconsistent with all the other |
@svick I like the idea as it would indeed solve the type-inference issue, but yeah, as you pointed out, it would create an abrupt inconsistency in where all the static methods live. I'm hoping the alternative API I proposed becomes a viable path forward. |
Placing the method on the builder seems like a more reasonable choice, however there is also the alternative of using a Related to #22928. |
@eiriktsarpalis I think that would be useful as well, but it feels like unnecessary ceremony if I already know the exact size of my array. I want this to function like the |
I think the reason that |
BTW it seems that #22928 didn't actually add an implicit conversion from |
So, now that I see this is marked as "needs work", what direction can it go in? Is there a reasonable way to make this work? Personally, I would not be against a collections marshal method (a la |
Is this still required with #83141 implemented? The shape of this proposal looks "safer", but they serves for the same purpose. |
@huoyaoyuan I would tend to agree, while a delegate-based API might make sense for strings I don't think it's essential for immutable arrays, particularly given the newly added API. It still necessitates a tiny bit of buffer management on the user's part, but I don't think it's a dealbreaker. |
This issue has been marked |
This issue has been automatically marked |
This issue will now be closed since it had been marked |
Background and Motivation
As .NET increasingly leverages
Span<T>
across its APIs, it becomes increasingly important that all core data structures fully support it as well. WhileImmutableArray<T>
does support being accessed as aReadOnlySpan<T>
by way ofAsSpan()
, there does not seem to be a way to access aSpan<T>
during the creation. This results in unnecessary copies of (possibly huge) arrays or performance-hostile rapid-fire usage ofimmutableArrayBuilder.Add(item)
(particularly if it's abyte
array).Currently, I use an unsafe workaround to enable all my
Span<T>
goodness throughout the array-building process.Proposed API
There is already an excellent template for one way to accomplish this: the
String.Create(...)
method (seen here).namespace System.Collections.Immutable { public static class ImmutableArray { + public static ImmutableArray<T> Create<T, TState>(int length, TState state, SpanAction<T, TState> action) => throw null; } }
The
TState
enables callers to avoid closures/allocations. TheSpanAction
grants temporary access to the entirety of the buffer, which can now be filled using modern APIs that speak toSpan<T>
. Examples include the results of astream.Read(...)
, a call toBase64.DecodeFromUtf8(...)
, etc.Usage Examples
The usage reflects
string.Create
.Alternative Designs
An unfortunate aspect of the proposal above is that, unlike
string.Create(...)
, the generic types cannot be easily inferred from usage, so they have to be specified up front. I'd be open to another approach that simply exposes the buffer inside of a builder.namespace System.Collections.Immutable { public struct ImmutableArray<T> { public sealed class Builder { + public void SetValues<TState>(TState state, SpanAction<T, TState> action) => throw null; } } }
This would also be an acceptable solution.
It adds a step, but being able to mutate the data en mass inside a builder could have greater potential.
SetValues
is not a great name, which is why it is my alternative proposal. :)Risks
The only risk that really comes to mind is the further encouragement of immutable collections, but it is hopefully seen as an upside for most. I can't think of any neighboring APIs or extensions this would bump into or cause issues with.
The text was updated successfully, but these errors were encountered: