Skip to content

Commit

Permalink
Merge pull request #3341 from Sergio0694/optimization/struct-enumerators
Browse files Browse the repository at this point in the history
Optimized struct enumerators
  • Loading branch information
michael-hawker authored Jun 12, 2020
2 parents 5faa6aa + ffe05a5 commit 4aca259
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 270 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,94 +15,72 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct ReadOnlySpanEnumerable<T>
public ref struct ReadOnlySpanEnumerable<T>
{
/// <summary>
/// The source <see cref="ReadOnlySpan{T}"/> instance
/// The source <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
private readonly ReadOnlySpan<T> span;

/// <summary>
/// The current index within <see cref="span"/>.
/// </summary>
private int index;

/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlySpanEnumerable{T}"/> struct.
/// </summary>
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> to enumerate.</param>
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpanEnumerable(ReadOnlySpan<T> span)
{
this.span = span;
this.index = -1;
}

/// <summary>
/// Implements the duck-typed <see cref="IEnumerable{T}.GetEnumerator"/> method.
/// </summary>
/// <returns>An <see cref="Enumerator"/> instance targeting the current <see cref="ReadOnlySpan{T}"/> value.</returns>
/// <returns>An <see cref="ReadOnlySpanEnumerable{T}"/> instance targeting the current <see cref="ReadOnlySpan{T}"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(this.span);
public ReadOnlySpanEnumerable<T> GetEnumerator() => this;

/// <summary>
/// An enumerator for a source <see cref="ReadOnlySpan{T}"/> instance.
/// Implements the duck-typed <see cref="System.Collections.IEnumerator.MoveNext"/> method.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public ref struct Enumerator
/// <returns><see langword="true"/> whether a new element is available, <see langword="false"/> otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
/// <summary>
/// The source <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
private readonly ReadOnlySpan<T> span;

/// <summary>
/// The current index within <see cref="span"/>.
/// </summary>
private int index;

/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator(ReadOnlySpan<T> span)
{
this.span = span;
this.index = -1;
}
int newIndex = this.index + 1;

/// <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()
if (newIndex < this.span.Length)
{
int newIndex = this.index + 1;
this.index = newIndex;

if (newIndex < this.span.Length)
{
this.index = newIndex;

return true;
}

return false;
return true;
}

/// <summary>
/// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
/// </summary>
public Item Current
return false;
}

/// <summary>
/// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
/// </summary>
public Item Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
#if SPAN_RUNTIME_SUPPORT
ref T r0 = ref MemoryMarshal.GetReference(this.span);
ref T ri = ref Unsafe.Add(ref r0, this.index);
ref T r0 = ref MemoryMarshal.GetReference(this.span);
ref T ri = ref Unsafe.Add(ref r0, this.index);

// See comment in SpanEnumerable<T> about this
return new Item(ref ri, this.index);
// See comment in SpanEnumerable<T> about this
return new Item(ref ri, this.index);
#else
return new Item(this.span, this.index);
return new Item(this.span, this.index);
#endif
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,119 +14,90 @@ namespace Microsoft.Toolkit.HighPerformance.Enumerables
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly ref struct ReadOnlySpanTokenizer<T>
public ref struct ReadOnlySpanTokenizer<T>
where T : IEquatable<T>
{
/// <summary>
/// The source <see cref="ReadOnlySpan{T}"/> instance
/// The source <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
private readonly ReadOnlySpan<T> span;

/// <summary>
/// The separator <typeparamref name="T"/> item to use.
/// The separator item to use.
/// </summary>
private readonly T separator;

/// <summary>
/// The current initial offset.
/// </summary>
private int start;

/// <summary>
/// The current final offset.
/// </summary>
private int end;

/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlySpanTokenizer{T}"/> struct.
/// </summary>
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> to tokenize.</param>
/// <param name="separator">The separator <typeparamref name="T"/> item to use.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> instance.</param>
/// <param name="separator">The separator item to use.</param>
public ReadOnlySpanTokenizer(ReadOnlySpan<T> span, T separator)
{
this.span = span;
this.separator = separator;
this.start = 0;
this.end = -1;
}

/// <summary>
/// Implements the duck-typed <see cref="IEnumerable{T}.GetEnumerator"/> method.
/// </summary>
/// <returns>An <see cref="Enumerator"/> instance targeting the current <see cref="ReadOnlySpan{T}"/> value.</returns>
/// <returns>An <see cref="ReadOnlySpanTokenizer{T}"/> instance targeting the current <see cref="ReadOnlySpan{T}"/> value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Enumerator GetEnumerator() => new Enumerator(this.span, this.separator);
public ReadOnlySpanTokenizer<T> GetEnumerator() => this;

/// <summary>
/// An enumerator for a source <see cref="ReadOnlySpan{T}"/> instance.
/// Implements the duck-typed <see cref="System.Collections.IEnumerator.MoveNext"/> method.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public ref struct Enumerator
/// <returns><see langword="true"/> whether a new element is available, <see langword="false"/> otherwise</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
/// <summary>
/// The source <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
private readonly ReadOnlySpan<T> span;

/// <summary>
/// The separator item to use.
/// </summary>
private readonly T separator;

/// <summary>
/// The current initial offset.
/// </summary>
private int start;

/// <summary>
/// The current final offset.
/// </summary>
private int end;

/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
/// <param name="span">The source <see cref="ReadOnlySpan{T}"/> instance.</param>
/// <param name="separator">The separator item to use.</param>
public Enumerator(ReadOnlySpan<T> span, T separator)
{
this.span = span;
this.separator = separator;
this.start = 0;
this.end = -1;
}
int
newEnd = this.end + 1,
length = this.span.Length;

/// <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()
// Additional check if the separator is not the last character
if (newEnd <= length)
{
int
newEnd = this.end + 1,
length = this.span.Length;

// Additional check if the separator is not the last character
if (newEnd <= length)
{
this.start = newEnd;
this.start = newEnd;

int index = this.span.Slice(newEnd).IndexOf(this.separator);
int index = this.span.Slice(newEnd).IndexOf(this.separator);

// Extract the current subsequence
if (index >= 0)
{
this.end = newEnd + index;

return true;
}

this.end = length;
// Extract the current subsequence
if (index >= 0)
{
this.end = newEnd + index;

return true;
}

return false;
}
this.end = length;

/// <summary>
/// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
/// </summary>
public ReadOnlySpan<T> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.span.Slice(this.start, this.end - this.start);
return true;
}

return false;
}

/// <summary>
/// Gets the duck-typed <see cref="IEnumerator{T}.Current"/> property.
/// </summary>
public ReadOnlySpan<T> Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.span.Slice(this.start, this.end - this.start);
}
}
}
Loading

0 comments on commit 4aca259

Please sign in to comment.