-
Notifications
You must be signed in to change notification settings - Fork 392
Expand file tree
/
Copy pathSpanEnumerable{T}.cs
More file actions
180 lines (164 loc) · 5.91 KB
/
Copy pathSpanEnumerable{T}.cs
File metadata and controls
180 lines (164 loc) · 5.91 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
168
169
170
171
172
173
174
175
176
177
178
179
180
// 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.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace CommunityToolkit.HighPerformance.Enumerables;
/// <summary>
/// A <see langword="ref"/> <see langword="struct"/> that enumerates the items in a given <see cref="Span{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public ref struct SpanEnumerable<T>
{
/// <summary>
/// The source <see cref="Span{T}"/> instance.
/// </summary>
private readonly Span<T> span;
/// <summary>
/// The current index within <see cref="span"/>.
/// </summary>
private int index;
/// <summary>
/// Initializes a new instance of the <see cref="SpanEnumerable{T}"/> struct.
/// </summary>
/// <param name="span">The source <see cref="Span{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanEnumerable(Span<T> span)
{
this.span = span;
this.index = -1;
}
/// <summary>
/// Implements the duck-typed <see cref="IEnumerable{T}.GetEnumerator"/> method.
/// </summary>
/// <returns>An <see cref="SpanEnumerable{T}"/> instance targeting the current <see cref="Span{T}"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly SpanEnumerable<T> GetEnumerator() => this;
/// <summary>
/// Implements the duck-typed <see cref="System.Collections.IEnumerator.MoveNext"/> method.
/// </summary>
/// <returns><see langword="true"/> whether a new element is available, <see langword="false"/> otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
return ++this.index < this.span.Length;
}
/// <summary>
/// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
/// </summary>
public readonly Item Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NETSTANDARD2_1_OR_GREATER
ref T r0 = ref MemoryMarshal.GetReference(this.span);
ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index);
// On .NET Standard 2.1 and .NET Core (or on any target that offers runtime
// support for the Span<T> types), we can save 4 bytes by piggybacking the
// current index in the length of the wrapped span. We're going to use the
// first item as the target reference, and the length as a host for the
// current original offset. This is not possible on eg. .NET Standard 2.0,
// as we lack the API to create Span<T>-s from arbitrary references.
return new(ref ri, this.index);
#else
return new(this.span, this.index);
#endif
}
}
/// <summary>
/// An item from a source <see cref="Span{T}"/> instance.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct Item
{
#if NET8_0_OR_GREATER
/// <summary>
/// The <typeparamref name="T"/> reference for the <see cref="Item"/> instance.
/// </summary>
private readonly ref T reference;
/// <summary>
/// The index of the current <see cref="Item"/> instance.
/// </summary>
private readonly int index;
#else
/// <summary>
/// The source <see cref="Span{T}"/> instance.
/// </summary>
private readonly Span<T> span;
#endif
#if NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="Item"/> struct.
/// </summary>
/// <param name="value">A reference to the target value.</param>
/// <param name="index">The index of the target value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Item(ref T value, int index)
{
#if NET8_0_OR_GREATER
this.reference = ref value;
this.index = index;
#else
this.span = MemoryMarshal.CreateSpan(ref value, index);
#endif
}
#else
/// <summary>
/// The current index within <see cref="span"/>.
/// </summary>
private readonly int index;
/// <summary>
/// Initializes a new instance of the <see cref="Item"/> struct.
/// </summary>
/// <param name="span">The source <see cref="Span{T}"/> instance.</param>
/// <param name="index">The current index within <paramref name="span"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Item(Span<T> span, int index)
{
this.span = span;
this.index = index;
}
#endif
/// <summary>
/// Gets the reference to the current value.
/// </summary>
public ref T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NET8_0_OR_GREATER
return ref this.reference;
#elif NETSTANDARD2_1_OR_GREATER
return ref MemoryMarshal.GetReference(this.span);
#else
ref T r0 = ref MemoryMarshal.GetReference(this.span);
ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index);
return ref ri;
#endif
}
}
/// <summary>
/// Gets the current index.
/// </summary>
public int Index
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if NET8_0_OR_GREATER
return this.index;
#elif NETSTANDARD2_1_OR_GREATER
return this.span.Length;
#else
return this.index;
#endif
}
}
}
}