- 
                Notifications
    You must be signed in to change notification settings 
- Fork 114
Closed
Labels
Description
Is your feature request related to a problem? Please describe.
There are a few issues with the fixed array helpers structs (such as internal partial struct __char_32):
- They do not play nicely with C# 11 (generate new warnings).
- They also are bulkier than necessary.
- They could be faster by leveraging built-in helpers (such as .IndexOf).
- Equals logic is unexpected.
Describe the solution you'd like
- Utilization of [UnscopedRef]to eliminate warnings in C# 11. (Available in RC1)
- Provide an AsReadOnlySpanthat can be marked asreadonlyto avoid implicit struct copies.
- Introduce a span extensions class to help with interop span usage.
- Fix and clarify Equalslogic (it currently matches incoming spans that are longer than the content).
- Move the structs to a shared location to avoid duplicate copies for same sized structs.
- Consider removing methods that don't provide much value (CopyTo,ToArray,ToString(int length)).
Existing
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal partial struct __char_32
{
    internal char _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31;
    internal readonly int Length => 32;
    internal ref char this[int index] => ref AsSpan()[index];
    internal Span<char> AsSpan() => MemoryMarshal.CreateSpan(ref _0, 32);
    internal unsafe readonly void CopyTo(Span<char> target, int length = 32);
    internal readonly char[] ToArray(int length = 32);
    internal unsafe readonly bool Equals(ReadOnlySpan<char> value);
    internal readonly bool Equals(string value) => Equals(value.AsSpan());
    internal unsafe readonly string ToString(int length);
    public override readonly unsafe string ToString();
    public static implicit operator __char_32(string value) => value.AsSpan();
    public static implicit operator __char_32(ReadOnlySpan<char> value);
}Proposed
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal partial struct __char_32
{
    private unsafe fixed char _value[32];
    internal readonly int Length => 32;
    // How useful is this?
    [UnscopedRef]
    internal ref char this[int index] => ref AsWritableSpan()[index];
    [UnscopedRef]
    internal unsafe Span<char> AsWritableSpan() => MemoryMarshal.CreateSpan(ref _value[0], 32);
    // Suggesting making the `ReadOnlySpan` method `AsSpan()` to align with `string.AsSpan()`.
    // Need a `readonly` method to allow other `readonly` methods to utilize without creating implicit struct copies.
    [UnscopedRef]
    internal unsafe readonly ReadOnlySpan<char> AsSpan()
        => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in _value[0]), 32);
    // This should probably be removed. `AsSpan().CopyTo(target)` is pretty straight-forward.
    internal unsafe readonly void CopyTo(Span<char> target, int length = 32)
    {
        // If we don't remove the methods I've marked, the checks could be removed and we could allow bounds checks to fail.
        if (length > 32) throw new ArgumentOutOfRangeException(nameof(length));
        AsSpan()[..length].CopyTo(target);
    }
    // This should probably be removed. `AsSpan().ToArray()` is pretty straight-forward.
    internal readonly char[] ToArray(int length = 32)
    {
        if (length > 32) throw new ArgumentOutOfRangeException(nameof(length));
        return AsSpan()[..length].ToArray();
    }
    internal unsafe readonly bool Equals(ReadOnlySpan<char> value)
        => AsSpan().SliceAtNull().SequenceEqual(value);
    // This also is unnecessary. `string` is implicitly convertable to `ReadonlySpan<char>`.
    internal readonly bool Equals(string value) => Equals(value.AsSpan());
    // This is also something that should be removed. 
    internal unsafe readonly string ToString(int length)
    {
        if (length < 0 || length > Length) throw new ArgumentOutOfRangeException(nameof(length), length, "Length must be between 0 and the fixed array length.");
        return AsSpan()[..length].ToString();
    }
    public override readonly unsafe string ToString() => AsSpan().SliceAtNull().ToString();
    public static implicit operator __char_32(string value) => value.AsSpan();
    public static implicit operator __char_32(ReadOnlySpan<char> value)
    {
        __char_32 result = default;
        value.CopyTo(result.AsWritableSpan());
        return result;
    }
}
// NEW: Extension class to make working with spans easier.
internal static class SpanExtensions
{
    public static ReadOnlySpan<char> SliceAtNull(this ReadOnlySpan<char> value)
    {
        int length = value.IndexOf('\0');
        return new string(length < 0 ? value : value[..length]);
    }
}Proposed without unnecessary methods
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal partial struct __char_32
{
    private unsafe fixed char _value[32];
    internal readonly int Length => 32;
    [UnscopedRef]
    internal ref char this[int index] => ref AsWritableSpan()[index];
    [UnscopedRef]
    internal unsafe Span<char> AsWritableSpan() => MemoryMarshal.CreateSpan(ref _value[0], 32);
    [UnscopedRef]
    internal unsafe readonly ReadOnlySpan<char> AsSpan()
        => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in _value[0]), 32);
    internal unsafe readonly bool Equals(ReadOnlySpan<char> value)
        => AsSpan().SliceAtNull().SequenceEqual(value);
    public override readonly unsafe string ToString() => AsSpan().SliceAtNull().ToString();
    public static implicit operator __char_32(string value) => value.AsSpan();
    public static implicit operator __char_32(ReadOnlySpan<char> value)
    {
        __char_32 result = default;
        value.CopyTo(result.AsWritableSpan());
        return result;
    }
}cc: @lonitra