Skip to content

Commit d2979e5

Browse files
feat: FixedArray & fixes
1 parent ae986d6 commit d2979e5

14 files changed

Lines changed: 234 additions & 40 deletions

File tree

Hypercube.Utilities.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hypercube.Utilities.Analyze
88
EndProject
99
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hypercube.Utilities.Analyzers.CodeFix", "src\Hypercube.Utilities.Analyzers.CodeFix\Hypercube.Utilities.Analyzers.CodeFix.csproj", "{33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}"
1010
EndProject
11+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hypercube.Utilities.Attributes", "src\Hypercube.Utilities.Attributes\Hypercube.Utilities.Attributes.csproj", "{8A1E3386-301E-424E-AD0F-C2BC29B225E6}"
12+
EndProject
1113
Global
1214
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1315
Debug|Any CPU = Debug|Any CPU
@@ -30,5 +32,9 @@ Global
3032
{33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Debug|Any CPU.Build.0 = Debug|Any CPU
3133
{33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Release|Any CPU.ActiveCfg = Release|Any CPU
3234
{33E0A347-BAE3-43A1-9D6B-E5AF033CCE16}.Release|Any CPU.Build.0 = Release|Any CPU
35+
{8A1E3386-301E-424E-AD0F-C2BC29B225E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36+
{8A1E3386-301E-424E-AD0F-C2BC29B225E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
37+
{8A1E3386-301E-424E-AD0F-C2BC29B225E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
38+
{8A1E3386-301E-424E-AD0F-C2BC29B225E6}.Release|Any CPU.Build.0 = Release|Any CPU
3339
EndGlobalSection
3440
EndGlobal

build/roslyn.props

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
<Project>
22

33
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>12</LangVersion>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
9+
<Version>1.1.2</Version>
10+
411
<IsRoslynComponent>true</IsRoslynComponent>
12+
<OutputItemType>Analyzer</OutputItemType>
13+
<IncludeBuildOutput>false</IncludeBuildOutput>
514
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
6-
<IncludeAnalyzerReleaseTrackingFiles>true</IncludeAnalyzerReleaseTrackingFiles>
15+
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
16+
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\Generated</CompilerGeneratedFilesOutputPath>
717
</PropertyGroup>
818

919
</Project>

src/Hypercube.Utilities.Analyzers.CodeFix/Hypercube.Utilities.Analyzers.CodeFix.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
3-
<Import Project="..\..\build\main.props" />
2+
43
<Import Project="..\..\build\package.props" />
54
<Import Project="..\..\build\roslyn.props" />
65

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using System.Text;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using Microsoft.CodeAnalysis.Text;
5+
6+
namespace Hypercube.Utilities.Analyzers.Generators;
7+
8+
[Generator]
9+
public sealed class FixedArrayGenerator : IIncrementalGenerator
10+
{
11+
private const string AttributeFullName = "Hypercube.Utilities.Attributes.FixedArrayAttribute";
12+
13+
public void Initialize(IncrementalGeneratorInitializationContext context)
14+
{
15+
var attributeSymbolProvider = context.CompilationProvider
16+
.Select(static (compilation, _) => compilation.GetTypeByMetadataName(AttributeFullName));
17+
18+
var structSymbols = context.SyntaxProvider
19+
.CreateSyntaxProvider(
20+
predicate: static (node, _) =>
21+
node is StructDeclarationSyntax syntax &&
22+
syntax.Modifiers.Any(m => m.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PartialKeyword)) &&
23+
syntax.AttributeLists.Count > 0,
24+
transform: static (ctx, _) =>
25+
{
26+
var structSyntax = (StructDeclarationSyntax) ctx.Node;
27+
return ctx.SemanticModel.GetDeclaredSymbol(structSyntax) as INamedTypeSymbol;
28+
})
29+
.Where(s => s is not null);
30+
31+
var pipeline = structSymbols.Combine(attributeSymbolProvider);
32+
33+
context.RegisterSourceOutput(pipeline, static (spc, source) =>
34+
{
35+
var (symbol, attributeSymbol) = source;
36+
if (symbol is null || attributeSymbol is null)
37+
return;
38+
39+
var attrData = symbol.GetAttributes()
40+
.FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeSymbol));
41+
42+
if (attrData is null)
43+
return;
44+
45+
if (attrData.ConstructorArguments.Length == 0 || attrData.ConstructorArguments[0].Value is not int length || length < 0)
46+
return;
47+
48+
var ns = symbol.ContainingNamespace.IsGlobalNamespace ? null : symbol.ContainingNamespace.ToDisplayString();
49+
var structName = symbol.Name;
50+
51+
var sourceText = GenerateSource(ns, structName, length);
52+
53+
var hintName = $"{symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}_FixedArray.g.cs"
54+
.Replace("global::", "")
55+
.Replace('<', '_')
56+
.Replace('>', '_');
57+
58+
spc.AddSource(hintName, SourceText.From(sourceText, Encoding.UTF8));
59+
});
60+
}
61+
62+
private static string GenerateSource(string? ns, string structName, int length)
63+
{
64+
var sb = new StringBuilder();
65+
66+
sb.AppendLine("// <auto-generated/>");
67+
sb.AppendLine("#nullable enable");
68+
sb.AppendLine("using System;");
69+
sb.AppendLine("using System.Runtime.CompilerServices;");
70+
sb.AppendLine("using System.Runtime.InteropServices;");
71+
sb.AppendLine();
72+
73+
if (!string.IsNullOrEmpty(ns))
74+
{
75+
sb.AppendLine($"namespace {ns}");
76+
sb.AppendLine("{");
77+
sb.Append(" ");
78+
}
79+
80+
sb.AppendLine("[StructLayout(LayoutKind.Sequential)]");
81+
sb.AppendLine($"public partial struct {structName}<T> where T : unmanaged");
82+
sb.AppendLine("{");
83+
sb.AppendLine($" public static {structName}<T> Empty => new {structName}<T>();");
84+
sb.AppendLine();
85+
sb.AppendLine($" public const int Length = {length};");
86+
sb.AppendLine();
87+
88+
for (var i = 0; i < length; i++)
89+
sb.AppendLine($" private T _element{i};");
90+
91+
sb.AppendLine();
92+
93+
sb.AppendLine(" public ref T this[int index]");
94+
sb.AppendLine(" {");
95+
sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
96+
sb.AppendLine(" get => ref AsSpan()[index];");
97+
sb.AppendLine(" }");
98+
sb.AppendLine();
99+
100+
sb.Append(GenerateConstructors(structName, length));
101+
102+
sb.AppendLine(" [MethodImpl(MethodImplOptions.AggressiveInlining)]");
103+
sb.AppendLine(length > 0
104+
? " public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _element0, Length);"
105+
: " public Span<T> AsSpan() => Span<T>.Empty;");
106+
107+
sb.AppendLine("}");
108+
109+
if (!string.IsNullOrEmpty(ns))
110+
sb.AppendLine("}");
111+
112+
return sb.ToString();
113+
}
114+
115+
private static string GenerateConstructors(string structName, int length)
116+
{
117+
var sb = new StringBuilder();
118+
119+
for (var count = 1; count <= length; count++)
120+
{
121+
var ctorParams = new StringBuilder();
122+
for (var i = 0; i < count; i++)
123+
{
124+
if (i > 0) ctorParams.Append(", ");
125+
ctorParams.Append($"T element{i}");
126+
}
127+
128+
sb.AppendLine($" public {structName}({ctorParams})");
129+
sb.AppendLine(" {");
130+
for (var i = 0; i < length; i++)
131+
{
132+
sb.AppendLine(i < count
133+
? $" _element{i} = element{i};"
134+
: $" _element{i} = default;");
135+
}
136+
137+
sb.AppendLine(" }");
138+
sb.AppendLine();
139+
}
140+
141+
return sb.ToString();
142+
}
143+
}

src/Hypercube.Utilities.Analyzers/Hypercube.Utilities.Analyzers.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
3-
<Import Project="..\..\build\main.props" />
2+
43
<Import Project="..\..\build\package.props" />
54
<Import Project="..\..\build\roslyn.props" />
65

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Hypercube.Utilities.Attributes;
2+
3+
[AttributeUsage(AttributeTargets.Struct)]
4+
public sealed class FixedArrayAttribute(int length) : Attribute
5+
{
6+
public int Length { get; } = length;
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
</Project>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Hypercube.Utilities.Collections;
2+
3+
namespace Hypercube.Utilities.UnitTests.Collections;
4+
5+
[TestFixture]
6+
public sealed class FixedArrayTests
7+
{
8+
[Test]
9+
public void GenerationTest()
10+
{
11+
var array = new FixedArray2<int>();
12+
13+
Assert.That(array[0], Is.EqualTo(0));
14+
Assert.That(array[1], Is.EqualTo(0));
15+
16+
array[0] = 10;
17+
18+
Assert.That(array[0], Is.EqualTo(10));
19+
}
20+
}

src/Hypercube.Utilities.UnitTests/Dependencies/DependenciesContainerTests.cs

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -128,23 +128,7 @@ public void InjectInConstructor()
128128

129129
container.Register<IService, Service>();
130130

131-
var instance = container.Instantiate<DependentInConstructor>();
132-
133-
Assert.That(instance.Service, Is.Not.Null);
134-
Assert.That(instance.Service, Is.TypeOf<Service>());
135-
}
136-
137-
[Test]
138-
public void InjectInConstructorAll()
139-
{
140-
var container = new DependenciesContainer();
141-
142-
container.Register<IService, Service>();
143-
container.Register<DependentInConstructor>();
144-
145-
container.ResolveAll();
146-
147-
var instance = container.Resolve<DependentInConstructor>();
131+
var instance = container.Instantiate<DependentConstructor>();
148132

149133
Assert.That(instance.Service, Is.Not.Null);
150134
Assert.That(instance.Service, Is.TypeOf<Service>());
@@ -248,9 +232,9 @@ public void AutoInject()
248232
var container = new DependenciesContainer();
249233

250234
container.Register<IService, Service>();
251-
container.Register<DependentInConstructor>();
235+
container.Register<DependentConstructor>();
252236

253-
var service = container.Resolve<DependentInConstructor>().Service;
237+
var service = container.Resolve<DependentConstructor>().Service;
254238

255239
Assert.That(service, Is.EqualTo(container.Resolve<IService>()));
256240
}
@@ -275,20 +259,6 @@ public void PostInject()
275259
private interface IService;
276260

277261
private class Service : IService;
278-
279-
private sealed class DependentInConstructor
280-
{
281-
// ReSharper disable once MemberHidesStaticFromOuterClass
282-
public readonly IService? Service;
283-
284-
[UsedImplicitly, Dependency]
285-
private readonly IService _service;
286-
287-
public DependentInConstructor()
288-
{
289-
Service = _service;
290-
}
291-
}
292262

293263
private sealed class DependentPostInject : IPostInject
294264
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Hypercube.Utilities.Attributes;
2+
using JetBrains.Annotations;
3+
4+
namespace Hypercube.Utilities.Collections;
5+
6+
[FixedArray( 2), PublicAPI] public partial struct FixedArray2<T>;
7+
[FixedArray( 3), PublicAPI] public partial struct FixedArray3<T>;
8+
[FixedArray( 4), PublicAPI] public partial struct FixedArray4<T>;
9+
[FixedArray( 5), PublicAPI] public partial struct FixedArray5<T>;
10+
[FixedArray( 6), PublicAPI] public partial struct FixedArray6<T>;
11+
[FixedArray( 7), PublicAPI] public partial struct FixedArray7<T>;
12+
[FixedArray( 8), PublicAPI] public partial struct FixedArray8<T>;
13+
[FixedArray( 9), PublicAPI] public partial struct FixedArray9<T>;
14+
[FixedArray(10), PublicAPI] public partial struct FixedArray10<T>;
15+
[FixedArray(11), PublicAPI] public partial struct FixedArray11<T>;
16+
[FixedArray(12), PublicAPI] public partial struct FixedArray12<T>;
17+
[FixedArray(13), PublicAPI] public partial struct FixedArray13<T>;
18+
[FixedArray(14), PublicAPI] public partial struct FixedArray14<T>;
19+
[FixedArray(15), PublicAPI] public partial struct FixedArray15<T>;
20+
[FixedArray(16), PublicAPI] public partial struct FixedArray16<T>;

0 commit comments

Comments
 (0)