Skip to content

Derive from Stream in ReadOnlyMemoryStream and WritableMemoryStream#130744

Merged
jozkee merged 5 commits into
mainfrom
dacantu/roms_wms_stream
Jul 16, 2026
Merged

Derive from Stream in ReadOnlyMemoryStream and WritableMemoryStream#130744
jozkee merged 5 commits into
mainfrom
dacantu/roms_wms_stream

Conversation

@jozkee

@jozkee jozkee commented Jul 15, 2026

Copy link
Copy Markdown
Member

Instead of deriving from MemoryStream.
Also support WritableMemoryStream.SetLength(long) since 1) is writable and seekable and 2) to properly align with MemoryStream user-buffer mode.

Fixes #130330.

@jozkee jozkee added this to the 11.0.0 milestone Jul 15, 2026
@jozkee jozkee self-assigned this Jul 15, 2026
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:20 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:20 — with GitHub Actions Inactive
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@jozkee
jozkee had a problem deploying to copilot-pat-pool July 15, 2026 00:22 — with GitHub Actions Failure
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:23 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:23 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:23 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates System.IO.ReadOnlyMemoryStream and System.IO.WritableMemoryStream to derive from Stream (instead of MemoryStream), removing MemoryStream-specific surface area and adding WritableMemoryStream.SetLength(long) support within the fixed-capacity backing buffer.

Changes:

  • Change ReadOnlyMemoryStream / WritableMemoryStream base type from MemoryStream to Stream and update the reference assembly accordingly.
  • Implement WritableMemoryStream.SetLength(long) to allow shrinking/growing length up to the fixed capacity (including zeroing newly exposed regions).
  • Update stream conformance tests and add/adjust unit tests to validate fixed-capacity semantics (write/set-length beyond capacity, extending after shrink, etc.).

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/libraries/System.Private.CoreLib/src/System/IO/WritableMemoryStream.cs Re-implements WritableMemoryStream as a Stream, adds internal length tracking and SetLength behavior for fixed-capacity buffers.
src/libraries/System.Private.CoreLib/src/System/IO/ReadOnlyMemoryStream.cs Re-implements ReadOnlyMemoryStream as a Stream and ensures write operations throw.
src/libraries/System.Runtime/ref/System.Runtime.cs Updates public API contract: types now derive from Stream and drop MemoryStream-specific members.
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx Adds a new resource string used for stream position range validation errors.
src/libraries/System.Runtime/tests/System.IO.Tests/WritableMemoryStream/WritableMemoryStreamTests.cs Updates/extends unit coverage for fixed-capacity and new SetLength behaviors.
src/libraries/System.Runtime/tests/System.IO.Tests/WritableMemoryStream/WritableMemoryStreamConformanceTests.cs Adjusts conformance harness for the new SetLength behavior (but currently skips empty-case growth coverage).
src/libraries/System.Runtime/tests/System.IO.Tests/ReadOnlyMemoryStream/ReadOnlyMemoryStreamTests.cs Updates tests to validate write operations are not supported after base-type change.
src/libraries/System.Runtime/tests/System.IO.Tests/MemoryStream/MemoryStreamTests.cs Adds coverage for user-buffer MemoryStream fixed-capacity behaviors to align expectations.
src/libraries/System.Memory/tests/ReadOnlyBuffer/NativeWritableMemoryStream.ConformanceTests.cs Aligns native-buffer wrapper conformance expectations (but currently skips empty-case growth coverage).
src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs Updates shared conformance tests: ensures write/read tests provision sufficient capacity and adds a grow-from-empty write test.

Copilot AI review requested due to automatic review settings July 15, 2026 00:46
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:46 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:46 — with GitHub Actions Inactive
@jozkee
jozkee had a problem deploying to copilot-pat-pool July 15, 2026 00:48 — with GitHub Actions Failure
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:49 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:49 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 00:50 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Comment thread src/libraries/System.Runtime/ref/System.Runtime.cs
Copilot AI review requested due to automatic review settings July 15, 2026 14:03
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 14:03 — with GitHub Actions Inactive
@jozkee
jozkee had a problem deploying to copilot-pat-pool July 15, 2026 14:04 — with GitHub Actions Failure
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 14:06 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 14:06 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 14:06 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread src/libraries/System.Private.CoreLib/src/System/IO/MemoryStream.cs Outdated

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for taking care of this @jozkee. I've left some comments, but please don't block the PR only because I don't like given micro optimization (if it turns out it's not needed anymore with runtime async, we are just going to send another PR and remove it from everywhere).

private ReadOnlyMemory<byte> _memory;
private int _position;
private bool _isOpen;
private CachedCompletedInt32Task _lastReadTask; // The last successful task returned from ReadAsync

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subjective: I am not a big fan of this performance optimization.

@jakobbotsch what some of the BCL Streams do is that they keep a cached Task<int> instance and re-use it if the following read returned as many bytes as the previous one:

public Task<int> GetTask(int result)
{
if (_task is Task<int> task)
{
Debug.Assert(task.IsCompletedSuccessfully, "Expected that a stored last task completed successfully");
if (task.Result == result)
{
return task;
}
}
return _task = Task.FromResult(result);

So for example, when we open a large file and keep reading the contents using the buffer of the same size, we avoid allocating for all reads except of the first one (it initializes the cache) and the last one (assuming the number of bytes in 0 or just different than buffer size)

@jakobbotsch do we still need such a micro optimization with runtime async enabled?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakobbotsch do we still need such a micro optimization with runtime async enabled?

No, this pattern is actually quite detrimental to performance. Runtime async recognizes return Task.FromResult(x) specially, and it will turn it into something that never allocates if things are runtime async.

Similarly bad is stuff like this:

ValueTask<int> readResult = ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken);
return readResult.IsCompletedSuccessfully
? _lastSyncCompletedReadTask.GetTask(readResult.Result)
: readResult.AsTask();

This forces ValueTask<int> to be created instead of remaining as runtime async throughout if this just returned ReadAsync(...).AsTask() directly.

@tannergooding

Copy link
Copy Markdown
Member

Overall LGTM, but a few pieces of feedback/questions

Copilot AI review requested due to automatic review settings July 15, 2026 18:35
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 18:35 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 18:36 — with GitHub Actions Inactive
@jozkee
jozkee had a problem deploying to copilot-pat-pool July 15, 2026 18:37 — with GitHub Actions Failure
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 18:38 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 18:38 — with GitHub Actions Inactive
@jozkee
jozkee temporarily deployed to copilot-pat-pool July 15, 2026 18:38 — with GitHub Actions Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

@jozkee
jozkee enabled auto-merge (squash) July 15, 2026 20:56
@jozkee
jozkee merged commit f2281d5 into main Jul 16, 2026
143 of 146 checks passed
@jozkee
jozkee deleted the dacantu/roms_wms_stream branch July 16, 2026 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Amend WritableMemoryStream and ReadOnlyMemoryStream to derive from Stream, not MemoryStream

5 participants