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.
6 using System.Collections.Generic;
7 using System.Collections.ObjectModel;
8 using System.Diagnostics;
9 using Internal.Runtime.CompilerServices;
11 namespace System.Runtime.InteropServices.WindowsRuntime
13 // This is a set of stub methods implementing the support for the IVector`1 interface on managed
14 // objects that implement IList`1. Used by the interop mashaling infrastructure.
16 // The methods on this class must be written VERY carefully to avoid introducing security holes.
17 // That's because they are invoked with special "this"! The "this" object
18 // for all of these methods are not ListToVectorAdapter objects. Rather, they are of type
19 // IList<T>. No actual ListToVectorAdapter object is ever instantiated. Thus, you will
20 // see a lot of expressions that cast "this" to "IList<T>".
21 internal sealed class ListToVectorAdapter
23 private ListToVectorAdapter()
25 Debug.Fail("This class is never instantiated");
28 // T GetAt(uint index)
29 internal T GetAt<T>(uint index)
31 IList<T> _this = Unsafe.As<IList<T>>(this);
32 EnsureIndexInt32(index, _this.Count);
36 return _this[(int)index];
38 catch (ArgumentOutOfRangeException ex)
40 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
45 internal uint Size<T>()
47 IList<T> _this = Unsafe.As<IList<T>>(this);
48 return (uint)_this.Count;
51 // IVectorView<T> GetView()
52 internal IReadOnlyList<T> GetView<T>()
54 IList<T> _this = Unsafe.As<IList<T>>(this);
55 Debug.Assert(_this != null);
57 // Note: This list is not really read-only - you could QI for a modifiable
58 // list. We gain some perf by doing this. We believe this is acceptable.
59 if (!(_this is IReadOnlyList<T> roList))
61 roList = new ReadOnlyCollection<T>(_this);
66 // bool IndexOf(T value, out uint index)
67 internal bool IndexOf<T>(T value, out uint index)
69 IList<T> _this = Unsafe.As<IList<T>>(this);
70 int ind = _this.IndexOf(value);
82 // void SetAt(uint index, T value)
83 internal void SetAt<T>(uint index, T value)
85 IList<T> _this = Unsafe.As<IList<T>>(this);
86 EnsureIndexInt32(index, _this.Count);
90 _this[(int)index] = value;
92 catch (ArgumentOutOfRangeException ex)
94 throw WindowsRuntimeMarshal.GetExceptionForHR(HResults.E_BOUNDS, ex, "ArgumentOutOfRange_IndexOutOfRange");
98 // void InsertAt(uint index, T value)
99 internal void InsertAt<T>(uint index, T value)
101 IList<T> _this = Unsafe.As<IList<T>>(this);
103 // Inserting at an index one past the end of the list is equivalent to appending
104 // so we need to ensure that we're within (0, count + 1).
105 EnsureIndexInt32(index, _this.Count + 1);
109 _this.Insert((int)index, value);
111 catch (ArgumentOutOfRangeException ex)
113 // Change error code to match what WinRT expects
114 ex.HResult = HResults.E_BOUNDS;
119 // void RemoveAt(uint index)
120 internal void RemoveAt<T>(uint index)
122 IList<T> _this = Unsafe.As<IList<T>>(this);
123 EnsureIndexInt32(index, _this.Count);
127 _this.RemoveAt((int)index);
129 catch (ArgumentOutOfRangeException ex)
131 // Change error code to match what WinRT expects
132 ex.HResult = HResults.E_BOUNDS;
137 // void Append(T value)
138 internal void Append<T>(T value)
140 IList<T> _this = Unsafe.As<IList<T>>(this);
144 // void RemoveAtEnd()
145 internal void RemoveAtEnd<T>()
147 IList<T> _this = Unsafe.As<IList<T>>(this);
148 if (_this.Count == 0)
150 Exception e = new InvalidOperationException(SR.InvalidOperation_CannotRemoveLastFromEmptyCollection);
151 e.HResult = HResults.E_BOUNDS;
155 uint size = (uint)_this.Count;
156 RemoveAt<T>(size - 1);
160 internal void Clear<T>()
162 IList<T> _this = Unsafe.As<IList<T>>(this);
166 // uint GetMany(uint startIndex, T[] items)
167 internal uint GetMany<T>(uint startIndex, T[] items)
169 IList<T> _this = Unsafe.As<IList<T>>(this);
170 return GetManyHelper<T>(_this, startIndex, items);
173 // void ReplaceAll(T[] items)
174 internal void ReplaceAll<T>(T[] items)
176 IList<T> _this = Unsafe.As<IList<T>>(this);
181 foreach (T item in items)
190 private static void EnsureIndexInt32(uint index, int listCapacity)
192 // We use '<=' and not '<' becasue int.MaxValue == index would imply
193 // that Size > int.MaxValue:
194 if (((uint)int.MaxValue) <= index || index >= (uint)listCapacity)
196 Exception e = new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexLargerThanMaxValue);
197 e.HResult = HResults.E_BOUNDS;
202 private static uint GetManyHelper<T>(IList<T> sourceList, uint startIndex, T[] items)
204 // Calling GetMany with a start index equal to the size of the list should always
205 // return 0 elements, regardless of the input item size
206 if (startIndex == sourceList.Count)
211 EnsureIndexInt32(startIndex, sourceList.Count);
218 uint itemCount = Math.Min((uint)items.Length, (uint)sourceList.Count - startIndex);
219 for (uint i = 0; i < itemCount; ++i)
221 items[i] = sourceList[(int)(i + startIndex)];
224 if (typeof(T) == typeof(string))
226 string[] stringItems = (items as string[])!;
228 // Fill in rest of the array with string.Empty to avoid marshaling failure
229 for (uint i = itemCount; i < items.Length; ++i)
230 stringItems[i] = string.Empty;