Skip to content
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

[API Proposal]: MemoryExtensions.Replace with separate source/destination buffers #81829

Closed
stephentoub opened this issue Feb 8, 2023 · 4 comments · Fixed by #83120
Closed
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Memory
Milestone

Comments

@stephentoub
Copy link
Member

stephentoub commented Feb 8, 2023

Background and motivation

We recently added:

public static void Replace<T>(this Span<T> span, T oldValue, T newValue) where T : IEquatable<T>?

and we've been able to use it ourselves in several places, e.g. in StringBuilder.Replace. However, there are several places that actually want to be able to perform a copy/replace operation together, and doing so as a copy followed by a replace can be significantly slower (upwards of 2x) than a combined operation. For example, string.Replace needs to copy into a separate buffer and perform the replace in that buffer, and https://github.com/dotnet/aspnetcore/blob/287d7eab1c644bce2d4c96751bc3d323362b1bb7/src/Shared/QueryStringEnumerable.cs#L166-L229 similarly copies from a ReadOnlySpan<char> to a Span<char> doing a replace in the process.

We already have the implementation for this as part of string.Replace, so this is effectively just exposing that through an additional Replace overload, which can then be used in other places, like in ASP.NET.

API Proposal

namespace System

public static class MemoryExtensions
{
     public static void Replace<T>(this Span<T> span, T oldValue, T newValue) where T : IEquatable<T>?
+    public static void Replace<T>(this ReadOnlySpan<T> source, Span<T> destination, T oldValue, T newValue) where T : IEquatable<T>?
}

API Usage

// Create a string from the ReadOnlySpan<char>, replacing all pluses with spaces.
return string.Create(source.Length, (IntPtr)(&source), (dest, rosPtr) =>
{
    ReadOnlySpan<char> source = *(ReadOnlySpan<char>*)rosPtr;
    source.Replace(dest, '+', ' ');
});

Alternative Designs

No response

Risks

No response

@stephentoub stephentoub added api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Memory labels Feb 8, 2023
@stephentoub stephentoub added this to the 8.0.0 milestone Feb 8, 2023
@ghost
Copy link

ghost commented Feb 8, 2023

Tagging subscribers to this area: @dotnet/area-system-memory
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

We recently added:

public static void Replace<T>(this Span<T> span, T oldValue, T newValue) where T : IEquatable<T>?

and we've been able to use it ourselves in several places. However, there are several places that actually want to be able to perform a copy/replace operation together. For example, string.Replace needs to copy into a separate buffer and perform the replace in that buffer, and https://github.com/dotnet/aspnetcore/blob/287d7eab1c644bce2d4c96751bc3d323362b1bb7/src/Shared/QueryStringEnumerable.cs#L166-L229 similarly copies from a ReadOnlySpan<char> to a Span<char> doing a replace in the process.

We already have the implementation for this as part of string.Replace, so this is effectively just exposing that through an additional Replace overload, which can then be used in other places, like in ASP.NET.

API Proposal

namespace System

public static class MemoryExtensions
{
     public static void Replace<T>(this Span<T> span, T oldValue, T newValue) where T : IEquatable<T>?
+    public static void Replace<T>(this ReadOnlySpan<T> span, Span<T>, T oldValue, T newValue) where T : IEquatable<T>?
}

API Usage

// Create a string from the ReadOnlySpan<char>, replacing all pluses with spaces.
return string.Create(source.Length, (IntPtr)(&source), (dest, rosPtr) =>
{
    ReadOnlySpan<char> source = *(ReadOnlySpan<char>*)rosPtr;
    source.Replace(dest, '+', ' ');
});

Alternative Designs

No response

Risks

No response

Author: stephentoub
Assignees: -
Labels:

api-suggestion, area-System.Memory

Milestone: 8.0.0

@MihaZupan
Copy link
Member

Replace(this ReadOnlySpan span, Span, T oldValue, T newValue)

Likely meant Replace<T>(this ReadOnlySpan<T> span, Span<T> destination, T oldValue, T newValue)?

@stephentoub
Copy link
Member Author

Fixed

@stephentoub stephentoub added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Feb 13, 2023
@stephentoub stephentoub added the blocking Marks issues that we want to fast track in order to unblock other important work label Mar 6, 2023
@bartonjs
Copy link
Member

bartonjs commented Mar 7, 2023

Video

Looks good as proposed. We discussed whether or not the method warranted an "-Into" suffix, given the existing overload, and decided it didn't.

The method probably should reject source and destination in a partial overlap.

Given that the "existing" Replace method was added in this release, consider whether it is still warranted, or should be removed in favor of this one.

The concern was also raised that a this Span<T> source, Span<T> destination overload might be desired, given the extension methods. Such an overload is an easy approval of just squaring off the overload group, but isn't specifically being recommended at this time.

namespace System

public static partial class MemoryExtensions
{
     public static void Replace<T>(this ReadOnlySpan<T> source, Span<T> destination, T oldValue, T newValue) where T : IEquatable<T>?
}

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed blocking Marks issues that we want to fast track in order to unblock other important work api-ready-for-review API is ready for review, it is NOT ready for implementation labels Mar 7, 2023
@stephentoub stephentoub self-assigned this Mar 7, 2023
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Mar 8, 2023
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Mar 20, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Apr 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Memory
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants