Skip to content

Port 'WindowsRuntimeBufferExtensions' to 'WinRT.Runtime.dll'#2298

Open
Sergio0694 wants to merge 31 commits intostaging/3.0from
user/sergiopedri/port-buffer-extensions
Open

Port 'WindowsRuntimeBufferExtensions' to 'WinRT.Runtime.dll'#2298
Sergio0694 wants to merge 31 commits intostaging/3.0from
user/sergiopedri/port-buffer-extensions

Conversation

@Sergio0694
Copy link
Member

Title.

Change WindowsRuntimePinnedArrayBuffer.Buffer from an internal property getter to a public unsafe Buffer() method (inlined) and update call sites. Also make GetArraySegment public, add an XML <returns> doc and AggressiveInlining, and remove a redundant Debug.Assert for Capacity. Update ABI and WindowsRuntimeBufferMarshal usages to call Buffer(). These changes expose the buffer access method and unify callers to the new signature.
Introduce WindowsRuntimeExternalArrayBuffer, an internal sealed implementation of IBuffer that wraps an external byte[] with support for offset, length and capacity. Provides Buffer() and GetArraySegment() APIs and a Length setter that enforces capacity (throws E_BOUNDS). Implements a pinning strategy using PinnedGCHandle and caches the pinned data pointer with Interlocked.CompareExchange to be thread-safe, and disposes the pinned handle in a finalizer. Includes debug assertions for constructor parameters and is marked as a managed-only WinRT type.
Introduce ABI support and a custom marshaller for WindowsRuntimeExternalArrayBuffer. Adds TypeMapAssociation, a file-scoped ABI type, and a fixed set of ComInterfaceEntry vtables (IBuffer, IBufferByteAccess, IStringable, IWeakReferenceSource, IMarshal, IAgileObject, IInspectable, IUnknown). Implements a WindowsRuntimeComWrappersMarshallerAttribute that provides vtable computation and GetOrCreateComInterfaceForObject behavior, and a native IBufferByteAccess implementation with an unmanaged Buffer method that returns the underlying byte pointer and converts exceptions to HRESULTs.
Add handling for the WindowsRuntimeExternalArrayBuffer type in WindowsRuntimeBufferMarshal to extract underlying memory and array segments. Introduces early-return branches that call Buffer()/GetArraySegment() on external array buffers and adds clarifying comments; existing pinned-array handling remains unchanged.
Remove an extra 'Streams' nesting by relocating several Windows.Storage.Streams files into the Windows.Storage/Streams folder. This cleans up the directory layout to better match namespaces and simplify imports.

