Skip to content

Commit 564e1f2

Browse files
committed
refactor: Optimize code with more generic collection types and LINQ
- Changed the `Children` property of multiple node classes from `List<Node>` to `IEnumerable<Node>` for improved flexibility - Refactored multiple algorithms using LINQ queries and immutable collections to simplify code logic - Optimized parallel queries to avoid unnecessary `ToArray()` calls - Refactored conditional branches and loop processing to improve readability and performance
1 parent 8faa941 commit 564e1f2

26 files changed

+133
-272
lines changed

src/Prober/Lxns/LxnsResourceClient.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,14 @@ private async Task<T> GetListAsync<T>(string type, string includeType, int? vers
99
CancellationToken cancellationToken = default)
1010
{
1111
string url = $"/api/v0/maimai/{type}/list";
12-
List<string> queryParts = [];
13-
if (version.HasValue)
14-
{
15-
queryParts.Add($"version={version.Value}");
16-
}
17-
18-
if (include.HasValue)
12+
string query = string.Join("&", new string?[]
1913
{
20-
queryParts.Add($"{includeType}={include.Value.ToString().ToLowerInvariant()}");
21-
}
22-
23-
if (queryParts.Count > 0)
14+
version.HasValue ? $"version={version.Value}" : null,
15+
include.HasValue ? $"{includeType}={include.Value.ToString().ToLowerInvariant()}" : null
16+
}.OfType<string>());
17+
if (!string.IsNullOrWhiteSpace(query))
2418
{
25-
url += $"?{string.Join("&", queryParts)}";
19+
url += $"?{query}";
2620
}
2721

2822
T response = await GetAsync<T>(url, cancellationToken);

src/Render/ExpressionEngine/AsyncNCalcEngine.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void RegisterFunction(string name, Delegate func)
4040
return (T?)ConvertValue(result, typeof(T));
4141
}
4242

43-
return (T)(object)enumerable.Cast<object?>().Where(item => item is not null).Cast<object>().ToArray();
43+
return (T)(object)enumerable.OfType<object>();
4444
}
4545

4646
public async Task<object?> EvalAsync(string expr, object? scope)
@@ -57,15 +57,10 @@ public void RegisterFunction(string name, Delegate func)
5757
}
5858

5959
ParameterInfo[] parameters = _functionParameters.GetOrAdd(name, _ => func.Method.GetParameters());
60-
object?[] funcArgs = new object[args.Parameters.Length];
61-
for (int i = 0; i < args.Parameters.Length; ++i)
62-
{
63-
object? paramValue = await args.Parameters[i].EvaluateAsync();
64-
funcArgs[i] = CoerceValue(paramValue,
65-
i < parameters.Length ? parameters[i].ParameterType : typeof(object));
66-
}
60+
object?[] evaluatedArgs = await Task.WhenAll(args.Parameters.Select(parameter => parameter.EvaluateAsync().AsTask()));
61+
IEnumerable<object?> funcArgs = evaluatedArgs.Select((paramValue, index) => CoerceValue(paramValue, index < parameters.Length ? parameters[index].ParameterType : typeof(object)));
6762

68-
object? result = func.DynamicInvoke(funcArgs);
63+
object? result = func.DynamicInvoke([.. funcArgs]);
6964
args.Result = result;
7065
};
7166

src/Render/NodeRenderer.Layout.cs

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Limekuma.Render.Nodes;
22
using Limekuma.Render.Types;
33
using SixLabors.ImageSharp;
4+
using System.Collections.Immutable;
45

56
namespace Limekuma.Render;
67

@@ -9,11 +10,11 @@ public static partial class NodeRenderer
910
private static float ResolveMainAxisContainerSize(int? explicitSize, int? desiredSize, float fallback) =>
1011
explicitSize ?? desiredSize ?? fallback;
1112

