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(Span<T>, T, T) #75322

Closed
stephentoub opened this issue Sep 9, 2022 · 5 comments · Fixed by #76337
Closed

[API Proposal]: MemoryExtensions.Replace(Span<T>, T, T) #75322

stephentoub opened this issue Sep 9, 2022 · 5 comments · Fixed by #76337
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Memory
Milestone

Comments

@stephentoub
Copy link
Member

Background and motivation

string.Replace(char, char) is useful for replacing every occurrence of a character value. When using spans/arrays to build up strings, that capability is also useful, but today it needs to be hand-rolled, e.g.

Span<char> tmp = ...;
int pos;
while ((pos = tmp.IndexOf(oldValue)) >= 0)
{
    tmp[pos] = newValue;
    tmp = tmp.Slice(pos + 1);
}

It'd be good to have this as a reusable helper on MemoryExtensions so that such open-coded loops can be replaced. There are other more complicated variations of this as well, but this is a simple and common one we can include.

e.g.

for (ushort i = 0; i < (ushort)count; ++i)
{
if (result[i] == '/')
{
result[i] = '\\';
}
}

int pos;
while ((pos = dest.IndexOf('\\')) >= 0)
{
dest[pos] = '/';
dest = dest.Slice(pos + 1);
}

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>?;
    }
}

API Usage

char[] result = ...;
result.AsSpan().Replace('\\', '/');
return new string(result);

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 Sep 9, 2022
@stephentoub stephentoub added this to the 8.0.0 milestone Sep 9, 2022
@ghost
Copy link

ghost commented Sep 9, 2022

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

string.Replace(char, char) is useful for replacing every occurrence of a character value. When using spans/arrays to build up strings, that capability is also useful, but today it needs to be hand-rolled, e.g.

Span<char> tmp = ...;
int pos;
while ((pos = tmp.IndexOf(oldValue)) >= 0)
{
    tmp[pos] = newValue;
    tmp = tmp.Slice(pos + 1);
}

It'd be good to have this as a reusable helper on MemoryExtensions so that such open-coded loops can be replaced. There are other more complicated variations of this as well, but this is a simple and common one we can include.

e.g.

for (ushort i = 0; i < (ushort)count; ++i)
{
if (result[i] == '/')
{
result[i] = '\\';
}
}

int pos;
while ((pos = dest.IndexOf('\\')) >= 0)
{
dest[pos] = '/';
dest = dest.Slice(pos + 1);
}

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>?;
    }
}

API Usage

char[] result = ...;
result.AsSpan().Replace('\\', '/');
return new string(result);

Alternative Designs

No response

Risks

No response

Author: stephentoub
Assignees: -
Labels:

api-suggestion, area-System.Memory

Milestone: 8.0.0

@gfoidl
Copy link
Member

gfoidl commented Sep 9, 2022

I'm looking forward that this API gets approved -- so I can implement it 😉

@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 Sep 10, 2022
@bartonjs
Copy link
Member

bartonjs commented Sep 20, 2022

Video

  • We discussed whether Replace should also return the replacement count, and the answer was no, because it has a nontrivial cost that exceeds its value (a counting variant could be added in the future, if needed)
    • If someone wants to try experimenting with this and show it's actually negligible, please change it to returning int.
  • We discussed whether we needed a more complicated name, like ReplaceInPlace, to avoid a confusing overload with future potential replaces. The feeling was "no".
namespace System
{
    public static class MemoryExtensions
    {
        public static void Replace<T>(this Span<T> span, 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 api-ready-for-review API is ready for review, it is NOT ready for implementation labels Sep 20, 2022
@stephentoub
Copy link
Member Author

One issue we didn't discuss today but should:
This is doing in-place replacement. What do we do for code that wants to replace into something else, e.g. to be able to use this from string.Replace? Currently string.Replace uses a conditional select to copy/replace from the source into the destination in one pass. With the approved shape of this API, for string to use it, presumably it would need to copy to the destination and then in-place replace in the destination. Will that perform measurably worse? Of course, the other approach is have Replace take two spans, a source and a destination, and we'd allow the input to be the same for each, but then we have the complication of what do we do if the arguments are overlapping but not the same, do we need to check for that or just say it's undefined behavior, etc. We could also consider having both overloads.

We should come up with a pattern we want to use for these sorts of things, as it's going to start showing up more often, e.g. in #75901, in #26651, etc.

@stephentoub
Copy link
Member Author

I'm looking forward that this API gets approved -- so I can implement it 😉

Ok 😄 I assigned it to you.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Sep 28, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Nov 11, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Dec 12, 2022
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