Skip to content

Commit ba31473

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 ba31473

28 files changed

+174
-277
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[]
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: 6 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)enumerable.OfType<object>();
4444
}
4545

4646
public async Task<object?> EvalAsync(string expr, object? scope)
@@ -57,15 +57,12 @@ 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 =
61+
await Task.WhenAll(args.Parameters.Select(parameter => parameter.EvaluateAsync().AsTask()));
62+
IEnumerable<object?> funcArgs = evaluatedArgs.Select((paramValue, index) =>
63+
CoerceValue(paramValue, index < parameters.Length ? parameters[index].ParameterType : typeof(object)));
6764

68-
object? result = func.DynamicInvoke(funcArgs);
65+
object? result = func.DynamicInvoke([.. funcArgs]);
6966
args.Result = result;
7067
};
7168

src/Render/NodeRenderer.Layout.cs

Lines changed: 21 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,14 @@ 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(
14+
IEnumerable<(Node Node, Size Size)> items,
1315
bool isRow, bool wrap, float spacing, float containerMain)
1416
{
15-
List<List<(Node Node, Size Size)>> lines = [];
16-
List<(Node Node, Size Size)> currentLine = [];
17+
ImmutableArray<ImmutableArray<(Node Node, Size Size)>>.Builder lines =
18+
ImmutableArray.CreateBuilder<ImmutableArray<(Node Node, Size Size)>>();
19+
ImmutableArray<(Node Node, Size Size)>.Builder currentLine =
20+
ImmutableArray.CreateBuilder<(Node Node, Size Size)>();
1721
float currentMain = 0;
1822
foreach ((Node node, Size size) in items)
1923
{
@@ -22,8 +26,8 @@ private static float ResolveMainAxisContainerSize(int? explicitSize, int? desire
2226
bool wrapNow = wrap && currentLine.Count > 0 && nextMain > containerMain;
2327
if (wrapNow)
2428
{
25-
lines.Add(currentLine);
26-
currentLine = [];
29+
lines.Add(currentLine.ToImmutable());
30+
currentLine = ImmutableArray.CreateBuilder<(Node Node, Size Size)>();
2731
currentMain = 0;
2832
}
2933

@@ -33,32 +37,25 @@ private static float ResolveMainAxisContainerSize(int? explicitSize, int? desire
3337

3438
if (currentLine.Count > 0)
3539
{
36-
lines.Add(currentLine);
40+
lines.Add(currentLine.ToImmutable());
3741
}
3842

39-
return lines;
43+
return lines.ToImmutable();
4044
}
4145

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)
46+
private static ImmutableArray<(float Main, int Cross)> ResolveStackLineSizes(
47+
IEnumerable<IReadOnlyList<(Node Node, Size Size)>> lines, bool isRow, float spacing) =>
48+
[
49+
.. lines.Select(line =>
4750
{
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-
51+
float lineMain = line.Sum(i => isRow ? i.Size.Width : i.Size.Height) +
52+
(line.Count > 1 ? spacing * (line.Count - 1) : 0);
5453
int lineCross = line.Max(i => isRow ? i.Size.Height : i.Size.Width);
55-
lineSizes.Add((lineMain, lineCross));
56-
}
57-
58-
return lineSizes;
59-
}
54+
return (lineMain, lineCross);
55+
})
56+
];
6057

