summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/IReadOnlyListToIVectorViewAdapter.cs
blob: 431d16256e21d3ef7427d91eea5c5f843e4ddf77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 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;
using System.Security;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace System.Runtime.InteropServices.WindowsRuntime
{
    // This is a set of stub methods implementing the support for the IVectorView`1 interface on managed
    // objects that implement IReadOnlyList`1. Used by the interop mashaling infrastructure.
    //
    // The methods on this class must be written VERY carefully to avoid introducing security holes.
    // That's because they are invoked with special "this"! The "this" object
    // for all of these methods are not IReadOnlyListToIVectorViewAdapter objects. Rather, they are of type
    // IReadOnlyList<T>. No actual IReadOnlyListToIVectorViewAdapter object is ever instantiated. Thus, you will
    // see a lot of expressions that cast "this" to "IReadOnlyList<T>". 
    [DebuggerDisplay("Size = {Size}")]
    internal sealed class IReadOnlyListToIVectorViewAdapter
    {
        private IReadOnlyListToIVectorViewAdapter()
        {
            Debug.Assert(false, "This class is never instantiated");
        }

        // T GetAt(uint index)
        internal T GetAt<T>(uint index)
        {
            IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this);
            EnsureIndexInt32(index, _this.Count);        

            try
            {
                return _this[(int) index];
            }
            catch (ArgumentOutOfRangeException ex)
            {
                ex.SetErrorCode(__HResults.E_BOUNDS);
                throw;
            }
        }

        // uint Size { get }
        internal uint Size<T>()
        {
            IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this);
            return (uint)_this.Count;
        }

        // bool IndexOf(T value, out uint index)
        internal bool IndexOf<T>(T value, out uint index)
        {
            IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this);

            int ind = -1;
            int max = _this.Count;
            for (int i = 0; i < max; i++)
            {
                if (EqualityComparer<T>.Default.Equals(value, _this[i]))
                {
                    ind = i;
                    break;
                }
            }

            if (-1 == ind)
            {
                index = 0;
                return false;
            }

            index = (uint)ind;
            return true;
        }

        // uint GetMany(uint startIndex, T[] items)
        internal uint GetMany<T>(uint startIndex, T[] items)
        {
            IReadOnlyList<T> _this = JitHelpers.UnsafeCast<IReadOnlyList<T>>(this);

            // REX spec says "calling GetMany with startIndex equal to the length of the vector 
            // (last valid index + 1) and any specified capacity will succeed and return zero actual
            // elements".
            if (startIndex == _this.Count)
                return 0;

            EnsureIndexInt32(startIndex, _this.Count);

            if (items == null)
            {
                return 0;
            }

            uint itemCount = Math.Min((uint)items.Length, (uint)_this.Count - startIndex);

            for (uint i = 0; i < itemCount; ++i)
            {
                items[i] = _this[(int)(i + startIndex)];
            }

            if (typeof(T) == typeof(string))
            {
                string[] stringItems = items as string[];

                // Fill in the rest of the array with String.Empty to avoid marshaling failure
                for (uint i = itemCount; i < items.Length; ++i)
                    stringItems[i] = String.Empty;
            }

            return itemCount;
        }

        #region Helpers

        private static void EnsureIndexInt32(uint index, int listCapacity)
        {
            // We use '<=' and not '<' because Int32.MaxValue == index would imply
            // that Size > Int32.MaxValue:
            if (((uint)Int32.MaxValue) <= index || index >= (uint)listCapacity)
            {
                Exception e = new ArgumentOutOfRangeException(nameof(index), Environment.GetResourceString("ArgumentOutOfRange_IndexLargerThanMaxValue"));
                e.SetErrorCode(__HResults.E_BOUNDS);
                throw e;
            }
        }

        #endregion Helpers
    }
}