Skip to content

Commit

Permalink
Improve incremental source generator
Browse files Browse the repository at this point in the history
  • Loading branch information
GerardSmit committed Jan 3, 2024
1 parent a8a6bd2 commit 1309866
Show file tree
Hide file tree
Showing 54 changed files with 1,978 additions and 680 deletions.
4 changes: 2 additions & 2 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>

<PropertyGroup>
<Version>1.0.0-alpha.8</Version>
<PackageVersion>1.0.0-alpha.8</PackageVersion>
<Version>1.0.0-alpha.9</Version>
<PackageVersion>1.0.0-alpha.9</PackageVersion>
<Authors>Zapto</Authors>
<RepositoryUrl>https://github.com/zapto-dev/Mediator</RepositoryUrl>
<Copyright>Copyright © 2023 Zapto</Copyright>
Expand Down
234 changes: 234 additions & 0 deletions src/Mediator.SourceGenerator/Extensions/EquatableArray.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Zapto.Mediator.Generator;

/// <summary>
/// An immutable, equatable array. This is equivalent to <see cref="ImmutableArray"/> but with value equality support.
/// </summary>
/// <typeparam name="T">The type of values in the array.</typeparam>
internal readonly struct EquatableArray<T> : IEquatable<EquatableArray<T>>, IEnumerable<T>
where T : IEquatable<T>
{
public static readonly EquatableArray<T> Empty = new(ImmutableArray<T>.Empty);

/// <summary>
/// The underlying <typeparamref name="T"/> array.
/// </summary>
private readonly T[]? _array;

/// <summary>
/// Initializes a new instance of the <see cref="EquatableArray{T}"/> struct.
/// </summary>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> to wrap.</param>
public EquatableArray(ImmutableArray<T> array)
{
_array = Unsafe.As<ImmutableArray<T>, T[]>(ref array);
}

/// <summary>
/// Initializes a new instance of the <see cref="EquatableArray{T}"/> struct.
/// </summary>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> to wrap.</param>
public EquatableArray(T[]? array)
{
_array = array;
}

/// <summary>
/// Gets the number of items in the array.
/// </summary>
public int Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => AsImmutableArray().Length;
}

/// <summary>
/// Gets a value indicating whether the current array is empty.
/// </summary>
public bool IsEmpty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => AsImmutableArray().IsEmpty;
}

/// <summary>
/// Gets a reference to an item at a specified position within the array.
/// </summary>
/// <param name="index">The index of the item to retrieve a reference to.</param>
/// <returns>A reference to an item at a specified position within the array.</returns>
public ref readonly T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref AsImmutableArray().ItemRef(index);
}

/// <summary>
/// Implicitly converts an <see cref="ImmutableArray{T}"/> to <see cref="EquatableArray{T}"/>.
/// </summary>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static implicit operator EquatableArray<T>(ImmutableArray<T> array)
{
return FromImmutableArray(array);
}

/// <summary>
/// Implicitly converts an <see cref="EquatableArray{T}"/> to <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <returns>An <see cref="ImmutableArray{T}"/> instance from a given <see cref="EquatableArray{T}"/>.</returns>
public static implicit operator ImmutableArray<T>(EquatableArray<T> array)
{
return array.AsImmutableArray();
}

/// <summary>
/// Checks whether two <see cref="EquatableArray{T}"/> values are the same.
/// </summary>
/// <param name="left">The first <see cref="EquatableArray{T}"/> value.</param>
/// <param name="right">The second <see cref="EquatableArray{T}"/> value.</param>
/// <returns>Whether <paramref name="left"/> and <paramref name="right"/> are equal.</returns>
public static bool operator ==(EquatableArray<T> left, EquatableArray<T> right)
{
return left.Equals(right);
}

/// <summary>
/// Checks whether two <see cref="EquatableArray{T}"/> values are not the same.
/// </summary>
/// <param name="left">The first <see cref="EquatableArray{T}"/> value.</param>
/// <param name="right">The second <see cref="EquatableArray{T}"/> value.</param>
/// <returns>Whether <paramref name="left"/> and <paramref name="right"/> are not equal.</returns>
public static bool operator !=(EquatableArray<T> left, EquatableArray<T> right)
{
return !left.Equals(right);
}

/// <summary>
/// Creates an <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> instance.</param>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static EquatableArray<T> FromImmutableArray(ImmutableArray<T> array)
{
return new(array);
}

/// <inheritdoc/>
public bool Equals(EquatableArray<T> array)
{
return AsSpan().SequenceEqual(array.AsSpan());
}

