Skip to content

Commit bfbcad0

Browse files
committed
chore: memory optimizations
1 parent 4bbac17 commit bfbcad0

15 files changed

Lines changed: 374 additions & 65 deletions

.editorconfig

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ dotnet_diagnostic.ide0002.severity = warning
1212
dotnet_diagnostic.sa0001.severity = none
1313
dotnet_diagnostic.ide0003.severity = warning
1414
dotnet_diagnostic.ide0009.severity = warning
15+
dotnet_diagnostic.IDE0220.severity = error
16+
1517
dotnet_style_qualification_for_field = false:warning
1618
dotnet_style_qualification_for_property = false:warning
1719
dotnet_style_qualification_for_method = false:warning
1820
dotnet_style_qualification_for_event = false:warning
1921

22+
dotnet_diagnostic.SA1633.severity = none
23+
dotnet_diagnostic.SA1600.severity = none
24+
dotnet_diagnostic.SA1502.severity = none
25+
2026
dotnet_diagnostic.SA1010.severity = none
2127

2228
dotnet_diagnostic.ide0004.severity = warning
@@ -316,6 +322,15 @@ csharp_style_prefer_tuple_swap = false:suggestion
316322
dotnet_diagnostic.ide1005.severity = suggestion
317323
csharp_style_conditional_delegate_call = true:suggestion
318324

325+
# IDE0290
326+
dotnet_diagnostic.ide0290.severity = none
327+
csharp_style_prefer_primary_constructors = false
328+
resharper_convert_to_primary_constructor_highlighting = none
329+
330+
# IDE0305
331+
dotnet_diagnostic.ide0305.severity = none
332+
dotnet_style_prefer_collection_expression = false
333+
319334
dotnet_diagnostic.ca1000.severity = none
320335
dotnet_diagnostic.ca1001.severity = warning
321336
dotnet_diagnostic.ca1002.severity = warning
@@ -446,6 +461,7 @@ dotnet_diagnostic.ca1854.severity = suggestion
446461
dotnet_diagnostic.ca2000.severity = suggestion
447462
# dotnet_diagnostic.CA2000.severity = warning
448463
dotnet_diagnostic.ca2002.severity = none
464+
dotnet_diagnostic.ca2007.severity = none
449465
dotnet_diagnostic.ca2008.severity = none
450466
dotnet_diagnostic.ca2009.severity = warning
451467
dotnet_diagnostic.ca2011.severity = warning
@@ -566,4 +582,23 @@ csharp_wrap_chained_method_calls = chop_if_long
566582

567583
# ReSharper properties
568584
resharper_csharp_wrap_after_declaration_lpar = true
569-
resharper_csharp_wrap_parameters_style = chop_if_long
585+
resharper_csharp_wrap_parameters_style = chop_if_long
586+
587+
dotnet_diagnostic.CS1591.severity = none
588+
dotnet_diagnostic.CA1062.severity = none
589+
dotnet_diagnostic.CA1034.severity = none
590+
591+
dotnet_diagnostic.SA1101.severity = none
592+
dotnet_diagnostic.SA1503.severity = none
593+
dotnet_diagnostic.SA1309.severity = none
594+
dotnet_diagnostic.SA1601.severity = none
595+
dotnet_diagnostic.SA1201.severity = none
596+
dotnet_diagnostic.SA1127.severity = none
597+
dotnet_diagnostic.SA1128.severity = none
598+
dotnet_diagnostic.SA1611.severity = none
599+
dotnet_diagnostic.SA1604.severity = none
600+
dotnet_diagnostic.SA1629.severity = none
601+
dotnet_diagnostic.SA1602.severity = none
602+
dotnet_diagnostic.SA1615.severity = none
603+
604+
resharper_braces_redundant = false

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66
<ItemGroup>
77
<PackageVersion Include="FluentScanning" Version="2.0.1" />
8-
<PackageVersion Include="Itmo.Dev.Editorconfig" Version="1.0.3" />
8+
<PackageVersion Include="Itmo.Dev.Editorconfig" Version="1.0.9" />
99
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
1010
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
1111
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />

sample/Phazor.Sample/Pages/GridPage.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
dimension @(_dimension)
1515
</div>
1616

17-
<PhazorGrid @ref="_grid" Dimension="_dimension" Gap="10px" Style="background-color: teal">
17+
<PhazorGrid @ref="_grid" Dimension="_dimension" Gap="10px">
1818
<PhazorReactiveForEach Elements="_values" Context="_">
1919
<PhazorGridSection>
2020
<PhazorGridItem>

src/Phazor.Components/Components/PhazorComponent.razor.cs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
using Microsoft.AspNetCore.Components;
2+
using Phazor.Components.Extensions;
3+
using Phazor.Components.Models;
24
using Phazor.Components.Tools;
35

46
namespace Phazor.Components;
57

68
public abstract class PhazorComponent : ComponentBase, ICssClassDirector, ICssStyleDirector
79
{
8-
protected string ComputedStyle => new CssStyleBuilder().Use(this).Build();
9-
protected string ComputedClass => new CssClassBuilder().Use(this).Build();
10+
private readonly CssStyleFactory _styleFactory;
11+
private readonly CssClassFactory _classFactory;
12+
13+
protected PhazorComponent()
14+
{
15+
_styleFactory = new CssStyleFactory(Direct);
16+
_classFactory = new CssClassFactory(Direct);
17+
}
18+
19+
protected string ComputedStyle => _styleFactory.Value;
20+
protected string ComputedClass => _classFactory.Value;
1021

1122
[Parameter]
1223
public string? Style { get; set; }
@@ -43,7 +54,37 @@ public CssStyleBuilder Direct(CssStyleBuilder builder)
4354
return builder;
4455
}
4556

57+
public sealed override async Task SetParametersAsync(ParameterView parameters)
58+
{
59+
bool styleChanged =
60+
parameters.TryGetUpdatedValue(nameof(Style), Style, out _)
61+
|| parameters.TryGetUpdatedValue(nameof(Width), Width, out _)
62+
|| parameters.TryGetUpdatedValue(nameof(Height), Height, out _);
63+
64+
bool classChanged = parameters.TryGetUpdatedValue(nameof(Class), Class, out _);
65+
66+
PhazorSetParametersResult beforeResult = await BeforeSetParametersAsync(parameters);
67+
await base.SetParametersAsync(parameters);
68+
PhazorSetParametersResult afterResult = await AfterSetParametersAsync(parameters);
69+
70+
if (styleChanged || beforeResult.StyleChanged || afterResult.StyleChanged)
71+
OnStyleChanged();
72+
73+
if (classChanged || beforeResult.ClassChanged || afterResult.ClassChanged)
74+
OnClassChanged();
75+
}
76+
77+
protected void OnStyleChanged() => _styleFactory.Invalidate();
78+
79+
protected void OnClassChanged() => _classFactory.Invalidate();
80+
4681
protected virtual void ConfigureClasses(CssClassBuilder builder) { }
4782

4883
protected virtual void ConfigureStyle(CssStyleBuilder builder) { }
49-
}
84+
85+
protected virtual ValueTask<PhazorSetParametersResult> BeforeSetParametersAsync(ParameterView parameters)
86+
=> ValueTask.FromResult(new PhazorSetParametersResult(StyleChanged: false, ClassChanged: false));
87+
88+
protected virtual ValueTask<PhazorSetParametersResult> AfterSetParametersAsync(ParameterView parameters)
89+
=> ValueTask.FromResult(new PhazorSetParametersResult(StyleChanged: false, ClassChanged: false));
90+
}

