Skip to content

Stream wrappers for memory and text-based types#1

Open
ViveliDuCh wants to merge 17 commits intomainfrom
stream-tests
Open

Stream wrappers for memory and text-based types#1
ViveliDuCh wants to merge 17 commits intomainfrom
stream-tests

Conversation

@ViveliDuCh
Copy link
Copy Markdown
Owner

@ViveliDuCh ViveliDuCh commented Dec 17, 2025

Fixes dotnet#82801

This PR introduces the initial API prototype for System.IO.Stream factory methods and extension methods, establishing standardized stream wrappers to convert text-based and memory types into streams. This implementation follows the validated scope from the initial API review and establishes a baseline for future benchmarking, design iteration, and API evolution.

Implemented Stream Wrappers

The following stream wrappers are implemented, each providing high correctness test coverage and conformance/complementary behavioral tests:

  • ReadOnlyTextStream: Wraps a string or ReadOnlyMemory<char> as a seekable, read-only stream with on-demand encoding. Particularly efficient for slicing and non-allocating substring scenarios.
  • MemoryByteStream: Wraps Memory<byte> as a seekable, writable stream with a fixed-size buffer, or ReadOnlyMemory<byte> as a seekable, read-only stream.
  • ReadOnlySequenceStream: Wraps ReadOnlySequence<byte> as a seekable, read-only stream. Available via extension method in System.Memory.

API Surface

Static methods on System.IO.Stream (in System.Private.CoreLib):

public static Stream FromText(string text, Encoding? encoding = null);
public static Stream FromText(ReadOnlyMemory<char> text, Encoding? encoding = null);
public static Stream FromReadOnlyData(ReadOnlyMemory<byte> data);
public static Stream FromWritableData(Memory<byte> data);

C#14 Extension method (in System.Memory):

public static class ReadOnlySequenceStreamExtensions
{
    extension(Stream)
    {
        public static Stream FromReadOnlyData(ReadOnlySequence<byte> sequence) =>
            new ReadOnlySequenceStream(sequence);
    }
}

Design Notes

  • Buffer Ownership: Stream wrappers do not own or expand their provided data/buffers.
  • Consistency: Disposing a stream wrapper does not alter the underlying data/buffer.
  • Usability: Writing past capacity throws; reading past the end returns zero, per .NET convention.
  • Seekable: All stream implementations support seeking (CanSeek = true).

Usage Example

using System.IO;
using System.Text;

// Create a stream from a string for HTTP content
Stream stream = Stream.FromText("Hello world", Encoding.UTF8);
// Use with HttpClient, File I/O, etc.

// Create a read/write stream over a Memory buffer
Memory<byte> buffer = new byte[4096];
using Stream writableStream = Stream.FromWritableData(buffer);
// ... perform stream operations

// Create a read-only stream from ReadOnlySequence<byte>
ReadOnlySequence<byte> sequence = ...;
using Stream sequenceStream = sequence.AsStream();

Implementation Goals

  • High fidelity to .NET conventions and expectations around stream ownership and buffer lifetime.
  • High correctness through exhaustive test coverage for all implemented wrappers and API behaviors.
  • Agility to extend/adjust API and implementation in response to future dotnet/runtime API review and benchmarking.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Add Stream wrappers for memory and text-based types

5 participants