[API Proposal]: SequenceReader missing endian-aware read methods for C# built-in ValueTypes

Open
#95,320 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
csharp

Research direction

Start by reviewing the existing endian-aware methods on SequenceReaderExtensions and the related BinaryPrimitives support mentioned in the proposal. Check how SequenceReader handles split sequences and how the proposed built-in types, especially decimal, map to existing APIs. Done means reaching an agreed API design and adding the corresponding implementation and tests.

Written by the indexing model from the issue text.

Description

api-suggestion area-System.Memory needs-further-triage
Background and motivation:

As the SequenceReader is a component quite commonly used in the performance optimized System.IO.Pipelines API, it would be nice for it to have a full complement of low level Read methods which are endian-aware (such that higher level code appears clean and consistent in accessing the lower level memory sequences presented by the System.IO.Pipelines APIs and others).
There is support for some of the C# built-in types, but many are currently lacking.

API Proposal:

The addition of the following methods:

namespace System.Buffers;

public static partial class SequenceReaderExtensions
{
...
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out ushort value;
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out uint value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out ulong value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out ulong value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out nint value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out nuint value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.Int128 value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.UInt128 value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.Half value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out single value);
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out double value);
...
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out ushort value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out uint value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out ulong value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out nint value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out nuint value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.Int128 value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.UInt128 value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out System.Half value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out single value);
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out double value);
...
}

I also believe that consideration should be given to supporting the decimal C# datatype as well.

namespace System.Buffers;

public static partial class SequenceReaderExtensions
{
+        //NOTE: Throws NotImplemented on LittleEndian architecture
+        public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out decimal value);

+        //NOTE: Throws NotImplemented on BigEndian architecture
+        public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out decimal value);
}

The reason I believe this should be considered is not strictly for the Endian handling aspect, but more for the Sequence handling aspect.
As the decimal is a native C# type, when using the SequenceReader API it doesn't feel appropriate that an API-user would need to concern themselves over re-assembling this native C# type themselves from potentially split sequences.

With the decimal being constructed of a 96bit integer, and a 32 bit flag, I'm unsure how it would strictly be handled in terms of endian swapping. I would however suggest that the API should be included in the SequenceReader, for completeness, but where the opposing read (BigEndian on little endian architecture and vice versa) would throw a NotImplemented exception. The current implementation could then only handle the endianness of the architecture until BinaryPrimatives supports this handling also.

API Usage:
using System.Buffers;

namespace My.Magic.Bus;
public struct MyCustomStruct
{
    public uint CustomerIndex;
    public decimal CustomersBigMoney;
}

public static class MyCustomStructSequenceReaderExtensions
{
    public static bool TryReadBigEndian(this ref System.Buffers.SequenceReader<byte> reader, out MyCustomStruct value)
    {
        MyCustomStruct temp;
        bool success = true;
        success &= reader.TryReadBigEndian(out temp.CustomerIndex);
        success &= reader.TryReadBigEndian(out temp.CustomersBigMoney);
        value = temp;
        return success;
    }

    public static bool TryReadLittleEndian(this ref System.Buffers.SequenceReader<byte> reader, out MyCustomStruct value)
    {
        MyCustomStruct temp;
        bool success = true;
        success &= reader.TryReadLittleEndian(out temp.CustomerIndex);
        success &= reader.TryReadLittleEndian(out temp.CustomersBigMoney);
        value = temp;
        return success;
    }
}
Dominant language
C#
Stars
18.3k
Forks
5.6k
Avg merge
2d 19h
Merged PRs (30d)
589

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/runtime

All issues in dotnet/runtime

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.