/
CollectionsMarshal.cs
106 lines (95 loc) · 5.35 KB
/
CollectionsMarshal.cs
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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace System.Runtime.InteropServices
{
/// <summary>
/// An unsafe class that provides a set of methods to access the underlying data representations of collections.
/// </summary>
public static class CollectionsMarshal
{
/// <summary>
/// Get a <see cref="Span{T}"/> view over a <see cref="List{T}"/>'s data.
/// Items should not be added or removed from the <see cref="List{T}"/> while the <see cref="Span{T}"/> is in use.
/// </summary>
/// <param name="list">The list to get the data view over.</param>
/// <typeparam name="T">The type of the elements in the list.</typeparam>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpan<T>(List<T>? list)
{
Span<T> span = default;
if (list is not null)
{
int size = list._size;
T[] items = list._items;
Debug.Assert(items is not null, "Implementation depends on List<T> always having an array.");
if ((uint)size > (uint)items.Length)
{
// List<T> was erroneously mutated concurrently with this call, leading to a count larger than its array.
ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
}
Debug.Assert(typeof(T[]) == list._items.GetType(), "Implementation depends on List<T> always using a T[] and not U[] where U : T.");
span = new Span<T>(ref MemoryMarshal.GetArrayDataReference(items), size);
}
return span;
}
/// <summary>
/// Gets either a ref to a <typeparamref name="TValue"/> in the <see cref="Dictionary{TKey, TValue}"/> or a ref null if it does not exist in the <paramref name="dictionary"/>.
/// </summary>
/// <param name="dictionary">The dictionary to get the ref to <typeparamref name="TValue"/> from.</param>
/// <param name="key">The key used for lookup.</param>
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
/// <remarks>
/// Items should not be added or removed from the <see cref="Dictionary{TKey, TValue}"/> while the ref <typeparamref name="TValue"/> is in use.
/// The ref null can be detected using System.Runtime.CompilerServices.Unsafe.IsNullRef
/// </remarks>
public static ref TValue GetValueRefOrNullRef<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key) where TKey : notnull
=> ref dictionary.FindValue(key);
/// <summary>
/// Gets a ref to a <typeparamref name="TValue"/> in the <see cref="Dictionary{TKey, TValue}"/>, adding a new entry with a default value if it does not exist in the <paramref name="dictionary"/>.
/// </summary>
/// <param name="dictionary">The dictionary to get the ref to <typeparamref name="TValue"/> from.</param>
/// <param name="key">The key used for lookup.</param>
/// <param name="exists">Whether or not a new entry for the given key was added to the dictionary.</param>
/// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
/// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
/// <remarks>Items should not be added to or removed from the <see cref="Dictionary{TKey, TValue}"/> while the ref <typeparamref name="TValue"/> is in use.</remarks>
public static ref TValue? GetValueRefOrAddDefault<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, out bool exists) where TKey : notnull
=> ref Dictionary<TKey, TValue>.CollectionsMarshalHelper.GetValueRefOrAddDefault(dictionary, key, out exists);
/// <summary>
/// Sets the count of the <see cref="List{T}"/> to the specified value.
/// </summary>
/// <param name="list">The list to set the count of.</param>
/// <param name="count">The value to set the list's count to.</param>
/// <typeparam name="T">The type of the elements in the list.</typeparam>
/// <exception cref="NullReferenceException">
/// <paramref name="list"/> is <see langword="null"/>.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="count"/> is negative.
/// </exception>
/// <remarks>
/// When increasing the count, uninitialized data is being exposed.
/// </remarks>
public static void SetCount<T>(List<T> list, int count)
{
if (count < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(nameof(count));
}
list._version++;
if (count > list.Capacity)
{
list.Grow(count);
}
else if (count < list._size && RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
Array.Clear(list._items, count, list._size - count);
}
list._size = count;
}
}
}