Skip to content

Proposal: {ReadOnly}Memory<T> Extensions (mirror Span) #23835

Description

@benaadams

{ReadOnly}Memory<T> Extensions

Background

Came up in dotnet/corefx#24389 (comment)

Currently you can use all the Span<T> extensions and methods from Memory<T> by extracting the Span<T> e.g.

memory0.Span.SequenceEqual(memory1.Span);

However this has two downsides

  1. Not discoverable; you need to use the Span property to find them
  2. Span can't be used in an async method; so to use them you need to create a second method and call that

Proposal

Include the second method as extensions on {ReadOnly}Memory<T> that passes through to the Span

public static class MemoryExtensions
{
    // Span methods converted to Memory extension
    public static void CopyTo<T>(this Memory<T> memory, Memory<T> destination)
        => memory.Span.CopyTo(destination.Span);
    public static void CopyTo<T>(this ReadOnlyMemory<T> memory, Memory<T> destination)
        => memory.Span.CopyTo(destination.Span);
    public static bool TryCopyTo<T>(this Memory<T> memory, Memory<T> destination)
        => memory.Span.TryCopyTo(destination.Span);
    public static bool TryCopyTo<T>(this ReadOnlyMemory<T> memory, Memory<T> destination)
        => memory.Span.TryCopyTo(destination.Span);
    public static void Fill<T>(this Memory<T> memory, T value)
        => memory.Span.Fill(value);

    // Array extension as per Span extension
    public static void CopyTo<T>(this T[] array, Memory<T> destination)
        => array.CopyTo(destination.Span);

    // Valid? via Owned memory?
    public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text);

    // Discoverable conversions as per Span
    public static Memory<T> AsMemory<T>(this ArraySegment<T> arraySegment) => arraySegment;
    public static Memory<T> AsMemory<T>(this T[] array) => array;

    // Span extension pass-throughs
    public static int IndexOf(this Memory<byte> memory, byte value)
        => memory.Span.IndexOf(value);
    public static int IndexOf(this Memory<byte> memory, ReadOnlyMemory<byte> value)
        => memory.Span.IndexOf(value.Span);
    public static int IndexOf(this ReadOnlyMemory<byte> memory, byte value)
        => memory.Span.IndexOf(value);
    public static int IndexOf(this ReadOnlyMemory<byte> memory, ReadOnlyMemory<byte> value)
        => memory.Span.IndexOf(value.Span);

    public static int IndexOf<T>(this Memory<T> memory, T value)
        where T : struct, IEquatable<T>
        => memory.Span.IndexOf(value);
    public static int IndexOf<T>(this Memory<T> memory, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => memory.Span.IndexOf(value.Span);
    public static int IndexOf<T>(this ReadOnlyMemory<T> memory, T value)
        where T : struct, IEquatable<T>
        => memory.Span.IndexOf(value);
    public static int IndexOf<T>(this ReadOnlyMemory<T> memory, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => memory.Span.IndexOf(value.Span);

    public static int IndexOfAny(this Memory<byte> memory, byte value0, byte value1)
        => memory.Span.IndexOfAny(value0, value1);
    public static int IndexOfAny(this Memory<byte> memory, byte value0, byte value1, byte value2)
        => memory.Span.IndexOfAny(value0, value1, value2);
    public static int IndexOfAny(this Memory<byte> memory, ReadOnlyMemory<byte> values)
        => memory.Span.IndexOfAny(values.Span);

    public static int IndexOfAny(this ReadOnlyMemory<byte> memory, byte value0, byte value1)
        => memory.Span.IndexOfAny(value0, value1);
    public static int IndexOfAny(this ReadOnlyMemory<byte> memory, byte value0, byte value1, byte value2)
        => memory.Span.IndexOfAny(value0, value1, value2);
    public static int IndexOfAny(this ReadOnlyMemory<byte> memory, ReadOnlyMemory<byte> values)
        => memory.Span.IndexOfAny(values.Span);

    public static bool SequenceEqual(this Memory<byte> first, ReadOnlyMemory<byte> second)
        => first.Span.SequenceEqual(second.Span);
    public static bool SequenceEqual(this ReadOnlyMemory<byte> first, ReadOnlyMemory<byte> second)
        => first.Span.SequenceEqual(second.Span);

    public static bool SequenceEqual<T>(this Memory<T> first, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => first.Span.SequenceEqual(value.Span);
    public static bool SequenceEqual<T>(this ReadOnlyMemory<T> first, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => first.Span.SequenceEqual(value.Span);

    public static bool StartsWith(this Memory<byte> memory, ReadOnlyMemory<byte> value)
        => memory.Span.StartsWith(value.Span);
    public static bool StartsWith(this ReadOnlyMemory<byte> memory, ReadOnlyMemory<byte> value)
        => memory.Span.StartsWith(value.Span);

    public static bool StartsWith<T>(this Memory<T> memory, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => memory.Span.StartsWith(value.Span);
    public static bool StartsWith<T>(this ReadOnlyMemory<T> memory, ReadOnlyMemory<T> value)
        where T : struct, IEquatable<T>
        => memory.Span.StartsWith(value.Span);
}

Notes

Items that change length can't be replicated due to the backing store e,g, AsBytes, NonPortableCast

Question

Is a string conversion possible?

    public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text);

/cc @davidfowl @KrzysztofCwalina @stephentoub @jkotas

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions