Skip to content

[API Proposal]: System.Buffers : BuffersExtensions.AsStream #100434

Open

Description

@mgravell

Background and motivation

This is tangentially related to #100290 and the .NET 9 distributed caching epic; the proposed hybid-cache API will use ReadOnlySequence<byte> and IBufferWriter<byte> as the primary serializer APIs, but: not all serializers support these APIs (many do, note) - with Stream being the most common fallback.

API Proposal

  // assembly: System.Memory
  namespace System.Buffers;
  
  public static class BuffersExtensions // pre-existing
  {
+     public static Stream AsStream(this ReadOnlySequence<byte> value) => new ReadOnlySequenceStream(value);
+     public static Stream AsStream(this IBufferWriter<byte> value) => new BufferWriterStream(value);
  }
+ internal sealed class ReadOnlySequenceStream : Stream {...}
+ internal sealed class BufferWriterStream : Stream {...}

API Usage

ReadOnlySequence<byte> payload = ...
Customer obj = SomeRandomSerializer.Deserialize<Customer>(payload.AsStream());

and

Customer obj = ...
IBufferWriter<byte> target = ...
SomeRandomSerializer.Serialize<Customer>(target.AsStream(), obj);

The implementations would be internal, but:

  • ReadOnlySequenceStream has a CanRead: true, CanWrite: false implementation that supports seek etc; no additional buffer copies, just a few "where are we" counters, using SequencePosition iteration and a ReadOnlyMemory<byte> snapshot of the current segment
  • BufferWriterStream has a CanRead: false, CanWrite: true implementation that does not support seek; position and length are read-only and report the bytes written so far; no double-buffering - it is assumed (as a fundamental part of IBufferWriter<byte>) that the underlying IBufferWriter<byte> already does some internal work there when responding to GetSpan/GetMemory (such that they are affordable), so this would be duplicated effort and an additional mem-copy
  • in both cases, all APIs are synchronous, with the async APIs implemented as async-over-sync as efficiently as possible
  • no need for fancy recycling, since we don't have any local buffers etc

Alternative Designs

The alternative is to use MemoryStream, which involves multiple additional copy operations and additional byte[] buffers.

Risks

None seen

Additional

I am happy to contribute the implementation effort.

Activity

  1. added
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Mar 29, 2024
  2. dotnet-policy-service commented on Mar 29, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-buffers
    See info in area-owners.md if you want to be subscribed.

  3. changed the title [-][API Proposal]: System.Buffers : BufferExtensions.AsStream[/-] [+][API Proposal]: System.Buffers : BuffersExtensions.AsStream[/+] on Mar 29, 2024
  4. stephentoub commented on Mar 29, 2024

    @stephentoub
    Member

    The ReadOnlySequence<T> side of this is #27156. @jozkee, where did we land on a design for these extra streams, e.g. AsStream vs dedicated publicly named streams vs etc. Implementation is straightforward, so if we have an agreed upon design, we can make quick forward progress here.

  5. added
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    and removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Apr 1, 2024
  6. jozkee commented on Apr 1, 2024

    @jozkee
    Member

    #82801 (comment)

    For [ReadOnly]Memory<byte> and ReadOnlySequence<byte>, we want to have static methods that return the Stream wrapping them. If we want to have them as extension methods in Stream we would need the static extensions language feature to have AsStream(this ReadOnlySequence<byte>), or we move ReadOnlySequence down to S.P.CoreLib.

    I discarded having Memory<byte> support directly in MemoryStream based on #84103.

  7. mgravell commented on Apr 1, 2024

    @mgravell
    ContributorAuthor

    Re corelib: that's why I proposed the existing BuffersExtensions as the root (or something in the same package/namespace) - it should have all the right refs.

  8. PaulusParssinen commented on Apr 2, 2024

    @PaulusParssinen
    Contributor

    Noting that we now have System.IO.Pipelines available in .NET 9 shared runtime meaning that there are now two types implementing IBufferWriter<byte> in the BCL; PipeWriter and ArrayBufferWriter<byte>.

    When I use S.IO.Pipelines I often find myself having to pull CommunityToolkit.HighPerformance in for its AsStream(this IBufferWriter<byte> writer) fallback.

  9. jozkee commented on Apr 2, 2024

    @jozkee
    Member

    Considering that static extensions is in the backlog dotnet/csharplang#192, I think we should consider this for 9 while keeping #82801 as future.

  10. added
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    and removed
    untriagedNew issue has not been triaged by the area owner
    on Jun 24, 2024
  11. added this to the Future milestone on Jul 19, 2024
  12. PaulusParssinen commented on Jan 31, 2025

    @PaulusParssinen
    Contributor

    Is the plan for this and all other static extension helpers proposals to wait until dotnet/csharplang#192 is done before any further triage?

  13. jeffhandley commented on Feb 9, 2025

    @jeffhandley
    Member

    At this point, we are not prioritizing this proposal. But I am taking note that when dotnet/csharplang#192 lands, we should consider this proposal as a use case to implement.

  14. removed
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    on Feb 9, 2025
  15. aradalvand commented on Feb 1, 2026

    @aradalvand

    This is still just as needed as two years ago. And dotnet/csharplang#192 has now made it to C# 14. @jeffhandley Did you guys ever re-consider this issue since then?

  16. jeffhandley commented on Feb 1, 2026

    @jeffhandley
    Member

    Hi @aradalvand. Indeed, @ViveliDuCh has been working in this space and on related issues. I didn't have this one assigned to her yet, but I know it's part of what she's been looking into.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions