diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs
index 2d36bd883..d2eb19e00 100644
--- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs
+++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs
@@ -59,58 +59,91 @@ public static IBuffer AsBuffer(this byte[] source, int offset, int length, int c
#endregion (Byte []).AsBuffer extensions
-#region (Byte []).CopyTo extensions for copying to an (IBuffer)
-
- ///
- /// Copies the contents of source
to destination
starting at offset 0.
- /// This method does NOT update destination.Length
.
- ///
- /// Array to copy data from.
- /// The buffer to copy to.
- public static void CopyTo(this byte[] source, IBuffer destination)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
-
- CopyTo(source, 0, destination, 0, source.Length);
- }
-
-
- ///
- /// Copies count
bytes from source
starting at offset sourceIndex
- /// to destination
starting at destinationIndex
.
- /// This method does NOT update destination.Length
.
- ///
- /// Array to copy data from.
- /// Position in the array from where to start copying.
- /// The buffer to copy to.
- /// Position in the buffer to where to start copying.
- /// The number of bytes to copy.
- public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
- if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
- if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex));
- if (source.Length < sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds, nameof(sourceIndex));
- if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset);
- if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity);
- if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer);
- if (count == 0) return;
-
- // If destination is backed by a managed array, use the array instead of the pointer as it does not require pinning:
- byte[] destDataArr;
- int destDataOffs;
- if (destination.TryGetUnderlyingData(out destDataArr, out destDataOffs))
- {
- global::System.Buffer.BlockCopy(source, sourceIndex, destDataArr, (int)(destDataOffs + destinationIndex), count);
- return;
- }
-
- IntPtr destPtr = destination.GetPointerAtOffset(destinationIndex);
- Marshal.Copy(source, sourceIndex, destPtr, count);
- }
-
+#region (Span).CopyTo extensions for copying to an (IBuffer)
+
+ ///
+ /// Copies the contents of source
to destination
starting at offset 0.
+ /// This method does NOT update destination.Length
.
+ ///
+ /// Span to copy data from.
+ /// The buffer to copy to.
+ public static void CopyTo(this Span source, IBuffer destination)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+
+ CopyTo(source, destination, 0);
+ }
+
+
+ ///
+ /// Copies count
bytes from source
starting at offset sourceIndex
+ /// to destination
starting at destinationIndex
.
+ /// This method does NOT update destination.Length
.
+ ///
+ /// Span to copy data from.
+ /// Position in the span from where to start copying.
+ /// The buffer to copy to.
+ /// Position in the buffer to where to start copying.
+ /// The number of bytes to copy.
+ public static void CopyTo(this Span source, IBuffer destination, uint destinationIndex)
+ {
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+ if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity);
+ if (destination.Capacity - destinationIndex < source.Length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer);
+ if (source.Length == 0) return;
+
+ Debug.Assert(destinationIndex <= int.MaxValue);
+
+ // If destination is backed by a managed memory, use the memory instead of the pointer as it does not require pinning:
+ Span destSpan = destination.TryGetUnderlyingData(out byte[] destDataArr, out int destOffset) ? destDataArr.AsSpan(destOffset + (int)destinationIndex) : destination.GetSpanForCapacityUnsafe(destinationIndex);
+ source.CopyTo(destSpan);
+
+ // Ensure destination stays alive for the copy operation
+ GC.KeepAlive(destination);
+
+ // Update Length last to make sure the data is valid
+ if (destinationIndex + source.Length > destination.Length)
+ {
+ destination.Length = destinationIndex + (uint)source.Length;
+ }
+ }
+
+#endregion (Span).CopyTo extensions for copying to an (IBuffer)
+
+#region (Byte []).CopyTo extensions for copying to an (IBuffer)
+
+ ///
+ /// Copies the contents of source
to destination
starting at offset 0.
+ /// This method does NOT update destination.Length
.
+ ///
+ /// Array to copy data from.
+ /// The buffer to copy to.
+ public static void CopyTo(this byte[] source, IBuffer destination)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+
+ CopyTo(source.AsSpan(), destination, 0);
+ }
+
+
+ ///
+ /// Copies count
bytes from source
starting at offset sourceIndex
+ /// to destination
starting at destinationIndex
.
+ /// This method does NOT update destination.Length
.
+ ///
+ /// Array to copy data from.
+ /// Position in the array from where to start copying.
+ /// The buffer to copy to.
+ /// Position in the buffer to where to start copying.
+ /// The number of bytes to copy.
+ public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+
+ CopyTo(source.AsSpan(sourceIndex, count), destination, destinationIndex);
+ }
+
#endregion (Byte []).CopyTo extensions for copying to an (IBuffer)
@@ -142,110 +175,98 @@ public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count)
#endregion (IBuffer).ToArray extensions for copying to a new (Byte [])
-#region (IBuffer).CopyTo extensions for copying to a (Byte [])
-
- public static void CopyTo(this IBuffer source, byte[] destination)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
-
- CopyTo(source, 0, destination, 0, checked((int)source.Length));
- }
-
-
- public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destination, int destinationIndex, int count)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
- if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
- if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex));
- if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length.");
- if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer);
- if (destination.Length < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds);
- if (destination.Length - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset);
- if (count == 0) return;
-
- // If source is backed by a managed array, use the array instead of the pointer as it does not require pinning:
- byte[] srcDataArr;
- int srcDataOffs;
- if (source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs))
- {
- global::System.Buffer.BlockCopy(srcDataArr, (int)(srcDataOffs + sourceIndex), destination, destinationIndex, count);
- return;
- }
-
- IntPtr srcPtr = source.GetPointerAtOffset(sourceIndex);
- Marshal.Copy(srcPtr, destination, destinationIndex, count);
- }
-
-#endregion (IBuffer).CopyTo extensions for copying to a (Byte [])
-
-
-#region (IBuffer).CopyTo extensions for copying to an (IBuffer)
-
- public static void CopyTo(this IBuffer source, IBuffer destination)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
-
- CopyTo(source, 0, destination, 0, source.Length);
- }
-
-
- public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destination, uint destinationIndex, uint count)
- {
- if (source == null) throw new ArgumentNullException(nameof(source));
- if (destination == null) throw new ArgumentNullException(nameof(destination));
- if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length.");
- if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer);
- if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity);
- if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer);
- if (count == 0) return;
-
- // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning:
- byte[] srcDataArr, destDataArr;
- int srcDataOffs, destDataOffs;
-
- bool srcIsManaged = source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs);
- bool destIsManaged = destination.TryGetUnderlyingData(out destDataArr, out destDataOffs);
-
- if (srcIsManaged && destIsManaged)
- {
- Debug.Assert(count <= int.MaxValue);
- Debug.Assert(sourceIndex <= int.MaxValue);
- Debug.Assert(destinationIndex <= int.MaxValue);
-
- global::System.Buffer.BlockCopy(srcDataArr!, srcDataOffs + (int)sourceIndex, destDataArr!, destDataOffs + (int)destinationIndex, (int)count);
- return;
- }
-
- IntPtr srcPtr, destPtr;
-
- if (srcIsManaged)
- {
- Debug.Assert(count <= int.MaxValue);
- Debug.Assert(sourceIndex <= int.MaxValue);
-
- destPtr = destination.GetPointerAtOffset(destinationIndex);
- Marshal.Copy(srcDataArr!, srcDataOffs + (int)sourceIndex, destPtr, (int)count);
- return;
- }
-
- if (destIsManaged)
- {
- Debug.Assert(count <= int.MaxValue);
- Debug.Assert(destinationIndex <= int.MaxValue);
-
- srcPtr = source.GetPointerAtOffset(sourceIndex);
- Marshal.Copy(srcPtr, destDataArr!, destDataOffs + (int)destinationIndex, (int)count);
- return;
- }
-
- srcPtr = source.GetPointerAtOffset(sourceIndex);
- destPtr = destination.GetPointerAtOffset(destinationIndex);
- MemCopy(srcPtr, destPtr, count);
- }
-
+#region (IBuffer).CopyTo extensions for copying to a (Span)
+
+ public static void CopyTo(this IBuffer source, Span destination)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+
+ CopyTo(source, 0, destination, checked((int)source.Length));
+ }
+
+ public static void CopyTo(this IBuffer source, uint sourceIndex, Span destination, int count)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
+ if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length.");
+ if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer);
+ if (destination.Length < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset);
+ if (count == 0) return;
+
+ Debug.Assert(sourceIndex <= int.MaxValue);
+
+ Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, count) : source.GetSpanForCapacityUnsafe(sourceIndex);
+ srcSpan.CopyTo(destination);
+
+ // Ensure source and destination stay alive for the copy operation
+ GC.KeepAlive(source);
+ }
+
+#endregion (IBuffer).CopyTo extensions for copying to a (Span)
+
+#region (IBuffer).CopyTo extensions for copying to a (Byte [])
+
+ public static void CopyTo(this IBuffer source, byte[] destination)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+
+ CopyTo(source, destination.AsSpan());
+ }
+
+ public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destination, int destinationIndex, int count)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+
+ CopyTo(source, sourceIndex, destination.AsSpan(destinationIndex, count), count);
+ }
+
+#endregion (IBuffer).CopyTo extensions for copying to a (Byte [])
+
+
+#region (IBuffer).CopyTo extensions for copying to an (IBuffer)
+
+ public static void CopyTo(this IBuffer source, IBuffer destination)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+
+ CopyTo(source, 0, destination, 0, source.Length);
+ }
+
+
+ public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destination, uint destinationIndex, uint count)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (destination == null) throw new ArgumentNullException(nameof(destination));
+ if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length.");
+ if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer);
+ if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity);
+ if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer);
+ if (count == 0) return;
+
+ Debug.Assert(count <= int.MaxValue);
+ Debug.Assert(sourceIndex <= int.MaxValue);
+ Debug.Assert(destinationIndex <= int.MaxValue);
+
+ // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning:
+ Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, (int)count) : source.GetSpanForCapacityUnsafe(sourceIndex);
+ Span destSpan = destination.TryGetUnderlyingData(out byte[] destDataArr, out int destOffset) ? destDataArr.AsSpan(destOffset + (int)destinationIndex) : destination.GetSpanForCapacityUnsafe(destinationIndex);
+
+ srcSpan.CopyTo(destSpan);
+
+ // Ensure source and destination stay alive for the copy operation
+ GC.KeepAlive(source);
+ GC.KeepAlive(destination);
+
+ // Update Length last to make sure the data is valid
+ if (destinationIndex + count > destination.Length)
+ {
+ destination.Length = destinationIndex + count;
+ }
+ }
+
#endregion (IBuffer).CopyTo extensions for copying to an (IBuffer)
@@ -440,13 +461,12 @@ public static byte GetByte(this IBuffer source, uint byteOffset)
return srcDataArr[srcDataOffs + byteOffset];
}
- IntPtr srcPtr = source.GetPointerAtOffset(byteOffset);
- unsafe
- {
- // Let's avoid an unnesecary call to Marshal.ReadByte():
- byte* ptr = (byte*)srcPtr;
- return *ptr;
- }
+ Span srcSpan = source.GetSpanForCapacityUnsafe(byteOffset);
+ byte value = srcSpan[0];
+
+ // Ensure source stays alive while we read values.
+ GC.KeepAlive(source);
+ return value;
}
#endregion Extensions for direct by-offset access to buffer data elements
@@ -596,39 +616,17 @@ public override void WriteByte(byte value)
_sourceBuffer.Length = (uint)Length;
}
} // class WindowsRuntimeBufferUnmanagedMemoryStream
-
- private static IntPtr GetPointerAtOffset(this IBuffer buffer, uint offset)
- {
- Debug.Assert(0 <= offset);
- Debug.Assert(offset < buffer.Capacity);
-
- unsafe
- {
- IntPtr buffPtr = buffer.As().Buffer;
- return new IntPtr((byte*)buffPtr + offset);
- }
- }
-
- private static unsafe void MemCopy(IntPtr src, IntPtr dst, uint count)
- {
- if (count > int.MaxValue)
- {
- MemCopy(src, dst, int.MaxValue);
- MemCopy(src + int.MaxValue, dst + int.MaxValue, count - int.MaxValue);
- return;
- }
-
- Debug.Assert(count <= int.MaxValue);
- int bCount = (int)count;
-
-
- // Copy via buffer.
- // Note: if becomes perf critical, we will port the routine that
- // copies the data without using Marshal (and byte[])
- byte[] tmp = new byte[bCount];
- Marshal.Copy(src, tmp, 0, bCount);
- Marshal.Copy(tmp, 0, dst, bCount);
- return;
+
+ private static Span GetSpanForCapacityUnsafe(this IBuffer buffer, uint offset)
+ {
+ Debug.Assert(0 <= offset);
+ Debug.Assert(offset < buffer.Capacity);
+
+ unsafe
+ {
+ IntPtr buffPtr = buffer.As().Buffer;
+ return new Span((byte*)buffPtr + offset, (int)(buffer.Capacity - offset));
+ }
}
#endregion Private plumbing
} // class WindowsRuntimeBufferExtensions