Nullable: System.Runtime.InteropServices.CustomMarshalers/WindowsRuntime (#23930)
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Runtime / InteropServices / WindowsRuntime / IReadOnlyListToIVectorViewAdapter.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 #nullable enable
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using Internal.Runtime.CompilerServices;
9
10 namespace System.Runtime.InteropServices.WindowsRuntime
11 {
12     // This is a set of stub methods implementing the support for the IVectorView`1 interface on managed
13     // objects that implement IReadOnlyList`1. Used by the interop mashaling infrastructure.
14     //
15     // The methods on this class must be written VERY carefully to avoid introducing security holes.
16     // That's because they are invoked with special "this"! The "this" object
17     // for all of these methods are not IReadOnlyListToIVectorViewAdapter objects. Rather, they are of type
18     // IReadOnlyList<T>. No actual IReadOnlyListToIVectorViewAdapter object is ever instantiated. Thus, you will
19     // see a lot of expressions that cast "this" to "IReadOnlyList<T>". 
20     [DebuggerDisplay("Size = {Size}")]
21     internal sealed class IReadOnlyListToIVectorViewAdapter
22     {
23         private IReadOnlyListToIVectorViewAdapter()
24         {
25             Debug.Fail("This class is never instantiated");
26         }
27
28         // T GetAt(uint index)
29         internal T GetAt<T>(uint index)
30         {
31             IReadOnlyList<T> _this = Unsafe.As<IReadOnlyList<T>>(this);
32             EnsureIndexInt32(index, _this.Count);
33
34             try
35             {
36                 return _this[(int)index];
37             }
38             catch (ArgumentOutOfRangeException ex)
39             {
40                 ex.HResult = HResults.E_BOUNDS;
41                 throw;
42             }
43         }
44
45         // uint Size { get }
46         internal uint Size<T>()
47         {
48             IReadOnlyList<T> _this = Unsafe.As<IReadOnlyList<T>>(this);
49             return (uint)_this.Count;
50         }
51
52         // bool IndexOf(T value, out uint index)
53         internal bool IndexOf<T>(T value, out uint index)
54         {
55             IReadOnlyList<T> _this = Unsafe.As<IReadOnlyList<T>>(this);
56
57             int ind = -1;
58             int max = _this.Count;
59             for (int i = 0; i < max; i++)
60             {
61                 if (EqualityComparer<T>.Default.Equals(value, _this[i]))
62                 {
63                     ind = i;
64                     break;
65                 }
66             }
67
68             if (-1 == ind)
69             {
70                 index = 0;
71                 return false;
72             }
73
74             index = (uint)ind;
75             return true;
76         }
77
78         // uint GetMany(uint startIndex, T[] items)
79         internal uint GetMany<T>(uint startIndex, T[] items)
80         {
81             IReadOnlyList<T> _this = Unsafe.As<IReadOnlyList<T>>(this);
82
83             // REX spec says "calling GetMany with startIndex equal to the length of the vector 
84             // (last valid index + 1) and any specified capacity will succeed and return zero actual
85             // elements".
86             if (startIndex == _this.Count)
87                 return 0;
88
89             EnsureIndexInt32(startIndex, _this.Count);
90
91             if (items == null)
92             {
93                 return 0;
94             }
95
96             uint itemCount = Math.Min((uint)items.Length, (uint)_this.Count - startIndex);
97
98             for (uint i = 0; i < itemCount; ++i)
99             {
100                 items[i] = _this[(int)(i + startIndex)];
101             }
102
103             if (typeof(T) == typeof(string))
104             {
105                 string[] stringItems = (items as string[])!;
106
107                 // Fill in the rest of the array with string.Empty to avoid marshaling failure
108                 for (uint i = itemCount; i < items.Length; ++i)
109                     stringItems[i] = string.Empty;
110             }
111
112             return itemCount;
113         }
114
115         #region Helpers
116
117         private static void EnsureIndexInt32(uint index, int listCapacity)
118         {
119             // We use '<=' and not '<' because int.MaxValue == index would imply
120             // that Size > int.MaxValue:
121             if (((uint)int.MaxValue) <= index || index >= (uint)listCapacity)
122             {
123                 Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
124                 e.HResult = HResults.E_BOUNDS;
125                 throw e;
126             }
127         }
128
129         #endregion Helpers
130     }
131 }