12-
private static List<List<(Node Node, Size Size)>> ResolveStackLines(IEnumerable<(Node Node, Size Size)> items,
13+
private static ImmutableArray<ImmutableArray<(Node Node, Size Size)>> ResolveStackLines(IEnumerable<(Node Node, Size Size)> items,
1314
bool isRow, bool wrap, float spacing, float containerMain)
1415
{
15-
List<List<(Node Node, Size Size)>> lines = [];
16-
List<(Node Node, Size Size)> currentLine = [];
16+
ImmutableArray<ImmutableArray<(Node Node, Size Size)>>.Builder lines = ImmutableArray.CreateBuilder<ImmutableArray<(Node Node, Size Size)>>();
17+
ImmutableArray<(Node Node, Size Size)>.Builder currentLine = ImmutableArray.CreateBuilder<(Node Node, Size Size)>();
1718
float currentMain = 0;
1819
foreach ((Node node, Size size) in items)
1920
{
@@ -22,8 +23,8 @@ private static float ResolveMainAxisContainerSize(int? explicitSize, int? desire
2223
bool wrapNow = wrap && currentLine.Count > 0 && nextMain > containerMain;
2324
if (wrapNow)
2425
{
25-
lines.Add(currentLine);
26-
currentLine = [];
26+
lines.Add(currentLine.ToImmutable());
27+
currentLine = ImmutableArray.CreateBuilder<(Node Node, Size Size)>();
2728
currentMain = 0;
2829
}
2930

@@ -33,32 +34,21 @@ private static float ResolveMainAxisContainerSize(int? explicitSize, int? desire
3334

3435
if (currentLine.Count > 0)
3536
{
36-
lines.Add(currentLine);
37+
lines.Add(currentLine.ToImmutable());
3738
}
3839

39-
return lines;
40+
return lines.ToImmutable();
4041
}
4142

42-
private static List<(float Main, int Cross)> ResolveStackLineSizes(
43-
IEnumerable<IReadOnlyList<(Node Node, Size Size)>> lines, bool isRow, float spacing)
44-
{
45-
List<(float Main, int Cross)> lineSizes = [];
46-
foreach (IReadOnlyList<(Node Node, Size Size)> line in lines)
43+
private static ImmutableArray<(float Main, int Cross)> ResolveStackLineSizes(
44+
IEnumerable<IReadOnlyList<(Node Node, Size Size)>> lines, bool isRow, float spacing) => [.. lines.Select(line =>
4745
{
48-
float lineMain = line.Sum(i => isRow ? i.Size.Width : i.Size.Height);
49-
if (line.Count > 1)
50-
{
51-
lineMain += spacing * (line.Count - 1);
52-
}
53-
46+
float lineMain = line.Sum(i => isRow ? i.Size.Width : i.Size.Height) + (line.Count > 1 ? spacing * (line.Count - 1) : 0);
5447
int lineCross = line.Max(i => isRow ? i.Size.Height : i.Size.Width);
55-
lineSizes.Add((lineMain, lineCross));
56-
}
57-
58-
return lineSizes;
59-
}
48+
return (lineMain, lineCross);
49+
})];
6050

61-
private static (float Main, float Cross) ResolveStackContentSize(List<(float Main, int Cross)> lineSizes,
51+
private static (float Main, float Cross) ResolveStackContentSize(IReadOnlyList<(float Main, int Cross)> lineSizes,
6252
float runSpacing)
6353
{
6454
if (lineSizes.Count is 0)

src/Render/NodeRenderer.Measurement.cs

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,20 @@ private static Size MeasureResizedNode(ResizedNode resized, AssetProvider assets
7979
private static Size MeasureStackNode(StackNode stack, AssetProvider assets, AssetProvider measurer,
8080
ConcurrentDictionary<Node, Size> measurementCache)
8181
{
82-
List<Node> flowChildren = ExpandFlowChildren(stack.Children);
82+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(stack.Children)];
8383
List<(Node Node, Size Size)> items = [];
8484
if (flowChildren.Count > 0)
8585
{
8686
Size[] sizes = new Size[flowChildren.Count];
8787
Parallel.For(0, flowChildren.Count,
8888
i => { sizes[i] = Measure(flowChildren[i], assets, measurer, measurementCache); });
89-
items.EnsureCapacity(flowChildren.Count);
90-
items.AddRange(flowChildren.Select((t, i) => (t, sizes[i])));
89+
items = [.. flowChildren.Select((child, index) => (child, sizes[index]))];
9190
}
9291

9392
bool isRow = stack.Direction is StackDirection.Row;
9493
float wrapMain = ResolveMainAxisContainerSize(isRow ? stack.Width : stack.Height, null, int.MaxValue);
95-
List<List<(Node Node, Size Size)>> lines = ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
96-
List<(float Main, int Cross)> lineSizes = ResolveStackLineSizes(lines, isRow, stack.Spacing);
94+
ImmutableArray<ImmutableArray<(Node Node, Size Size)>> lines = ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
95+
ImmutableArray<(float Main, int Cross)> lineSizes = ResolveStackLineSizes([.. lines], isRow, stack.Spacing);
9796
(float contentMain, float contentCross) = ResolveStackContentSize(lineSizes, stack.RunSpacing);
9897

9998
int contentWidth = (int)Math.Ceiling(isRow ? contentMain : contentCross);
@@ -104,7 +103,7 @@ private static Size MeasureStackNode(StackNode stack, AssetProvider assets, Asse
104103
private static Size MeasureGridNode(GridNode grid, AssetProvider assets, AssetProvider measurer,
105104
ConcurrentDictionary<Node, Size> measurementCache)
106105
{
107-
List<Node> flowChildren = ExpandFlowChildren(grid.Children);
106+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(grid.Children)];
108107
if (flowChildren.Count is 0)
109108
{
110109
return Size.Empty;
@@ -115,16 +114,8 @@ private static Size MeasureGridNode(GridNode grid, AssetProvider assets, AssetPr
115114
Size[] sizes = new Size[flowChildren.Count];
116115
Parallel.For(0, flowChildren.Count,
117116
i => { sizes[i] = Measure(flowChildren[i], assets, measurer, measurementCache); });
118-
int[] colWidths = new int[columns];
119-
int[] rowHeights = new int[rows];
120-
for (int i = 0; i < flowChildren.Count; i++)
121-
{
122-
Size size = sizes[i];
123-
int r = i / columns;
124-
int c = i % columns;
125-
colWidths[c] = Math.Max(colWidths[c], size.Width);
126-
rowHeights[r] = Math.Max(rowHeights[r], size.Height);
127-
}
117+
IEnumerable<int> colWidths = Enumerable.Range(0, columns).Select(c => sizes.Where((_, i) => i % columns == c).DefaultIfEmpty(Size.Empty).Max(size => size.Width));
118+
IEnumerable<int> rowHeights = Enumerable.Range(0, rows).Select(r => sizes.Where((_, i) => i / columns == r).DefaultIfEmpty(Size.Empty).Max(size => size.Height));
128119

129120
int width = colWidths.Sum() + (Math.Max(0, columns - 1) * grid.ColumnGap);
130121
int height = rowHeights.Sum() + (Math.Max(0, rows - 1) * grid.RowGap);
@@ -147,32 +138,9 @@ private static Size MeasureLayerNode(LayerNode layer, AssetProvider assets, Asse
147138
private static Size MeasureChildren(IEnumerable<Node> children, AssetProvider assets, AssetProvider measurer,
148139
ConcurrentDictionary<Node, Size> measurementCache)
149140
{
150-
int width = 0;
151-
int height = 0;
152-
foreach (Node child in children)
153-
{
154-
Size size = Measure(child, assets, measurer, measurementCache);
155-
width = Math.Max(width, size.Width);
156-
height = Math.Max(height, size.Height);
157-
}
158-
141+
(int width, int height) = children.Select(child => Measure(child, assets, measurer, measurementCache)).Aggregate((Width: 0, Height: 0), static (acc, size) => (Math.Max(acc.Width, size.Width), Math.Max(acc.Height, size.Height)));
159142
return new(width, height);
160143
}
161144

162-
private static List<Node> ExpandFlowChildren(IEnumerable<Node> children)
163-
{
164-
List<Node> output = [];
165-
foreach (Node child in children)
166-
{
167-
if (child is LayerNode { Opacity: 1, Key: null, Children: var nested })
168-
{
169-
output.AddRange(ExpandFlowChildren(nested));
170-
continue;
171-
}
172-
173-
output.Add(child);
174-
}
175-
176-
return output;
177-
}
145+
private static IEnumerable<Node> ExpandFlowChildren(IEnumerable<Node> children) => children.SelectMany(static child => child is LayerNode { Opacity: 1, Key: null, Children: var nested } ? ExpandFlowChildren(nested) : [child]);
178146
}

src/Render/NodeRenderer.Rendering.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
7979
Point origin, float inheritedOpacity, Size? desiredSize, float scale, ResamplerType resampler,
8080
ConcurrentDictionary<Node, Size> measurementCache)
8181
{
82-
List<Node> flowChildren = ExpandFlowChildren(stack.Children);
82+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(stack.Children)];
8383
List<(Node Node, Size Size)> items = [];
8484
if (flowChildren.Count > 0)
8585
{
@@ -97,8 +97,8 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
9797

9898
bool isRow = stack.Direction is StackDirection.Row;
9999
float wrapMain = ResolveMainAxisContainerSize(isRow ? stack.Width : stack.Height, null, int.MaxValue);
100-
List<List<(Node Node, Size Size)>> lines = ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
101-
List<(float Main, int Cross)> lineSizes = ResolveStackLineSizes(lines, isRow, stack.Spacing);
100+
ImmutableArray<ImmutableArray<(Node Node, Size Size)>> lines = ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
101+
ImmutableArray<(float Main, int Cross)> lineSizes = ResolveStackLineSizes([.. lines], isRow, stack.Spacing);
102102
(float contentMain, float contentCross) = ResolveStackContentSize(lineSizes, stack.RunSpacing);
103103
float resolvedContainerMain = ResolveMainAxisContainerSize(isRow ? stack.Width : stack.Height,
104104
isRow ? desiredSize?.Width : desiredSize?.Height, contentMain);
@@ -113,18 +113,18 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
113113
? (int)Math.Floor(stack.RunSpacing)
114114
: (int)Math.Ceiling(stack.RunSpacing);
115115
float runSpacingFraction = stack.RunSpacing - runSpacingWhole;
116-
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
116+
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
117117
{
118-
List<(Node Node, Size Size)> line = lines[lineIndex];
118+
ImmutableArray<(Node Node, Size Size)> line = lines[lineIndex];
119119
(float lineMain, int lineCross) = lineSizes[lineIndex];
120120
(float startMain, float between) = ResolveMainAxisLayout(stack.JustifyContent, resolvedContainerMain,
121-
lineMain, stack.Spacing, line.Count);
121+
lineMain, stack.Spacing, line.Length);
122122
float mainBase = (isRow ? origin.X : origin.Y) + startMain;
123123
int mainCursor = (int)Math.Round(mainBase, MidpointRounding.AwayFromZero);
124124
float gapError = mainBase - mainCursor;
125125
int betweenWhole = between >= 0 ? (int)Math.Floor(between) : (int)Math.Ceiling(between);
126126
float betweenFraction = between - betweenWhole;
127-
for (int itemIndex = 0; itemIndex < line.Count; itemIndex++)
127+
for (int itemIndex = 0; itemIndex < line.Length; itemIndex++)
128128
{
129129
(Node node, Size size) = line[itemIndex];
130130
int itemCross = isRow ? size.Height : size.Width;
@@ -141,7 +141,7 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
141141
(int)Math.Round(childY, MidpointRounding.AwayFromZero));
142142
RenderNode(canvas, node, assets, measurer, childOrigin, inheritedOpacity, childDesiredSize, scale,
143143
resampler, measurementCache);
144-
if (itemIndex == line.Count - 1)
144+
if (itemIndex == line.Length - 1)
145145
{
146146
continue;
147147
}
@@ -163,7 +163,7 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
163163
mainCursor += step;
164164
}
165165

166-
if (lineIndex == lines.Count - 1)
166+
if (lineIndex == lines.Length - 1)
167167
{
168168
continue;
169169
}
@@ -189,7 +189,7 @@ private static void RenderGridNode(Image canvas, GridNode grid, AssetProvider as
189189
Point origin, float inheritedOpacity, Size? desiredSize, float scale, ResamplerType resampler,
190190
ConcurrentDictionary<Node, Size> measurementCache)
191191
{
192-
List<Node> flowChildren = ExpandFlowChildren(grid.Children);
192+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(grid.Children)];
193193
if (flowChildren.Count is 0)
194194
{
195195
return;

src/Render/Nodes/CanvasNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ public sealed record CanvasNode(
66
int Width,
77
int Height,
88
Color? Background,
9-
List<Node> Children,
9+
IEnumerable<Node> Children,
1010
string? Key
1111
) : Node(Key);

src/Render/Nodes/GridNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ public sealed record GridNode(
1212
AlignItems AlignItems,
1313
int? Width,
1414
int? Height,
15-
List<Node> Children,
15+
IEnumerable<Node> Children,
1616
string? Key
1717
) : Node(Key);

src/Render/Nodes/LayerNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ namespace Limekuma.Render.Nodes;
22

33
public sealed record LayerNode(
44
float Opacity,
5-
List<Node> Children,
5+
IEnumerable<Node> Children,
66
string? Key
77
) : Node(Key);

src/Render/Nodes/PositionedNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ public sealed record PositionedNode(
99
PositionAnchor AnchorY,
1010
int? Width,
1111
int? Height,
12-
List<Node> Children,
12+
IEnumerable<Node> Children,
1313
string? Key
1414
) : Node(Key);

src/Render/Nodes/StackNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ public sealed record StackNode(
1212
ContentAlign AlignContent,
1313
int? Width,
1414
int? Height,
15-
List<Node> Children,
15+
IEnumerable<Node> Children,
1616
string? Key
1717
) : Node(Key);

0 commit comments

Comments
 (0)