Skip to content

Add StringBuilder.MoveChunks(StringBuilder) API#127823

Merged
tannergooding merged 6 commits intomainfrom
copilot/api-proposal-read-only-view-stringbuilder
May 6, 2026
Merged

Add StringBuilder.MoveChunks(StringBuilder) API#127823
tannergooding merged 6 commits intomainfrom
copilot/api-proposal-read-only-view-stringbuilder

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 5, 2026

Implements the approved API from #97570, enabling O(1) transfer of a StringBuilder's chunk chain to a fresh instance so that callers (e.g. Roslyn's StringBuilderText) can hold a no-copy, immutable view of the contents while releasing the original.

namespace System.Text;

public partial class StringBuilder
{
    public static StringBuilder MoveChunks(StringBuilder source);
}

Description

  • StringBuilder.MoveChunks (src/libraries/System.Private.CoreLib/.../StringBuilder.cs)
    • Constructs the returned StringBuilder via the existing private StringBuilder(StringBuilder from) copy constructor, which transfers m_ChunkChars, m_ChunkPrevious, m_ChunkLength, m_ChunkOffset, and m_MaxCapacity from source and avoids the wasted 16-char default buffer that new StringBuilder() would allocate.
    • Drains source by zeroing m_ChunkChars (set to Array.Empty<char>()), m_ChunkPrevious, m_ChunkLength, and m_ChunkOffset, while preserving m_MaxCapacity. The drained source is left in an empty but fully usable state — distinct from Clear(), which retains the buffer. Subsequent append or insert operations on source will succeed, allocating new buffers as needed.
    • Throws ArgumentNullException when source is null.
  • Ref assembly updated (System.Runtime.cs).
  • Tests in StringBuilderTests cover null arg, empty source, single chunk, multi-chunk chain (using MemoryMarshal.TryGetArray to assert backing-array identity by reference, not just content), calling MoveChunks on an already-drained source, and that the drained source remains usable with its original MaxCapacity preserved.

Customer Impact

Unblocks the no-copy SourceText shape Roslyn has been waiting on (dotnet/roslyn#61326) and provides a general primitive for transferring ownership of a StringBuilder's contents without materializing a contiguous string or array of chunks.

Regression?

  • Yes
  • No

New API; no behavior change to existing members.

Risk

  • Low
  • Medium
  • High

Pure addition; the implementation only manipulates internal fields already reachable via existing code paths. After the call, source retains its original MaxCapacity and is fully usable as an empty StringBuilder.

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f88c747f-738f-4828-b2b4-e5a1206af008

Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 5, 2026 19:43
Copilot AI requested review from Copilot and removed request for Copilot May 5, 2026 19:45
Copilot AI changed the title [WIP] Add read-only view for StringBuilder body and clear method Add StringBuilder.MoveChunks(StringBuilder) API May 5, 2026
Copilot AI requested a review from tannergooding May 5, 2026 19:46
Comment thread src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs Outdated
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/91a900e5-c44d-4d1f-8e7a-cec9c663fb20

Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 5, 2026 21:12
Copilot AI requested a review from tannergooding May 5, 2026 21:13
Copilot AI requested review from Copilot and removed request for Copilot May 5, 2026 21:52
@tannergooding tannergooding marked this pull request as ready for review May 5, 2026 21:55
Copilot AI review requested due to automatic review settings May 5, 2026 21:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 adds a new public StringBuilder.MoveChunks(StringBuilder source) API to enable O(1) transfer of an existing StringBuilder’s chunk chain to a new StringBuilder, leaving the source drained.

Changes:

  • Added StringBuilder.MoveChunks(StringBuilder) implementation in System.Private.CoreLib.
  • Updated the System.Runtime reference assembly to expose the new API.
  • Added new StringBuilderTests coverage for null handling, empty/single/multi-chunk transfers, and post-drain behavior.

Reviewed changes

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

File Description
src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs Adds the MoveChunks API and drains the source instance after transferring chunk state.
src/libraries/System.Runtime/ref/System.Runtime.cs Adds the public ref signature for StringBuilder.MoveChunks.
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/StringBuilderTests.cs Adds tests for chunk transfer behavior and drained-source behavior (including reflection-based validation).

Copy link
Copy Markdown
Member

@tarekgh tarekgh left a comment

Choose a reason for hiding this comment

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

Left minor comments, LGTM, otherwise.

One last question, is there any thread safety worries? I mean the source chunks get mutated while moving?

…r review feedback

- Remove `source.m_MaxCapacity = 0` so the drained source remains fully usable
- Update XML docs to clarify source is empty but usable with MaxCapacity preserved
- Update multi-chunk test to use MemoryMarshal.TryGetArray for backing-array identity checks
- Replace MoveChunks_DrainedSourceIsUnusable with MoveChunks_DrainedSource_RemainsUsable
- Add MoveChunks_AlreadyDrainedSource_ProducesEmptyDestination test
- Update AssertSourceIsDrained to remove MaxCapacity==0 assertion
- Add null-forgiving operators to s_chunkCharsField reflection call
- Add System.Runtime.InteropServices using directive for MemoryMarshal

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7936562a-e7c5-4539-885e-7d1582969b64

Co-authored-by: tannergooding <10487869+tannergooding@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 5, 2026

Left minor comments, LGTM, otherwise.

One last question, is there any thread safety worries? I mean the source chunks get mutated while moving?

StringBuilder has never been thread-safe, and MoveChunks is no different — it's a single-threaded ownership-transfer operation. The caller is responsible for ensuring no other thread is mutating source concurrently, just as with any other StringBuilder mutation. No additional synchronization is needed.

@tannergooding
Copy link
Copy Markdown
Member

One last question, is there any thread safety worries? I mean the source chunks get mutated while moving?

@tarekgh, no more than any type has. Most types are not explicitly thread safe and so require some kind of using lock if they are expecting multithreaded access to be going on.

@tannergooding
Copy link
Copy Markdown
Member

/ba-g net security tests failed with null log

@tannergooding tannergooding merged commit 4c972df into main May 6, 2026
148 of 152 checks passed
@tannergooding tannergooding deleted the copilot/api-proposal-read-only-view-stringbuilder branch May 6, 2026 16:01
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]: Get a Read-only view of a StringBuilder body and clear the StringBuilder.

9 participants