Fix encoding methods for Span.Empty (#16748)
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Runtime / InteropServices / MemoryMarshal.Fast.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Buffers;
6 using System.Runtime.CompilerServices;
7 using System.Collections.Generic;
8 using Internal.Runtime.CompilerServices;
9
10 namespace System.Runtime.InteropServices
11 {
12     /// <summary>
13     /// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>,
14     /// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>.
15     /// </summary>
16     public static partial class MemoryMarshal
17     {
18         /// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
19         /// <param name="readOnlyMemory">The <see cref="ReadOnlyMemory{T}"/>.</param>
20         /// <returns>A <see cref="Memory{T}"/> representing the same memory as the <see cref="ReadOnlyMemory{T}"/>, but writable.</returns>
21         /// <remarks>
22         /// <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> must be used with extreme caution.  <see cref="ReadOnlyMemory{T}"/> is used
23         /// to represent immutable data and other memory that is not meant to be written to; <see cref="Memory{T}"/> instances created
24         /// by <see cref="AsMemory{T}(ReadOnlyMemory{T})"/> should not be written to.  The method exists to enable variables typed
25         /// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>.
26         /// </remarks>
27         public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> readOnlyMemory) =>
28             Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref readOnlyMemory);
29
30         /// <summary>
31         /// 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
32         /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
33         /// </summary>
34         public static ref T GetReference<T>(Span<T> span) => ref span._pointer.Value;
35
36         /// <summary>
37         /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to the location where the 0th element
38         /// would have been stored. Such a reference may or may not be null. It can be used for pinning but must never be dereferenced.
39         /// </summary>
40         public static ref T GetReference<T>(ReadOnlySpan<T> span) => ref span._pointer.Value;
41
42         /// <summary>
43         /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to fake non-null pointer. Such a reference can be used
44         /// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
45         /// </summary>
46         [MethodImpl(MethodImplOptions.AggressiveInlining)]
47         internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef<T>((void*)1);
48
49         /// <summary>
50         /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference
51         /// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
52         /// </summary>
53         [MethodImpl(MethodImplOptions.AggressiveInlining)]
54         internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref span._pointer.Value : ref Unsafe.AsRef<T>((void*)1);
55
56         /// <summary>
57         /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
58         /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
59         /// </summary>
60         /// <remarks>
61         /// Supported only for platforms that support misaligned memory access.
62         /// </remarks>
63         /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
64         /// <exception cref="System.ArgumentException">
65         /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
66         /// </exception>
67         [MethodImpl(MethodImplOptions.AggressiveInlining)]
68         public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> source)
69             where TFrom : struct
70             where TTo : struct
71         {
72             if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
73                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
74             if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
75                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
76
77             // Use unsigned integers - unsigned division by constant (especially by power of 2)
78             // and checked casts are faster and smaller.
79             uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
80             uint toSize = (uint)Unsafe.SizeOf<TTo>();
81             uint fromLength = (uint)source.Length;
82             int toLength;
83             if (fromSize == toSize)
84             {
85                 // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
86                 // should be optimized to just `length` but the JIT doesn't do that today.
87                 toLength = (int)fromLength;
88             }
89             else if (fromSize == 1)
90             {
91                 // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
92                 // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
93                 // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
94                 // the JIT can't eliminate long multiply by 1.
95                 toLength = (int)(fromLength / toSize);
96             }
97             else
98             {
99                 // Ensure that casts are done in such a way that the JIT is able to "see"
100                 // the uint->ulong casts and the multiply together so that on 32 bit targets
101                 // 32x32to64 multiplication is used.
102                 ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
103                 toLength = checked((int)toLengthUInt64);
104             }
105
106             return new Span<TTo>(
107                 ref Unsafe.As<TFrom, TTo>(ref source._pointer.Value),
108                 toLength);
109         }
110
111         /// <summary>
112         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
113         /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
114         /// </summary>
115         /// <remarks>
116         /// Supported only for platforms that support misaligned memory access.
117         /// </remarks>
118         /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
119         /// <exception cref="System.ArgumentException">
120         /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
121         /// </exception>
122         [MethodImpl(MethodImplOptions.AggressiveInlining)]
123         public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> source)
124             where TFrom : struct
125             where TTo : struct
126         {
127             if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
128                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
129             if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
130                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
131
132             // Use unsigned integers - unsigned division by constant (especially by power of 2)
133             // and checked casts are faster and smaller.
134             uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
135             uint toSize = (uint)Unsafe.SizeOf<TTo>();
136             uint fromLength = (uint)source.Length;
137             int toLength;
138             if (fromSize == toSize)
139             {
140                 // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
141                 // should be optimized to just `length` but the JIT doesn't do that today.
142                 toLength = (int)fromLength;
143             }
144             else if (fromSize == 1)
145             {
146                 // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
147                 // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
148                 // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
149                 // the JIT can't eliminate long multiply by 1.
150                 toLength = (int)(fromLength / toSize);
151             }
152             else
153             {
154                 // Ensure that casts are done in such a way that the JIT is able to "see"
155                 // the uint->ulong casts and the multiply together so that on 32 bit targets
156                 // 32x32to64 multiplication is used.
157                 ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
158                 toLength = checked((int)toLengthUInt64);
159             }
160
161             return new ReadOnlySpan<TTo>(
162                 ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)),
163                 toLength);
164         }
165
166         /// <summary>
167         /// Create a new span over a portion of a regular managed object. This can be useful
168         /// if part of a managed object represents a "fixed array." This is dangerous because the
169         /// <paramref name="length"/> is not checked.
170         /// </summary>
171         /// <param name="reference">A reference to data.</param>
172         /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
173         [MethodImpl(MethodImplOptions.AggressiveInlining)]
174         public static Span<T> CreateSpan<T>(ref T reference, int length) => new Span<T>(ref reference, length);
175
176         /// <summary>
177         /// Create a new read-only span over a portion of a regular managed object. This can be useful
178         /// if part of a managed object represents a "fixed array." This is dangerous because the
179         /// <paramref name="length"/> is not checked.
180         /// </summary>
181         /// <param name="reference">A reference to data.</param>
182         /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
183         [MethodImpl(MethodImplOptions.AggressiveInlining)]
184         public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) => new ReadOnlySpan<T>(ref reference, length);
185     }
186 }