src/Phazor.Components/Components/Stacks/PhazorHStack.razor

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@namespace Phazor.Components
2+
@using Phazor.Components.Extensions
23
@using Phazor.Components.Tools
34
@using Phazor.Components.Models
45
@inherits PhazorComponent
@@ -24,6 +25,17 @@
2425
[Parameter]
2526
public RenderFragment? ChildContent { get; set; }
2627

28+
protected override ValueTask<PhazorSetParametersResult> BeforeSetParametersAsync(ParameterView parameters)
29+
{
30+
bool classChanged =
31+
parameters.TryGetUpdatedValue(nameof(Reversed), Reversed, out bool? _)
32+
|| parameters.TryGetUpdatedValue(nameof(Horizontal), Horizontal, out HorizontalDistribution? _)
33+
|| parameters.TryGetUpdatedValue(nameof(Vertical), Vertical, out VerticalAlignment? _)
34+
|| parameters.TryGetUpdatedValue(nameof(Variant), Variant, out PhazorStackVariant? _);
35+
36+
return ValueTask.FromResult(new PhazorSetParametersResult(StyleChanged: false, ClassChanged: classChanged));
37+
}
38+
2739
protected override void ConfigureClasses(CssClassBuilder builder)
2840
{
2941
builder

src/Phazor.Components/Components/Stacks/PhazorVStack.razor

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@namespace Phazor.Components
2+
@using Phazor.Components.Extensions
23
@using Phazor.Components.Tools
34
@using Phazor.Components.Models
45
@inherits PhazorComponent
@@ -24,6 +25,17 @@
2425
[Parameter]
2526
public RenderFragment? ChildContent { get; set; }
2627

28+
protected override ValueTask<PhazorSetParametersResult> BeforeSetParametersAsync(ParameterView parameters)
29+
{
30+
bool classChanged =
31+
parameters.TryGetUpdatedValue(nameof(Reversed), Reversed, out bool? _)
32+
|| parameters.TryGetUpdatedValue(nameof(Horizontal), Horizontal, out HorizontalAlignment? _)
33+
|| parameters.TryGetUpdatedValue(nameof(Vertical), Vertical, out VerticalDistribution? _)
34+
|| parameters.TryGetUpdatedValue(nameof(Variant), Variant, out PhazorStackVariant? _);
35+
36+
return ValueTask.FromResult(new PhazorSetParametersResult(StyleChanged: false, ClassChanged: classChanged));
37+
}
38+
2739
protected override void ConfigureClasses(CssClassBuilder builder)
2840
{
2941
builder
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace Phazor.Components.Models;
2+
3+
public readonly record struct PhazorSetParametersResult(bool StyleChanged, bool ClassChanged);
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace Phazor.Components.Tools;
4+
5+
/// <summary>
6+
/// <see cref="WeakReference"/> wrapper that allows to retain value based on it's usage
7+
/// </summary>
8+
/// <typeparam name="T">
9+
/// Type of the value
10+
/// </typeparam>
11+
internal class AdaptiveWeakReference<T>
12+
where T : class
13+
{
14+
private const ushort DefaultWeaknessBreakpoint = 15;
15+
16+
private readonly Func<T> _valueFactory;
17+
18+
/// <summary>
19+
/// <see cref="_weaknessFactor"/> less than <see cref="_weaknessBreakpoint"/>: reference must be weak
20+
/// <see cref="_weaknessFactor"/> greater or equal to <see cref="_weaknessBreakpoint"/>: reference must be strong
21+
/// </summary>
22+
private readonly ushort _weaknessBreakpoint;
23+
24+
/// <summary>
25+
/// Container for weak reference. If value is initialized, always contains reference to it. When strong
26+
/// reference is also initialized, weak reference will retrain its value because GC would not collect target
27+
/// object.
28+
/// </summary>
29+
private readonly WeakReference<T?> _reference;
30+
31+
/// <summary>
32+
/// Strong reference to value. Initialized when <see cref="_weaknessFactor"/> greater or equal
33+
/// to <see cref="_weaknessBreakpoint"/>
34+
/// </summary>
35+
private T? _value;
36+
37+
private ushort _weaknessFactor;
38+
private ushort _strengthenStreak;
39+
private ushort _weakenStreak;
40+
41+
public AdaptiveWeakReference(Func<T> valueFactory, ushort weaknessBreakpoint = DefaultWeaknessBreakpoint)
42+
{
43+
_valueFactory = valueFactory;
44+
_weaknessBreakpoint = weaknessBreakpoint;
45+
46+
_value = null;
47+
_reference = new WeakReference<T?>(null);
48+
}
49+
50+
/// <summary>
51+
/// Gets or sets wrapped value. When getting an uninitialized value, it will be created with provided value
52+
/// factory. When value is initialized in getter or set via setter it will either be weak or strong reference
53+
/// based on current <see cref="_weaknessFactor"/>.
54+
/// </summary>
55+
public T Value
56+
{
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
get => GetValue();
59+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
60+
set => SetValue(value);
61+
}
62+
63+
/// <summary>
64+
/// Checks whether value is initialized either with weak or strong reference.
65+
/// </summary>
66+
public bool HasValue
67+
{
68+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
69+
get => _value is not null || _reference.TryGetTarget(out _);
70+
}
71+
72+
/// <summary>
73+
/// Removes both weak and strong reference
74+
/// </summary>
75+
public void Clear()
76+
{
77+
_value = null;
78+
_reference.SetTarget(null);
79+
}
80+
81+
/// <summary>
82+
/// Increases the <see cref="_weaknessFactor"/>, changing weak to strong reference if required
83+
/// </summary>
84+
public void Strengthen()
85+
{
86+
if (_weaknessFactor is ushort.MaxValue)
87+
return;
88+
89+
if (_strengthenStreak is not ushort.MaxValue)
90+
_strengthenStreak++;
91+
92+
_weakenStreak = 0;
93+
94+
_weaknessFactor = ushort.MaxValue - _weaknessFactor < _strengthenStreak
95+
? ushort.MaxValue
96+
: (ushort)(_weaknessFactor + _strengthenStreak);
97+
98+
if (_weaknessFactor >= _weaknessBreakpoint)
99+
ConvertToStrongReference();
100+
}
101+
102+
/// <summary>
103+
/// Decreases the <see cref="_weaknessFactor"/>, changing strong to weak reference if required
104+
/// </summary>
105+
public void Weaken()
106+
{
107+
if (_weaknessFactor is 0)
108+
return;
109+
110+
if (_weakenStreak is not ushort.MaxValue)
111+
_weakenStreak++;
112+
113+
_strengthenStreak = 0;
114+
115+
_weaknessFactor = _weaknessFactor < _weakenStreak
116+
? ushort.MinValue
117+
: (ushort)(_weaknessFactor - _weakenStreak);
118+
119+
if (_weaknessFactor < _weaknessBreakpoint)
120+
ConvertToWeakReference();
121+
}
122+
123+
/// <summary>
124+
/// If current value is initialized – returns it, otherwise – creates and sets the value, and then returns it
125+
/// </summary>
126+
private T GetValue()
127+
{
128+
if (_value is not null)
129+
return _value;
130+
131+
if (_reference.TryGetTarget(out T? value))
132+
return value;
133+
134+
value = _valueFactory.Invoke();
135+
SetValue(value);
136+
137+
return value;
138+
}
139+
140+
private void SetValue(T value)
141+
{
142+
_value = _weaknessFactor < _weaknessBreakpoint ? null : value;
143+
_reference.SetTarget(value);
144+
}
145+
146+
private void ConvertToWeakReference()
147+
{
148+
_reference.SetTarget(_value);
149+
_value = null;
150+
}
151+
152+
private void ConvertToStrongReference()
153+
{
154+
_reference.TryGetTarget(out _value);
155+
}
156+
}

0 commit comments

Comments
 (0)