Skip to content

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

@stephentoub

Description

@stephentoub

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions