-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathArrayViewExtensions.cs
More file actions
167 lines (150 loc) · 7.1 KB
/
ArrayViewExtensions.cs
File metadata and controls
167 lines (150 loc) · 7.1 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
namespace Ramstack.Collections;
/// <summary>
/// Provides extension methods for the <see cref="ArrayView{T}"/> structure.
/// </summary>
public static class ArrayViewExtensions
{
/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> over an array.
/// </summary>
/// <typeparam name="T">The type of elements in the array.</typeparam>
/// <param name="value">The target array.</param>
/// <returns>
/// An <see cref="ArrayView{T}"/> representing the specified array.
/// </returns>
public static ArrayView<T> AsView<T>(this T[]? value) =>
new(value ?? []);
/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> over a portion of the specified array,
/// starting at a specified position to the end of the array.
/// </summary>
/// <typeparam name="T">The type of elements in the array.</typeparam>
/// <param name="value">The array to create a view over.</param>
/// <param name="index">The zero-based starting position of the view in the array.</param>
/// <returns>
/// An <see cref="ArrayView{T}"/> representing the specified portion of the array.
/// </returns>
public static ArrayView<T> AsView<T>(this T[]? value, int index) =>
new(value ?? [], index);
/// <summary>
/// Creates a new <see cref="ArrayView{T}"/> over a portion of the specified array
/// starting at a specified position for a specified number of elements.
/// </summary>
/// <typeparam name="T">The type of elements in the array.</typeparam>
/// <param name="value">The array to create a view over. Can be null.</param>
/// <param name="index">The zero-based starting position of the view in the array.</param>
/// <param name="count">The number of elements to include in the view.</param>
/// <returns>
/// An <see cref="ArrayView{T}"/> representing the specified portion of the array.
/// </returns>
public static ArrayView<T> AsView<T>(this T[]? value, int index, int count) =>
new(value ?? [], index, count);
/// <summary>
/// Finds the index of the first occurrence of a specified value in this instance.
/// </summary>
/// <param name="view">The <see cref="ArrayView{T}"/> instance.</param>
/// <param name="value">The value to locate in the array view.</param>
/// <returns>
/// The zero-based index of the first occurrence of the value in this instance; otherwise, <c>-1</c>.
/// </returns>
public static int IndexOf<T>(this ArrayView<T> view, T value) where T : IEquatable<T> =>
view.AsSpan().IndexOf(value);
/// <summary>
/// Finds the index of the first occurrence of a specified value in the array view.
/// </summary>
/// <param name="view">The <see cref="ArrayView{T}"/> instance.</param>
/// <param name="value">The value to locate in the array view.</param>
/// <param name="comparer">The equality comparer to use for the search.
/// If <see langword="null"/>, the default equality comparer is used.</param>
/// <returns>
/// The zero-based index of the first occurrence of the value in the array view; otherwise, <c>-1</c>.
/// </returns>
public static int IndexOf<T>(this ArrayView<T> view, T value, IEqualityComparer<T>? comparer)
{
return Impl(view.AsSpan(), value, comparer);
static int Impl(ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer)
{
if (typeof(T).IsValueType && (comparer is null || ReferenceEquals(comparer, EqualityComparer<T>.Default)))
{
for (var i = 0; i < span.Length; i++)
if (EqualityComparer<T>.Default.Equals(span[i], value))
return i;
}
else
{
comparer ??= EqualityComparer<T>.Default;
for (var i = 0; i < span.Length; i++)
if (comparer.Equals(span[i], value))
return i;
}
return -1;
}
}
/// <summary>
/// Finds the index of the last occurrence of a specified value in the array view.
/// </summary>
/// <param name="view">The <see cref="ArrayView{T}"/> instance.</param>
/// <param name="value">The value to locate in the array view.</param>
/// <returns>
/// The zero-based index of the last occurrence of the value in the array view; otherwise, <c>-1</c>.
/// </returns>
public static int LastIndexOf<T>(this ArrayView<T> view, T value) where T : IEquatable<T> =>
view.AsSpan().LastIndexOf(value);
/// <summary>
/// Finds the index of the last occurrence of a specified value in the array view.
/// </summary>
/// <param name="view">The <see cref="ArrayView{T}"/> instance.</param>
/// <param name="value">The value to locate in the array view.</param>
/// <param name="comparer">The equality comparer to use for the search.
/// If <see langword="null"/>, the default equality comparer is used.</param>
/// <returns>
/// The zero-based index of the last occurrence of the value in the array view; otherwise, <c>-1</c>.
/// </returns>
public static int LastIndexOf<T>(this ArrayView<T> view, T value, IEqualityComparer<T>? comparer)
{
return Impl(view.AsSpan(), value, comparer);
static int Impl(ReadOnlySpan<T> span, T value, IEqualityComparer<T>? comparer)
{
if (typeof(T).IsValueType && (comparer is null || ReferenceEquals(comparer, EqualityComparer<T>.Default)))
{
for (var i = span.Length - 1; i >= 0; i--)
if (EqualityComparer<T>.Default.Equals(span[i], value))
return i;
}
else
{
comparer ??= EqualityComparer<T>.Default;
for (var i = span.Length - 1; i >= 0; i--)
if (comparer.Equals(span[i], value))
return i;
}
return -1;
}
}
#if NET9_0_OR_GREATER
/// <summary>
/// Returns an <see cref="ArrayView{T}"/> over the specified list.
/// Items should not be added or removed from the <see cref="List{T}"/>
/// while the <see cref="ArrayView{T}"/> is in use.
/// </summary>
/// <typeparam name="T">The type of the elements in the list.</typeparam>
/// <param name="list">The list to get the data view over.</param>
/// <returns>
/// A <see cref="ArrayView{T}"/> instance over the specified list.
/// </returns>
public static ArrayView<T> AsView<T>(this List<T>? list)
{
if (list is not null)
{
var array = ListAccessor<T>.GetArray(list);
_ = array.Length;
//
// SCG.List<T> maintains internal invariants, so we can safely use the unchecked constructor
// to bypass redundant bounds checks for better performance.
//
return new ArrayView<T>(array, 0, list.Count, unused: 0);
}
return ArrayView<T>.Empty;
}
#endif
}