61-
private static (float Main, float Cross) ResolveStackContentSize(List<(float Main, int Cross)> lineSizes,
58+
private static (float Main, float Cross) ResolveStackContentSize(IReadOnlyList<(float Main, int Cross)> lineSizes,
6259
float runSpacing)
6360
{
6461
if (lineSizes.Count is 0)

src/Render/NodeRenderer.Measurement.cs

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,21 @@ 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 =
95+
ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
96+
ImmutableArray<(float Main, int Cross)> lineSizes = ResolveStackLineSizes([.. lines], isRow, stack.Spacing);
9797
(float contentMain, float contentCross) = ResolveStackContentSize(lineSizes, stack.RunSpacing);
9898

9999
int contentWidth = (int)Math.Ceiling(isRow ? contentMain : contentCross);
@@ -104,7 +104,7 @@ private static Size MeasureStackNode(StackNode stack, AssetProvider assets, Asse
104104
private static Size MeasureGridNode(GridNode grid, AssetProvider assets, AssetProvider measurer,
105105
ConcurrentDictionary<Node, Size> measurementCache)
106106
{
107-
List<Node> flowChildren = ExpandFlowChildren(grid.Children);
107+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(grid.Children)];
108108
if (flowChildren.Count is 0)
109109
{
110110
return Size.Empty;
@@ -115,16 +115,10 @@ private static Size MeasureGridNode(GridNode grid, AssetProvider assets, AssetPr
115115
Size[] sizes = new Size[flowChildren.Count];
116116
Parallel.For(0, flowChildren.Count,
117117
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-
}
118+
IEnumerable<int> colWidths = Enumerable.Range(0, columns).Select(c =>
119+
sizes.Where((_, i) => i % columns == c).DefaultIfEmpty(Size.Empty).Max(size => size.Width));
120+
IEnumerable<int> rowHeights = Enumerable.Range(0, rows).Select(r =>
121+
sizes.Where((_, i) => i / columns == r).DefaultIfEmpty(Size.Empty).Max(size => size.Height));
128122

129123
int width = colWidths.Sum() + (Math.Max(0, columns - 1) * grid.ColumnGap);
130124
int height = rowHeights.Sum() + (Math.Max(0, rows - 1) * grid.RowGap);
@@ -147,32 +141,14 @@ private static Size MeasureLayerNode(LayerNode layer, AssetProvider assets, Asse
147141
private static Size MeasureChildren(IEnumerable<Node> children, AssetProvider assets, AssetProvider measurer,
148142
ConcurrentDictionary<Node, Size> measurementCache)
149143
{
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-
144+
(int width, int height) = children.Select(child => Measure(child, assets, measurer, measurementCache))
145+
.Aggregate((Width: 0, Height: 0),
146+
static (acc, size) => (Math.Max(acc.Width, size.Width), Math.Max(acc.Height, size.Height)));
159147
return new(width, height);
160148
}
161149

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-
}
150+
private static IEnumerable<Node> ExpandFlowChildren(IEnumerable<Node> children) =>
151+
children.SelectMany(static child => child is LayerNode { Opacity: 1, Key: null, Children: var nested }
152+
? ExpandFlowChildren(nested)
153+
: [child]);
178154
}

src/Render/NodeRenderer.Rendering.cs

Lines changed: 11 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,9 @@ 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 =
101+
ResolveStackLines(items, isRow, stack.Wrap, stack.Spacing, wrapMain);
102+
ImmutableArray<(float Main, int Cross)> lineSizes = ResolveStackLineSizes([.. lines], isRow, stack.Spacing);
102103
(float contentMain, float contentCross) = ResolveStackContentSize(lineSizes, stack.RunSpacing);
103104
float resolvedContainerMain = ResolveMainAxisContainerSize(isRow ? stack.Width : stack.Height,
104105
isRow ? desiredSize?.Width : desiredSize?.Height, contentMain);
@@ -113,18 +114,18 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
113114
? (int)Math.Floor(stack.RunSpacing)
114115
: (int)Math.Ceiling(stack.RunSpacing);
115116
float runSpacingFraction = stack.RunSpacing - runSpacingWhole;
116-
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
117+
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
117118
{
118-
List<(Node Node, Size Size)> line = lines[lineIndex];
119+
ImmutableArray<(Node Node, Size Size)> line = lines[lineIndex];
119120
(float lineMain, int lineCross) = lineSizes[lineIndex];
120121
(float startMain, float between) = ResolveMainAxisLayout(stack.JustifyContent, resolvedContainerMain,
121-
lineMain, stack.Spacing, line.Count);
122+
lineMain, stack.Spacing, line.Length);
122123
float mainBase = (isRow ? origin.X : origin.Y) + startMain;
123124
int mainCursor = (int)Math.Round(mainBase, MidpointRounding.AwayFromZero);
124125
float gapError = mainBase - mainCursor;
125126
int betweenWhole = between >= 0 ? (int)Math.Floor(between) : (int)Math.Ceiling(between);
126127
float betweenFraction = between - betweenWhole;
127-
for (int itemIndex = 0; itemIndex < line.Count; itemIndex++)
128+
for (int itemIndex = 0; itemIndex < line.Length; itemIndex++)
128129
{
129130
(Node node, Size size) = line[itemIndex];
130131
int itemCross = isRow ? size.Height : size.Width;
@@ -141,7 +142,7 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
141142
(int)Math.Round(childY, MidpointRounding.AwayFromZero));
142143
RenderNode(canvas, node, assets, measurer, childOrigin, inheritedOpacity, childDesiredSize, scale,
143144
resampler, measurementCache);
144-
if (itemIndex == line.Count - 1)
145+
if (itemIndex == line.Length - 1)
145146
{
146147
continue;
147148
}
@@ -163,7 +164,7 @@ private static void RenderStackNode(Image canvas, StackNode stack, AssetProvider
163164
mainCursor += step;
164165
}
165166

166-
if (lineIndex == lines.Count - 1)
167+
if (lineIndex == lines.Length - 1)
167168
{
168169
continue;
169170
}
@@ -189,7 +190,7 @@ private static void RenderGridNode(Image canvas, GridNode grid, AssetProvider as
189190
Point origin, float inheritedOpacity, Size? desiredSize, float scale, ResamplerType resampler,
190191
ConcurrentDictionary<Node, Size> measurementCache)
191192
{
192-
List<Node> flowChildren = ExpandFlowChildren(grid.Children);
193+
IReadOnlyList<Node> flowChildren = [.. ExpandFlowChildren(grid.Children)];
193194
if (flowChildren.Count is 0)
194195
{
195196
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)