diff options
author | Ian Hays <ianha@microsoft.com> | 2018-01-22 19:07:15 -0800 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2018-01-22 19:07:15 -0800 |
commit | c69e8148e42d581ceec9fd3d8ef8d65706b14235 (patch) | |
tree | 93bcf1013337631dca0108a68d8aa79a8d9a2518 /src | |
parent | d69118807bff88c48ace28d4befa2c41bf4e7766 (diff) | |
download | coreclr-c69e8148e42d581ceec9fd3d8ef8d65706b14235.tar.gz coreclr-c69e8148e42d581ceec9fd3d8ef8d65706b14235.tar.bz2 coreclr-c69e8148e42d581ceec9fd3d8ef8d65706b14235.zip |
Misc Span/Memory changes (#15941)
* Misc Span/Memory additions
- Add MemoryMarshal.CreateSpan to replace DangerousCreate
- Add MemoryMarshal.CreateReadOnlySpan to replace DangerousCreate
- Add MemoryMarshal.Cast to replace NonPortableCast
- Add ToString override to Span and ReadOnlySpan
- Add ToEnumerable function to MemoryMarshal that takes a ReadOnlyMemory
Diffstat (limited to 'src')
7 files changed, 144 insertions, 56 deletions
diff --git a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems index bfe1eec776..89d7cfd039 100644 --- a/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/mscorlib/shared/System.Private.CoreLib.Shared.projitems @@ -435,6 +435,7 @@ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\LayoutKind.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalAsAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalDirectiveException.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.Fast.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OptionalAttribute.cs" /> <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\OutAttribute.cs" /> diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs index d15cc1cc8c..595fb56313 100644 --- a/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs +++ b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Text; +using System.Runtime.InteropServices; namespace System { @@ -703,7 +704,7 @@ namespace System if (nextChar >= 0 && nextChar != '%') { char nextCharChar = (char)nextChar; - StringBuilder origStringBuilder = FormatCustomized(dateTime, ReadOnlySpan<char>.DangerousCreate(null, ref nextCharChar, 1), dtfi, offset, result); + StringBuilder origStringBuilder = FormatCustomized(dateTime, MemoryMarshal.CreateReadOnlySpan<char>(ref nextCharChar, 1), dtfi, offset, result); Debug.Assert(ReferenceEquals(origStringBuilder, result)); tokenLen = 2; } diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs index bf12b246b0..a66e4600aa 100644 --- a/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs +++ b/src/mscorlib/shared/System/Globalization/TimeSpanFormat.cs @@ -4,6 +4,7 @@ using System.Text; using System.Diagnostics; +using System.Runtime.InteropServices; namespace System.Globalization { @@ -315,7 +316,7 @@ namespace System.Globalization if (nextChar >= 0 && nextChar != (int)'%') { char nextCharChar = (char)nextChar; - StringBuilder origStringBuilder = FormatCustomized(value, ReadOnlySpan<char>.DangerousCreate(null, ref nextCharChar, 1), dtfi, result); + StringBuilder origStringBuilder = FormatCustomized(value, MemoryMarshal.CreateReadOnlySpan<char>(ref nextCharChar, 1), dtfi, result); Debug.Assert(ReferenceEquals(origStringBuilder, result)); tokenLen = 2; } diff --git a/src/mscorlib/shared/System/ReadOnlySpan.cs b/src/mscorlib/shared/System/ReadOnlySpan.cs index 4e6339a74c..06af661d43 100644 --- a/src/mscorlib/shared/System/ReadOnlySpan.cs +++ b/src/mscorlib/shared/System/ReadOnlySpan.cs @@ -260,6 +260,11 @@ namespace System } /// <summary> + /// Returns a <see cref="String"/> with the name of the type and the number of elements + /// </summary> + public override string ToString() => "System.Span[" + Length.ToString() + "]"; + + /// <summary> /// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/> /// </summary> public static implicit operator ReadOnlySpan<T>(T[] array) => array != null ? new ReadOnlySpan<T>(array) : default; diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs new file mode 100644 index 0000000000..4e0caed6ad --- /dev/null +++ b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using Internal.Runtime.CompilerServices; + +namespace System.Runtime.InteropServices +{ + /// <summary> + /// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>, + /// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>. + /// </summary> + public static partial class MemoryMarshal + { + /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary> + /// <param name="readOnlyMemory">The <see cref="ReadOnlyMemory{T}"/>.</param> + /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns> + /// <remarks> + /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution. <see cref="ReadOnlyMemory{T}"/> is used + /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created + /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to. The method exists to enable variables typed + /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>. + /// </remarks> + public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> readOnlyMemory) => + Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref readOnlyMemory); + + /// <summary> + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// </summary> + public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value; + + /// <summary> + /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element + /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. + /// </summary> + public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value; + + /// <summary> + /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <remarks> + /// Supported only for platforms that support misaligned memory access. + /// </remarks> + /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new Span<TTo>( + ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()), + checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()))); + } + + /// <summary> + /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>. + /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety. + /// </summary> + /// <remarks> + /// Supported only for platforms that support misaligned memory access. + /// </remarks> + /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param> + /// <exception cref="System.ArgumentException"> + /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers. + /// </exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> source) + where TFrom : struct + where TTo : struct + { + if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom)); + if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo)); + + return new ReadOnlySpan<TTo>( + ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)), + checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()))); + } + + /// <summary> + /// Create a new span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because the + /// <paramref name="length"/> is not checked. + /// </summary> + /// <param name="reference">A reference to data.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span<T> CreateSpan<T>(ref T reference, int length) => new Span<T>(ref reference, length); + + /// <summary> + /// Create a new read-only span over a portion of a regular managed object. This can be useful + /// if part of a managed object represents a "fixed array." This is dangerous because the + /// <paramref name="length"/> is not checked. + /// </summary> + /// <param name="reference">A reference to data.</param> + /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) => new ReadOnlySpan<T>(ref reference, length); + } +} diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.cs index 63e301beca..cc815d0598 100644 --- a/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.cs +++ b/src/mscorlib/shared/System/Runtime/InteropServices/MemoryMarshal.cs @@ -4,9 +4,7 @@ using System.Buffers; using System.Runtime.CompilerServices; -#if !FEATURE_PORTABLE_SPAN -using Internal.Runtime.CompilerServices; -#endif // FEATURE_PORTABLE_SPAN +using System.Collections.Generic; namespace System.Runtime.InteropServices { @@ -14,58 +12,8 @@ namespace System.Runtime.InteropServices /// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>, /// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>. /// </summary> - public static class MemoryMarshal + public static partial class MemoryMarshal { - /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary> - /// <param name="readOnlyMemory">The <see cref="ReadOnlyMemory{T}"/>.</param> - /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns> - /// <remarks> - /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution. <see cref="ReadOnlyMemory{T}"/> is used - /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created - /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to. The method exists to enable variables typed - /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>. - /// </remarks> - public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> readOnlyMemory) => - Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref readOnlyMemory); - -#if FEATURE_PORTABLE_SPAN - /// <summary> - /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element - /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. - /// </summary> - public static ref T GetReference<T>(Span<T> span) - { - if (span.Pinnable == null) - unsafe { return ref Unsafe.AsRef<T>(span.ByteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset<T>(ref span.Pinnable.Data, span.ByteOffset); - } - - /// <summary> - /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element - /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. - /// </summary> - public static ref T GetReference<T>(ReadOnlySpan<T> span) - { - if (span.Pinnable == null) - unsafe { return ref Unsafe.AsRef<T>(span.ByteOffset.ToPointer()); } - else - return ref Unsafe.AddByteOffset<T>(ref span.Pinnable.Data, span.ByteOffset); - } -#else - /// <summary> - /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element - /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. - /// </summary> - public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value; - - /// <summary> - /// Returns a reference to the 0th element of the ReadOnlySpan. If the Span is empty, returns a reference to the location where the 0th element - /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. - /// </summary> - public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value; -#endif // FEATURE_PORTABLE_SPAN - /// <summary> /// Get an array segment from the underlying memory. /// If unable to get the array segment, return false with a default array segment. @@ -90,5 +38,18 @@ namespace System.Runtime.InteropServices arraySegment = default; return false; } + + /// <summary> + /// Creates an <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /> to allow + /// the <paramref name="memory" /> to be used in existing APIs that take an <see cref="IEnumerable{T}"/>. + /// </summary> + /// <typeparam name="T">The element type of the <paramref name="memory" />.</typeparam> + /// <param name="memory">The ReadOnlyMemory to view as an <see cref="IEnumerable{T}"/></param> + /// <returns>An <see cref="IEnumerable{T}"/> view of the given <paramref name="memory" /></returns> + public static IEnumerable<T> ToEnumerable<T>(ReadOnlyMemory<T> memory) + { + for (int i = 0; i < memory.Length; i++) + yield return memory.Span[i]; + } } } diff --git a/src/mscorlib/shared/System/Span.cs b/src/mscorlib/shared/System/Span.cs index 5a813174d9..8c57ab4883 100644 --- a/src/mscorlib/shared/System/Span.cs +++ b/src/mscorlib/shared/System/Span.cs @@ -341,6 +341,11 @@ namespace System } /// <summary> + /// Returns a <see cref="String"/> with the name of the type and the number of elements + /// </summary> + public override string ToString() => "System.Span[" + Length.ToString() + "]"; + + /// <summary> /// Defines an implicit conversion of an array to a <see cref="Span{T}"/> /// </summary> public static implicit operator Span<T>(T[] array) => array != null ? new Span<T>(array) : default; |