Files moved:
- Windows.Storage.Streams/Buffers/WindowsRuntimeBuffer.cs (from ...Streams/Buffers/...)
- Windows.Storage/Streams/IBuffer.cs (from ...Streams/Streams/...)
- Windows.Storage/Streams/IInputStream.cs (from ...Streams/Streams/...)
- Windows.Storage/Streams/IOutputStream.cs (from ...Streams/Streams/...)
- Windows.Storage/Streams/IRandomAccessStream.cs (from ...Streams/Streams/...)
- Windows.Storage/Streams/InputStreamOptions.cs (from ...Streams/Streams/...)
Replace the manual bounds check and construction of ArgumentOutOfRangeException in the Length setter with ArgumentOutOfRangeException.ThrowIfBufferLengthExceedsCapacity(value, Capacity). This centralizes validation, simplifies the code, and ensures consistent exception details (including HResult) when the buffer length exceeds capacity.
Introduce ReadOnlySpan<byte> GetSpan() implementations for WindowsRuntimePinnedArrayBuffer and WindowsRuntimeExternalArrayBuffer. Both methods use MemoryMarshal.GetArrayDataReference and Unsafe.Add to create a ReadOnlySpan via MemoryMarshal.CreateReadOnlySpan and are annotated with AggressiveInlining. The pinned variant notes that parameters were validated at construction so offset/capacity checks are skipped; the external variant references the same rationale to avoid extra overhead while providing efficient span access to the buffer data.
Rename WindowsRuntimePinnedArrayBuffer.GetSpan to GetSpanForCapacity to make the semantics explicit: the returned ReadOnlySpan<byte> covers Capacity (not Length). Update WindowsRuntimeExternalArrayBuffer XML doc cref to point to the new member and add a remark documenting the capacity-based length. Method inlining and implementation remain unchanged.
Introduce WindowsRuntimeBufferExtensions with three AsBuffer overloads for byte[]: AsBuffer(source), AsBuffer(source, offset, length) and AsBuffer(source, offset, length, capacity). The methods validate arguments and return a WindowsRuntimeExternalArrayBuffer that exposes the byte array as an IBuffer with specified length and capacity. Includes MIT header and preserved commented exception message checks for range/capacity validation.
Change ReadOnlySpan<byte> -> Span<byte> for GetSpanForCapacity in WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer to allow mutable access and use MemoryMarshal.CreateSpan. Add a new Argument_InvalidIBufferInstance message. Extend WindowsRuntimeBufferExtensions: adjust AsBuffer overloads to use named args, add CopyTo(ReadOnlySpan<byte>, IBuffer[, uint]) methods to efficiently copy into IBuffer (with GC.KeepAlive and Length update semantics), and add a private GetSpanForCapacity(IBuffer) helper that retrieves a Span<byte> for native, external-array, or pinned-array backed buffers and throws the new argument exception for unsupported IBuffer instances. Also add a few using directives required by the new code.
Refine XML docs for ReadOnlySpan<byte>.CopyTo overloads (singular "value" wording) and add missing <exception> tags documenting ArgumentNullException, ArgumentException and ArgumentOutOfRangeException cases. Introduce two new byte[] CopyTo overloads: CopyTo(byte[] source, IBuffer destination) and CopyTo(byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count). Both delegate to the existing ReadOnlySpan<byte> implementations (using AsSpan) and perform null checks before calling into the span-based logic.
Add two CopyTo overloads to copy from IBuffer to Span<byte> (full-buffer and ranged variants). Implement argument checks, early-return for zero-length copies, use GetSpanForCapacity(...).Slice for span slicing and call GC.KeepAlive(source) after the copy. Also replace a range-based span slice with explicit Slice, add a pragma to suppress IDE0057, and update XML docs and exception wording to better describe destination capacity requirements.
Add two convenience overloads for copying IBuffer contents to byte arrays: CopyTo(this IBuffer, byte[]) and CopyTo(this IBuffer, uint sourceIndex, byte[] destination, int destinationIndex, int count). Both perform null checks and forward to the existing Span-based CopyTo implementation. Also update the XML summary on the Span-based CopyTo to correctly reference Span<T>. These changes make it easier for callers using arrays to copy buffer data without manually creating spans.
Add two IBuffer.CopyTo overloads to copy entire buffers or a specified range between IBuffer instances, including XML docs. Introduce System.Diagnostics and Debug.Assert checks for int.MaxValue bounds, early-return for zero-length copies, Span-based copy logic, GC.KeepAlive calls, and update destination.Length after copy. Also add Debug.Assert guards in existing copy helpers and argument null checks to improve safety and correctness.
Introduce two ToArray extension overloads for IBuffer: a parameterless variant that copies the entire buffer and an overload that accepts a sourceIndex and count. Both perform null/argument validation, handle a zero-length fast path (returning an empty array), allocate an uninitialized byte[] via GC.AllocateUninitializedArray, and copy data using the buffer's CopyTo method. Some additional range checks (Array.MaxLength and capacity/remaining-space checks) are present but commented out.
Introduce IsSameData(IBuffer, IBuffer?) to detect whether two IBuffer instances share the same underlying memory (handles both managed and native-backed buffers). Add TryGetNativeSpanForCapacity and TryGetManagedSpanForCapacity to safely obtain Span<byte> for a buffer's Capacity without throwing/pinning, and refactor GetSpanForCapacity to use these helpers. Document potential exceptions for IBufferByteAccess.Buffer failures on various CopyTo/ToArray APIs and add necessary using/imports and attributes. These changes avoid unnecessary pinning, centralize native/managed span retrieval, and improve diagnostics when interacting with IBuffer internals.
Introduce GetByte(this IBuffer, uint) to WindowsRuntimeBufferExtensions with XML docs. The method validates the source is not null, obtains a Span<byte> via GetSpanForCapacity, reads the byte at the specified offset, and calls GC.KeepAlive to ensure the buffer remains alive. Provides a convenient, safe way to read a single byte from an IBuffer instance.
Introduce WindowsRuntimeBufferMemoryStream, an internal sealed MemoryStream implementation backed by a Windows.Storage.Streams.IBuffer. The stream constructor accepts an IBuffer, a backing byte[] and an offset and initializes the stream length from the buffer. Overrides SetLength, Write (sync and span), WriteAsync (task and ValueTask) and WriteByte to keep the IBuffer.Length in sync with the stream. This supports managed-to-WinRT buffer interop without parameter validation in the constructor.
Introduce WindowsRuntimeBufferUnmanagedMemoryStream: an UnmanagedMemoryStream implementation backed by a native IBuffer. The new class exposes a constructor accepting an IBuffer and native byte* memory, overrides SetLength and various Write APIs (sync/async/Span/byte) to keep the IBuffer.Length in sync, and uses FileAccess.ReadWrite. Also add a private _buffer field (with comment) to WindowsRuntimeBufferMemoryStream.cs. Note: the UnmanagedMemoryStream constructor does not validate parameters and uses unsafe pointer input.
Expose underlying array accessors on WindowsRuntimeExternalArrayBuffer and WindowsRuntimePinnedArrayBuffer (GetArray(out int offset)), and add MemoryStream <-> IBuffer helpers in WindowsRuntimeBufferExtensions: GetWindowsRuntimeBuffer overloads to create IBuffer from MemoryStream, and AsStream to wrap an IBuffer as a Stream (handles array-backed and native buffers). Also add System.IO using and fix inverted logic in GetSpanForCapacity to correctly throw for invalid IBuffer instances. These changes enable sharing memory between MemoryStream and IBuffer and provide a convenient Stream view over IBuffer data.
Add a set of ArgumentException/ArgumentOutOfRange/UnauthorizedAccess helpers to WindowsRuntimeExceptionExtensions and corresponding message constants to WindowsRuntimeExceptionMessages. Replace in-file commented/manual validation checks in WindowsRuntimeBufferExtensions with calls to the new ThrowIf* helpers (e.g. ThrowIfInsufficientArrayElementsAfterOffset, ThrowIfInsufficientBufferCapacity, ThrowIfBufferIndexExceedsCapacity/Length, ThrowIfInsufficientSpaceInSourceBuffer/TargetBuffer, ThrowIfStreamPositionBeyondEndOfStream, ThrowIfBufferLengthExceedsArrayMaxLength, ThrowInvalidIBufferInstance, ThrowInternalBufferAccess). Helpers are annotated for inlining/stack-trace hiding and centralize error text creation for clearer, consistent validation and minor performance improvements.
Delete WinRT buffer implementation and helpers for Windows.Storage.Streams (IBufferByteAccess.cs, IMarshal.cs, IMemoryBufferByteAccess.cs, WindowsRuntimeBuffer.cs, WindowsRuntimeBufferExtensions.cs) and remove their entries from src/cswinrt/cswinrt.vcxproj and cswinrt.vcxproj.filters. Cleans up deprecated/unneeded buffer wrapper code and project file references.
Copy link

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

Ports the WindowsRuntimeBufferExtensions/buffer plumbing from the cswinrt strings additions into WinRT.Runtime2 (WinRT.Runtime.dll), removing the old implementations from the cswinrt project.

Changes:

  • Removed legacy buffer extension + buffer/COM helper sources from src/cswinrt/strings/additions/Windows.Storage.Streams.
  • Added WindowsRuntimeBufferExtensions (and supporting buffer + stream types) under src/WinRT.Runtime2.
  • Added/expanded centralized exception messages and guard helpers for the new APIs.

Reviewed changes

Copilot reviewed 17 out of 23 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs Removed legacy IBuffer extension methods from cswinrt strings additions.
src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs Removed legacy managed IBuffer implementation from cswinrt strings additions.
src/cswinrt/strings/additions/Windows.Storage.Streams/IMemoryBufferByteAccess.cs Removed legacy ABI vtable helper from cswinrt strings additions.
src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs Removed legacy buffer IMarshal implementation from cswinrt strings additions.
src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs Removed legacy IBufferByteAccess definitions from cswinrt strings additions.
src/cswinrt/cswinrt.vcxproj Removed deleted sources from the vcxproj items list.
src/cswinrt/cswinrt.vcxproj.filters Removed deleted sources from the filters listing.
src/WinRT.Runtime2/Windows.Storage/Buffers/WindowsRuntimeBufferExtensions.cs Added new runtime-owned IBuffer extension methods + stream helpers.
src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionMessages.cs Added new shared exception message constants used by the port.
src/WinRT.Runtime2/Properties/WindowsRuntimeExceptionExtensions.cs Added new guard/throw helper methods used by the port.
src/WinRT.Runtime2/InteropServices/WindowsRuntimeBufferMarshal.cs Updated marshal helpers to recognize the new external array buffer type and new pinned-buffer APIs.
src/WinRT.Runtime2/InteropServices/Buffers/WindowsRuntimePinnedArrayBuffer.cs Updated pinned buffer to expose Buffer() and added helpers for spans/arrays.
src/WinRT.Runtime2/InteropServices/Buffers/WindowsRuntimeExternalArrayBuffer.cs Added external-array-backed managed IBuffer implementation.
src/WinRT.Runtime2/InteropServices/Buffers/MemoryStreams/WindowsRuntimeBufferUnmanagedMemoryStream.cs Added UnmanagedMemoryStream wrapper for native IBuffer.
src/WinRT.Runtime2/InteropServices/Buffers/MemoryStreams/WindowsRuntimeBufferMemoryStream.cs Added MemoryStream wrapper for managed IBuffer.
src/WinRT.Runtime2/ABI/WindowsRuntime.InteropServices/Buffers/WindowsRuntimePinnedArrayBuffer.cs Updated ABI projection to call Buffer() on pinned buffer.
src/WinRT.Runtime2/ABI/WindowsRuntime.InteropServices/Buffers/WindowsRuntimeExternalArrayBuffer.cs Added ABI projection for the new external-array buffer type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sergio0694 and others added 3 commits February 25, 2026 15:38
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Replace the incorrect Argument_BufferIndexExceedsLength usage with a new Argument_BufferOffsetExceedsLength in WindowsRuntimeExceptionExtensions to provide a more accurate error for offset checks. Add the corresponding constant message to WindowsRuntimeExceptionMessages.
Introduce clearer managed/native unwrapping helpers and simplify buffer logic. Adds TryGetArray and TryGetData to handle managed arrays (array + offset) and native pointers respectively, replaces span-based helpers, and updates equality, span retrieval and stream creation paths to use the new helpers. GetSpanForCapacity now prefers managed arrays, then native data, and throws for unrecognized buffers. Also removes the now-unused ThrowInvalidIBufferInstance helper from WindowsRuntimeExceptionExtensions.cs. These changes reduce low-level unsafe span usage, centralize native access, and make equality/stream logic rely on array/offset or raw pointers.
Copy link

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

Copilot reviewed 17 out of 23 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Remove unused using directives System.Runtime.CompilerServices and System.Runtime.InteropServices from WindowsRuntimeBufferExtensions.cs to clean up imports. No functional changes; reduces compiler warnings for unused namespaces.
Remove the special-case emission of the TypeMapAssociation assembly attribute from src/cswinrt/main.cpp. Add using Windows.Storage.Buffers and tidy up using directives in StreamOperationsImplementation.cs, WinRtIOHelper.cs and WinRtToNetFxStreamAdapter.cs (also adjust placement of System.Diagnostics.CodeAnalysis). These changes prepare the additions to use Windows.Storage.Buffers types and stop emitting the assembly-level TypeMapAssociation here.
Remove the unused System.Runtime.InteropServices.WindowsRuntime using directives from WinRtIOHelper.cs and WinRtToNetFxStreamAdapter.cs. This is a tidy-up to eliminate redundant imports (and related compiler warnings); no functional changes.
Replace direct WindowsRuntimeBuffer cast and TryGetUnderlyingData usage with null/placeholder assignments and TODO comments in StreamOperationsImplementation.cs. This temporarily disables the optimized managed-array path (the buffer.TryGetUnderlyingData branch) and keeps the generic allocation fallback intact. Restore the original cast and TryGetUnderlyingData calls later to recover the optimized behavior.
Remove obsolete System.Runtime.InteropServices.WindowsRuntime using directives from multiple test files and add using Windows.Storage.Buffers where needed. Affected files: FunctionalTests/Async/Program.cs, ObjectLifetimeTests/{App,MainWindow,MyControl,ObjectLifetimePage}.xaml.cs, UnitTest/ApiCompatTests.cs, and UnitTest/TestComponentCSharp_Tests.cs. This updates imports to use Windows.Storage.Buffers for buffer-related types and cleans up unnecessary WindowsRuntime interop using statements.
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.

2 participants