diff --git a/README.md b/README.md index 5fcdcb3..d8cefac 100644 --- a/README.md +++ b/README.md @@ -56,5 +56,6 @@ It also includes the following optional runtime-supported polyfills: **PolySharp**'s generation can be configured through some MSBuild properties to set in consuming projects. The following properties are available: -- "PolySharpUsePublicAccessibilityForGeneratedTypes": changes the accessibility of generated types from `internal` to `public`. -- "PolySharpIncludeRuntimeSupportedAttributes": enables polyfills for (dummy) runtime-supported attributes too. \ No newline at end of file +- "PolySharpUsePublicAccessibilityForGeneratedTypes": makes all generated types public. +- "PolySharpIncludeRuntimeSupportedAttributes": enables polyfills for (dummy) runtime-supported attributes too. +- "PolySharpExcludeGeneratedTypes": excludes specific types from generation (';' or ',' separated type names). \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.StackTraceHiddenAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.StackTraceHiddenAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Diagnostics.StackTraceHiddenAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Diagnostics.StackTraceHiddenAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.ObsoletedOSPlatformAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.ObsoletedOSPlatformAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.ObsoletedOSPlatformAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.ObsoletedOSPlatformAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.SupportedOSPlatformAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.SupportedOSPlatformAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.SupportedOSPlatformAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.SupportedOSPlatformAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.SupportedOSPlatformGuardAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.SupportedOSPlatformGuardAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.SupportedOSPlatformGuardAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.SupportedOSPlatformGuardAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.TargetPlatformAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.TargetPlatformAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.TargetPlatformAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.TargetPlatformAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.UnsupportedOSPlatformAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.UnsupportedOSPlatformAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.UnsupportedOSPlatformAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.UnsupportedOSPlatformAttribute.cs diff --git a/src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute.cs b/src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute.cs similarity index 100% rename from src/PolySharp.SourceGenerators/EmbeddedResources/Compatibility/System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute.cs rename to src/PolySharp.SourceGenerators/EmbeddedResources/RuntimeSupported/System.Runtime.Versioning.UnsupportedOSPlatformGuardAttribute.cs diff --git a/src/PolySharp.SourceGenerators/Extensions/AnalyzerConfigOptionsProviderExtensions.cs b/src/PolySharp.SourceGenerators/Extensions/AnalyzerConfigOptionsProviderExtensions.cs index d4b0af9..ff5227f 100644 --- a/src/PolySharp.SourceGenerators/Extensions/AnalyzerConfigOptionsProviderExtensions.cs +++ b/src/PolySharp.SourceGenerators/Extensions/AnalyzerConfigOptionsProviderExtensions.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; +using ComputeSharp.SourceGeneration.Helpers; using Microsoft.CodeAnalysis.Diagnostics; namespace PolySharp.SourceGenerators.Extensions; @@ -26,4 +28,32 @@ public static bool GetBoolMSBuildProperty(this AnalyzerConfigOptionsProvider opt options.GlobalOptions.TryGetValue($"build_property.{propertyName}", out string? propertyValue) && string.Equals(propertyValue, bool.TrueString, StringComparison.OrdinalIgnoreCase); } + + /// + /// Gets the value of an MSBuild property representing a semicolon-separated list of -s. + /// + /// The input instance. + /// The MSBuild property name. + /// The value of the specified MSBuild property. + public static ImmutableArray GetStringArrayMSBuildProperty(this AnalyzerConfigOptionsProvider options, string propertyName) + { + if (options.GlobalOptions.TryGetValue($"build_property.{propertyName}", out string? propertyValue)) + { + using ImmutableArrayBuilder builder = ImmutableArrayBuilder.Rent(); + + foreach (string part in propertyValue.Split(',', ';')) + { + string trimmed = part.Trim(); + + if (trimmed.Length > 0) + { + builder.Add(trimmed); + } + } + + return builder.ToImmutable(); + } + + return ImmutableArray.Empty; + } } \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/Extensions/IncrementalValueProviderExtensions.cs b/src/PolySharp.SourceGenerators/Extensions/IncrementalValueProviderExtensions.cs deleted file mode 100644 index 31af032..0000000 --- a/src/PolySharp.SourceGenerators/Extensions/IncrementalValueProviderExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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 Microsoft.CodeAnalysis; - -namespace PolySharp.SourceGenerators.Extensions; - -/// -/// Extension methods for the type. -/// -internal static class IncrementalValueProviderExtensions -{ - /// - /// Combines three instances. - /// - /// The type of values produced by the first input. - /// The type of values produced by the second input. - /// The type of values produced by the third input. - /// The first input. - /// The second input. - /// The third input. - /// The resulting combined result. - public static IncrementalValueProvider<(T1, T2, T3)> Combine( - this IncrementalValueProvider source1, - IncrementalValueProvider source2, - IncrementalValueProvider source3) - { - return - source1 - .Combine(source2) - .Combine(source3) - .Select(static (items, _) => (items.Left.Left, items.Left.Right, items.Right)); - } -} \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/Helpers/EquatableArray{T}.cs b/src/PolySharp.SourceGenerators/Helpers/EquatableArray{T}.cs new file mode 100644 index 0000000..74b57dc --- /dev/null +++ b/src/PolySharp.SourceGenerators/Helpers/EquatableArray{T}.cs @@ -0,0 +1,219 @@ +// 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; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace PolySharp.SourceGenerators.Helpers; + +/// +/// Extensions for . +/// +internal static class EquatableArray +{ + /// + /// Creates an instance from a given . + /// + /// The type of items in the input array. + /// The input instance. + /// An instance from a given . + public static EquatableArray AsEquatableArray(this ImmutableArray array) + where T : IEquatable + { + return new(array); + } +} + +/// +/// An imutable, equatable array. This is equivalent to but with value equality support. +/// +/// The type of values in the array. +internal readonly struct EquatableArray : IEquatable>, IEnumerable + where T : IEquatable +{ + /// + /// The underlying array. + /// + private readonly T[]? array; + + /// + /// Creates a new instance. + /// + /// The input to wrap. + public EquatableArray(ImmutableArray array) + { + this.array = Unsafe.As, T[]?>(ref array); + } + + /// + /// Gets a reference to an item at a specified position within the array. + /// + /// The index of the item to retrieve a reference to. + /// A reference to an item at a specified position within the array. + public T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => AsImmutableArray()[index]; + } + + /// + /// Gets a value indicating whether the current array is empty. + /// + public bool IsEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => AsImmutableArray().IsEmpty; + } + + /// + /// Gets a value indicating whether the current array is default or empty. + /// + public bool IsDefaultOrEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => AsImmutableArray().IsDefaultOrEmpty; + } + + /// + /// Gets the length of the current array. + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => AsImmutableArray().Length; + } + + /// + public bool Equals(EquatableArray array) + { + return AsSpan().SequenceEqual(array.AsSpan()); + } + + /// + public override bool Equals(object? obj) + { + return obj is EquatableArray array && Equals(this, array); + } + + /// + public override unsafe int GetHashCode() + { + if (this.array is not T[] array) + { + return 0; + } + + int hashCode = 0; + + foreach (T value in array) + { + hashCode = unchecked((hashCode * (int)0xA5555529) + value.GetHashCode()); + } + + return hashCode; + } + + /// + /// Gets an instance from the current . + /// + /// The from the current . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ImmutableArray AsImmutableArray() + { + return Unsafe.As>(ref Unsafe.AsRef(in this.array)); + } + + /// + /// Creates an instance from a given . + /// + /// The input instance. + /// An instance from a given . + public static EquatableArray FromImmutableArray(ImmutableArray array) + { + return new(array); + } + + /// + /// Returns a wrapping the current items. + /// + /// A wrapping the current items. + public ReadOnlySpan AsSpan() + { + return AsImmutableArray().AsSpan(); + } + + /// + /// Copies the contents of this instance. to a mutable array. + /// + /// The newly instantiated array. + public T[] ToArray() + { + return AsImmutableArray().ToArray(); + } + + /// + /// Gets an value to traverse items in the current array. + /// + /// An value to traverse items in the current array. + public ImmutableArray.Enumerator GetEnumerator() + { + return AsImmutableArray().GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)AsImmutableArray()).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)AsImmutableArray()).GetEnumerator(); + } + + /// + /// Implicitly converts an to . + /// + /// An instance from a given . + public static implicit operator EquatableArray(ImmutableArray array) + { + return FromImmutableArray(array); + } + + /// + /// Implicitly converts an to . + /// + /// An instance from a given . + public static implicit operator ImmutableArray(EquatableArray array) + { + return array.AsImmutableArray(); + } + + /// + /// Checks whether two values are the same. + /// + /// The first value. + /// The second value. + /// Whether and are equal. + public static bool operator ==(EquatableArray left, EquatableArray right) + { + return left.Equals(right); + } + + /// + /// Checks whether two values are not the same. + /// + /// The first value. + /// The second value. + /// Whether and are not equal. + public static bool operator !=(EquatableArray left, EquatableArray right) + { + return !left.Equals(right); + } +} \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/Helpers/ImmutableArrayBuilder{T}.cs b/src/PolySharp.SourceGenerators/Helpers/ImmutableArrayBuilder{T}.cs new file mode 100644 index 0000000..1cce225 --- /dev/null +++ b/src/PolySharp.SourceGenerators/Helpers/ImmutableArrayBuilder{T}.cs @@ -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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Runtime.CompilerServices; + +namespace ComputeSharp.SourceGeneration.Helpers; + +/// +/// A helper type to build sequences of values with pooled buffers. +/// +/// The type of items to create sequences for. +internal struct ImmutableArrayBuilder : IDisposable +{ + /// + /// The shared instance to share objects. + /// + private static readonly ObjectPool SharedObjectPool = new(static () => new Writer()); + + /// + /// The owner instance. + /// + private readonly ObjectPool objectPool; + + /// + /// The rented instance to use. + /// + private Writer? writer; + + /// + /// Creates a value with a pooled underlying data writer. + /// + /// A instance to write data to. + public static ImmutableArrayBuilder Rent() + { + return new(SharedObjectPool, SharedObjectPool.Allocate()); + } + + /// + /// Creates a new object with the specified parameters. + /// + /// The target to return to. + /// The target data writer to use. + private ImmutableArrayBuilder(ObjectPool objectPool, Writer writer) + { + this.objectPool = objectPool; + this.writer = writer; + } + + /// + /// Gets the data written to the underlying buffer so far, as a . + /// + public readonly ReadOnlySpan WrittenSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.writer!.WrittenSpan; + } + + /// + /// Gets the number of elements currently written in the current instance. + /// + public readonly int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.writer!.WrittenSpan.Length; + } + + /// + public readonly void Add(T item) + { + this.writer!.Add(item); + } + + /// + /// Adds the specified items to the end of the array. + /// + /// The items to add at the end of the array. + public readonly void AddRange(ReadOnlySpan items) + { + this.writer!.AddRange(items); + } + + /// + public readonly ImmutableArray ToImmutable() + { + T[] array = this.writer!.WrittenSpan.ToArray(); + + return Unsafe.As>(ref array); + } + + /// + public readonly T[] ToArray() + { + return this.writer!.WrittenSpan.ToArray(); + } + + /// + public override readonly string ToString() + { + return this.writer!.WrittenSpan.ToString(); + } + + /// + public void Dispose() + { + Writer? writer = this.writer; + + this.writer = null; + + if (writer is not null) + { + writer.Clear(); + + this.objectPool.Free(writer); + } + } + + /// + /// A class handling the actual buffer writing. + /// + private sealed class Writer + { + /// + /// The underlying array. + /// + private T[] array; + + /// + /// The starting offset within . + /// + private int index; + + /// + /// Creates a new instance with the specified parameters. + /// + public Writer() + { + if (typeof(T) == typeof(char)) + { + this.array = new T[1024]; + } + else + { + this.array = new T[8]; + } + + this.index = 0; + } + + /// + public ReadOnlySpan WrittenSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(this.array, 0, this.index); + } + + /// + public void Add(T value) + { + EnsureCapacity(1); + + this.array[this.index++] = value; + } + + /// + public void AddRange(ReadOnlySpan items) + { + EnsureCapacity(items.Length); + + items.CopyTo(this.array.AsSpan(this.index)); + + this.index += items.Length; + } + + /// + /// Clears the items in the current writer. + /// + public void Clear() + { + if (typeof(T) != typeof(char)) + { + this.array.AsSpan(0, this.index).Clear(); + } + + this.index = 0; + } + + /// + /// Ensures that has enough free space to contain a given number of new items. + /// + /// The minimum number of items to ensure space for in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacity(int requestedSize) + { + if (requestedSize > this.array.Length - this.index) + { + ResizeBuffer(requestedSize); + } + } + + /// + /// Resizes to ensure it can fit the specified number of new items. + /// + /// The minimum number of items to ensure space for in . + [MethodImpl(MethodImplOptions.NoInlining)] + private void ResizeBuffer(int sizeHint) + { + int minimumSize = this.index + sizeHint; + int requestedSize = Math.Max(this.array.Length * 2, minimumSize); + + T[] newArray = new T[requestedSize]; + + Array.Copy(this.array, newArray, this.index); + + this.array = newArray; + } + } +} + +/// +/// Private helpers for the type. +/// +file static class ImmutableArrayBuilder +{ + /// + /// Throws an for "index". + /// + public static void ThrowArgumentOutOfRangeExceptionForIndex() + { + throw new ArgumentOutOfRangeException("index"); + } +} \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/Helpers/ObjectPool{T}.cs b/src/PolySharp.SourceGenerators/Helpers/ObjectPool{T}.cs new file mode 100644 index 0000000..b025cf9 --- /dev/null +++ b/src/PolySharp.SourceGenerators/Helpers/ObjectPool{T}.cs @@ -0,0 +1,163 @@ +// 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. + +// Ported from Roslyn, see: https://github.com/dotnet/roslyn/blob/main/src/Dependencies/PooledObjects/ObjectPool%601.cs. + +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace ComputeSharp.SourceGeneration.Helpers; + +/// +/// +/// Generic implementation of object pooling pattern with predefined pool size limit. The main purpose +/// is that limited number of frequently used objects can be kept in the pool for further recycling. +/// +/// +/// Notes: +/// +/// +/// It is not the goal to keep all returned objects. Pool is not meant for storage. If there +/// is no space in the pool, extra returned objects will be dropped. +/// +/// +/// It is implied that if object was obtained from a pool, the caller will return it back in +/// a relatively short time. Keeping checked out objects for long durations is ok, but +/// reduces usefulness of pooling. Just new up your own. +/// +/// +/// +/// +/// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. +/// Rationale: if there is no intent for reusing the object, do not use pool - just use "new". +/// +/// +/// The type of objects to pool. +internal sealed class ObjectPool + where T : class +{ + /// + /// The factory is stored for the lifetime of the pool. We will call this only when pool needs to + /// expand. compared to "new T()", Func gives more flexibility to implementers and faster than "new T()". + /// + private readonly Func factory; + + /// + /// The array of cached items. + /// + private readonly Element[] items; + + /// + /// Storage for the pool objects. The first item is stored in a dedicated field + /// because we expect to be able to satisfy most requests from it. + /// + private T? firstItem; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The input factory to produce items. + public ObjectPool(Func factory) + : this(factory, Environment.ProcessorCount * 2) + { + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The input factory to produce items. + /// The pool size to use. + public ObjectPool(Func factory, int size) + { + this.factory = factory; + this.items = new Element[size - 1]; + } + + /// + /// Produces a instance. + /// + /// The returned item to use. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Allocate() + { + T? item = this.firstItem; + + if (item is null || item != Interlocked.CompareExchange(ref this.firstItem, null, item)) + { + item = AllocateSlow(); + } + + return item; + } + + /// + /// Returns a given instance to the pool. + /// + /// The instance to return. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Free(T obj) + { + if (this.firstItem is null) + { + this.firstItem = obj; + } + else + { + FreeSlow(obj); + } + } + + /// + /// Allocates a new item. + /// + /// The returned item to use. + [MethodImpl(MethodImplOptions.NoInlining)] + private T AllocateSlow() + { + foreach (ref Element element in this.items.AsSpan()) + { + T? instance = element.Value; + + if (instance is not null) + { + if (instance == Interlocked.CompareExchange(ref element.Value, null, instance)) + { + return instance; + } + } + } + + return this.factory(); + } + + /// + /// Frees a given item. + /// + /// The item to return to the pool. + [MethodImpl(MethodImplOptions.NoInlining)] + private void FreeSlow(T obj) + { + foreach (ref Element element in this.items.AsSpan()) + { + if (element.Value is null) + { + element.Value = obj; + + break; + } + } + } + + /// + /// A container for a produced item (using a wrapper to avoid covariance checks). + /// + private struct Element + { + /// + /// The value held at the current element. + /// + internal T? Value; + } +} \ No newline at end of file diff --git a/src/PolySharp.SourceGenerators/Models/GeneratedType.cs b/src/PolySharp.SourceGenerators/Models/GeneratedType.cs new file mode 100644 index 0000000..13a1ed3 --- /dev/null +++ b/src/PolySharp.SourceGenerators/Models/GeneratedType.cs @@ -0,0 +1,8 @@ +namespace PolySharp.SourceGenerators.Models; + +/// +/// A model to hold information on a type to generate. +/// +/// The fully qualified type name of the type to generate. +/// Whether to use public accessibility for the generated type. +internal sealed record GeneratedType(string FullyQualifiedMetadataName, bool IsPublicAccessibilityRequired); diff --git a/src/PolySharp.SourceGenerators/Models/GenerationOptions.cs b/src/PolySharp.SourceGenerators/Models/GenerationOptions.cs new file mode 100644 index 0000000..41713c5 --- /dev/null +++ b/src/PolySharp.SourceGenerators/Models/GenerationOptions.cs @@ -0,0 +1,14 @@ +using PolySharp.SourceGenerators.Helpers; + +namespace PolySharp.SourceGenerators.Models; + +/// +/// A model to hold all generation options for the source generator. +/// +/// Whether to use public accessibility for the generated types. +/// Whether to also generated dummy runtime supported attributes. +/// The collection of fully qualified type names of types to exclude from generation. +internal sealed record GenerationOptions( + bool UsePublicAccessibilityForGeneratedTypes, + bool IncludeRuntimeSupportedAttributes, + EquatableArray ExcludeGeneratedTypes); diff --git a/src/PolySharp.SourceGenerators/PolySharp.targets b/src/PolySharp.SourceGenerators/PolySharp.targets index e35cab6..bece541 100644 --- a/src/PolySharp.SourceGenerators/PolySharp.targets +++ b/src/PolySharp.SourceGenerators/PolySharp.targets @@ -41,6 +41,7 @@ +