/// <inheritdoc/>
public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is EquatableArray<T> array && Equals(this, array);
}

/// <inheritdoc/>
public override int GetHashCode()
{
if (this._array is not T[] array)
{
return 0;
}

HashCode hashCode = default;

foreach (T item in array)
{
hashCode.Add(item);
}

return hashCode.ToHashCode();
}

/// <summary>
/// Gets an <see cref="ImmutableArray{T}"/> instance from the current <see cref="EquatableArray{T}"/>.
/// </summary>
/// <returns>The <see cref="ImmutableArray{T}"/> from the current <see cref="EquatableArray{T}"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImmutableArray<T> AsImmutableArray()
{
return _array is null ? ImmutableArray<T>.Empty : Unsafe.As<T[], ImmutableArray<T>>(ref Unsafe.AsRef(in _array));
}

public EquatableArray<TResult> Select<TResult>(EquatableArray<T> array, Func<T, TResult> selector)
where TResult : IEquatable<TResult>
{
var builder = new EquatableArrayBuilder<TResult>(array.Length);

foreach (var item in array)
{
builder.Add(selector(item));
}

return builder.ToEquatableArray();
}

/// <summary>
/// Returns a <see cref="ReadOnlySpan{T}"/> wrapping the current items.
/// </summary>
/// <returns>A <see cref="ReadOnlySpan{T}"/> wrapping the current items.</returns>
public ReadOnlySpan<T> AsSpan()
{
return AsImmutableArray().AsSpan();
}

/// <summary>
/// Copies the contents of this <see cref="EquatableArray{T}"/> instance to a mutable array.
/// </summary>
/// <returns>The newly instantiated array.</returns>
public T[] ToArray()
{
return AsImmutableArray().ToArray();
}

/// <summary>
/// Gets an <see cref="ImmutableArray{T}.Enumerator"/> value to traverse items in the current array.
/// </summary>
/// <returns>An <see cref="ImmutableArray{T}.Enumerator"/> value to traverse items in the current array.</returns>
public ImmutableArray<T>.Enumerator GetEnumerator()
{
return AsImmutableArray().GetEnumerator();
}

/// <inheritdoc/>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)AsImmutableArray()).GetEnumerator();
}

/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)AsImmutableArray()).GetEnumerator();
}
}

/// <summary>
/// Extensions for <see cref="EquatableArray{T}"/>.
/// </summary>
internal static class EquatableArray
{
/// <summary>
/// Creates an <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.
/// </summary>
/// <typeparam name="T">The type of items in the input array.</typeparam>
/// <param name="array">The input <see cref="ImmutableArray{T}"/> instance.</param>
/// <returns>An <see cref="EquatableArray{T}"/> instance from a given <see cref="ImmutableArray{T}"/>.</returns>
public static EquatableArray<T> AsEquatableArray<T>(this ImmutableArray<T> array)
where T : IEquatable<T>
{
return new(array);
}
}
62 changes: 62 additions & 0 deletions src/Mediator.SourceGenerator/Extensions/EquatableArrayBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Buffers;
using System.Collections.Immutable;

namespace Zapto.Mediator.Generator;

internal ref struct EquatableArrayBuilder<T> where T : IEquatable<T>
{
private IMemoryOwner<T>? _memory;
private int _index;

public EquatableArrayBuilder()
{
}

public EquatableArrayBuilder(int capacity)
{
_memory = MemoryPool<T>.Shared.Rent(capacity);
}

public int Count => _index;

public void EnsureCapacity(int capacity)
{
if (_memory is null)
{
_memory = MemoryPool<T>.Shared.Rent(capacity);
}
else if (_memory.Memory.Length < capacity)
{
var oldMemory = _memory;

_memory = MemoryPool<T>.Shared.Rent(capacity);
oldMemory.Memory.CopyTo(_memory.Memory);
oldMemory.Memory.Span.Clear();

oldMemory.Dispose();
}
}

public void Add(T item)
{
if (item is null)
{
throw new ArgumentNullException(nameof(item));
}

EnsureCapacity(_index + 1);
_memory!.Memory.Span[_index++] = item;
}

public EquatableArray<T> ToEquatableArray()
{
return _memory is null ? default : new EquatableArray<T>(_memory.Memory.Slice(0, Count).ToArray());
}

public void Dispose()
{
_memory?.Memory.Span.Clear();
_memory?.Dispose();
}
}
Loading

0 comments on commit 1309866

Please sign in to comment.