com.wallstop-studios.unity-helpers 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md +16 -16
- package/Docs/INDEX.md +1 -0
- package/Docs/RANDOM_PERFORMANCE.md +15 -15
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/{package-lock.json.meta → Docs/REFLECTION_PERFORMANCE.md.meta} +1 -1
- package/Docs/RELATIONAL_COMPONENTS.md +6 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Editor/Sprites/AnimationCopier.cs +1 -1
- package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
- package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
- package/Editor/Sprites/TextureResizerWizard.cs +4 -3
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
- package/README.md +8 -3
- package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
- package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
- package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
- package/Runtime/Core/CodeGen.meta +8 -0
- package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
- package/Runtime/Core/Helper/Helpers.cs +8 -0
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
- package/Runtime/Core/Helper/Objects.cs +1 -1
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
- package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/FlurryBurstRandom.cs +0 -6
- package/Runtime/Tags/AttributeMetadataCache.cs +4 -6
- package/Runtime/Tags/CosmeticEffectData.cs +1 -1
- package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
- package/Tests/Editor/Helper/HelpersTests.cs +2 -2
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
- package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
- package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
- package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +1 -1
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
- package/package.json +1 -1
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
|
@@ -14,6 +14,8 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
14
14
|
public sealed class RelationalComponentAssigner : IRelationalComponentAssigner
|
|
15
15
|
{
|
|
16
16
|
private readonly AttributeMetadataCache _metadataCache;
|
|
17
|
+
private readonly Dictionary<Type, bool> _hasAssignmentsCache;
|
|
18
|
+
private readonly object _cacheLock = new();
|
|
17
19
|
|
|
18
20
|
/// <summary>
|
|
19
21
|
/// Creates a new assigner using the active <see cref="AttributeMetadataCache.Instance"/>.
|
|
@@ -27,6 +29,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
27
29
|
public RelationalComponentAssigner(AttributeMetadataCache metadataCache)
|
|
28
30
|
{
|
|
29
31
|
_metadataCache = metadataCache;
|
|
32
|
+
_hasAssignmentsCache = new Dictionary<Type, bool>();
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
/// <inheritdoc />
|
|
@@ -37,11 +40,20 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
37
40
|
return false;
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
lock (_cacheLock)
|
|
44
|
+
{
|
|
45
|
+
if (_hasAssignmentsCache.TryGetValue(componentType, out bool cachedResult))
|
|
46
|
+
{
|
|
47
|
+
return cachedResult;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
AttributeMetadataCache cache = _metadataCache ?? AttributeMetadataCache.Instance;
|
|
41
52
|
if (cache == null)
|
|
42
53
|
{
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
bool reflectionResult = HasRelationalAttributesViaReflection(componentType);
|
|
55
|
+
StoreCacheResult(componentType, reflectionResult);
|
|
56
|
+
return reflectionResult;
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
Type current = componentType;
|
|
@@ -55,13 +67,24 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
55
67
|
&& fields.Length > 0
|
|
56
68
|
)
|
|
57
69
|
{
|
|
70
|
+
StoreCacheResult(componentType, true);
|
|
58
71
|
return true;
|
|
59
72
|
}
|
|
60
73
|
current = current.BaseType;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
// Fallback: inspect fields via reflection to detect relational attributes
|
|
64
|
-
|
|
77
|
+
bool result = HasRelationalAttributesViaReflection(componentType);
|
|
78
|
+
StoreCacheResult(componentType, result);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private void StoreCacheResult(Type componentType, bool result)
|
|
83
|
+
{
|
|
84
|
+
lock (_cacheLock)
|
|
85
|
+
{
|
|
86
|
+
_hasAssignmentsCache[componentType] = result;
|
|
87
|
+
}
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
private static bool HasRelationalAttributesViaReflection(Type componentType)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
2
2
|
{
|
|
3
|
+
using System;
|
|
3
4
|
using UnityEngine;
|
|
4
5
|
|
|
5
6
|
/// <summary>
|
|
@@ -53,9 +54,24 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
53
54
|
/// </example>
|
|
54
55
|
public static void AssignRelationalComponents(this Component component)
|
|
55
56
|
{
|
|
56
|
-
component
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
if (component == null)
|
|
58
|
+
{
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Type componentType = component.GetType();
|
|
63
|
+
ParentComponentExtensions.AssignParentComponents(
|
|
64
|
+
component,
|
|
65
|
+
ParentComponentExtensions.GetOrCreateFields(componentType)
|
|
66
|
+
);
|
|
67
|
+
SiblingComponentExtensions.AssignSiblingComponents(
|
|
68
|
+
component,
|
|
69
|
+
SiblingComponentExtensions.GetOrCreateFields(componentType)
|
|
70
|
+
);
|
|
71
|
+
ChildComponentExtensions.AssignChildComponents(
|
|
72
|
+
component,
|
|
73
|
+
ChildComponentExtensions.GetOrCreateFields(componentType)
|
|
74
|
+
);
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
}
|
|
@@ -3,6 +3,9 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
3
3
|
using System;
|
|
4
4
|
using System.Collections;
|
|
5
5
|
using System.Collections.Generic;
|
|
6
|
+
using System.Linq;
|
|
7
|
+
using System.Linq.Expressions;
|
|
8
|
+
using System.Reflection;
|
|
6
9
|
using UnityEngine;
|
|
7
10
|
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
8
11
|
using WallstopStudios.UnityHelpers.Utils;
|
|
@@ -86,11 +89,22 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
86
89
|
/// </example>
|
|
87
90
|
public static void AssignSiblingComponents(this Component component)
|
|
88
91
|
{
|
|
89
|
-
Type componentType = component.GetType();
|
|
90
92
|
FieldMetadata<SiblingComponentAttribute>[] fields = FieldsByType.GetOrAdd(
|
|
91
|
-
|
|
93
|
+
component.GetType(),
|
|
92
94
|
type => GetFieldMetadata<SiblingComponentAttribute>(type)
|
|
93
95
|
);
|
|
96
|
+
AssignSiblingComponents(component, fields);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
internal static void AssignSiblingComponents(
|
|
100
|
+
Component component,
|
|
101
|
+
FieldMetadata<SiblingComponentAttribute>[] fields
|
|
102
|
+
)
|
|
103
|
+
{
|
|
104
|
+
if (component == null || fields == null || fields.Length == 0)
|
|
105
|
+
{
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
94
108
|
|
|
95
109
|
foreach (FieldMetadata<SiblingComponentAttribute> metadata in fields)
|
|
96
110
|
{
|
|
@@ -107,103 +121,142 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
107
121
|
}
|
|
108
122
|
else
|
|
109
123
|
{
|
|
110
|
-
FilterParameters filters =
|
|
111
|
-
|
|
124
|
+
FilterParameters filters = metadata.Filters;
|
|
125
|
+
if (
|
|
126
|
+
!metadata.isInterface
|
|
127
|
+
&& !filters.RequiresPostProcessing
|
|
128
|
+
&& metadata.attribute.MaxCount <= 0
|
|
129
|
+
)
|
|
130
|
+
{
|
|
131
|
+
foundSibling = TryAssignSiblingCollectionFast(component, metadata);
|
|
132
|
+
}
|
|
133
|
+
else
|
|
112
134
|
{
|
|
113
|
-
|
|
135
|
+
switch (metadata.kind)
|
|
114
136
|
{
|
|
115
|
-
|
|
116
|
-
Buffers<Component>.List.Get(out List<Component> components);
|
|
117
|
-
GetComponentsOfType(
|
|
118
|
-
component,
|
|
119
|
-
metadata.elementType,
|
|
120
|
-
metadata.isInterface,
|
|
121
|
-
metadata.attribute.AllowInterfaces,
|
|
122
|
-
components
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
int filteredCount = FilterComponentsInPlace(
|
|
126
|
-
components,
|
|
127
|
-
filters,
|
|
128
|
-
metadata.attribute,
|
|
129
|
-
metadata.elementType,
|
|
130
|
-
metadata.isInterface
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
Array correctTypedArray = metadata.arrayCreator(filteredCount);
|
|
134
|
-
for (int i = 0; i < filteredCount; ++i)
|
|
137
|
+
case FieldKind.Array:
|
|
135
138
|
{
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
using PooledResource<List<Component>> componentBuffer =
|
|
140
|
+
Buffers<Component>.List.Get(out List<Component> components);
|
|
141
|
+
GetComponentsOfType(
|
|
142
|
+
component,
|
|
143
|
+
metadata.elementType,
|
|
144
|
+
metadata.isInterface,
|
|
145
|
+
metadata.attribute.AllowInterfaces,
|
|
146
|
+
components
|
|
147
|
+
);
|
|
138
148
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
metadata.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
IList instance = metadata.listCreator(filteredCount);
|
|
164
|
-
for (int i = 0; i < filteredCount; ++i)
|
|
149
|
+
int filteredCount =
|
|
150
|
+
!filters.RequiresPostProcessing
|
|
151
|
+
&& metadata.attribute.MaxCount <= 0
|
|
152
|
+
? components.Count
|
|
153
|
+
: FilterComponentsInPlace(
|
|
154
|
+
components,
|
|
155
|
+
filters,
|
|
156
|
+
metadata.attribute,
|
|
157
|
+
metadata.elementType,
|
|
158
|
+
metadata.isInterface
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
Array correctTypedArray = metadata.arrayCreator(filteredCount);
|
|
162
|
+
for (int i = 0; i < filteredCount; ++i)
|
|
163
|
+
{
|
|
164
|
+
correctTypedArray.SetValue(components[i], i);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
metadata.SetValue(component, correctTypedArray);
|
|
168
|
+
foundSibling = filteredCount > 0;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case FieldKind.List:
|
|
165
172
|
{
|
|
166
|
-
|
|
173
|
+
using PooledResource<List<Component>> componentBuffer =
|
|
174
|
+
Buffers<Component>.List.Get(out List<Component> components);
|
|
175
|
+
GetComponentsOfType(
|
|
176
|
+
component,
|
|
177
|
+
metadata.elementType,
|
|
178
|
+
metadata.isInterface,
|
|
179
|
+
metadata.attribute.AllowInterfaces,
|
|
180
|
+
components
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
int filteredCount =
|
|
184
|
+
!filters.RequiresPostProcessing
|
|
185
|
+
&& metadata.attribute.MaxCount <= 0
|
|
186
|
+
? components.Count
|
|
187
|
+
: FilterComponentsInPlace(
|
|
188
|
+
components,
|
|
189
|
+
filters,
|
|
190
|
+
metadata.attribute,
|
|
191
|
+
metadata.elementType,
|
|
192
|
+
metadata.isInterface
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
object existing = metadata.GetValue(component);
|
|
196
|
+
if (existing is IList instance)
|
|
197
|
+
{
|
|
198
|
+
instance.Clear();
|
|
199
|
+
}
|
|
200
|
+
else
|
|
201
|
+
{
|
|
202
|
+
instance = metadata.listCreator(filteredCount);
|
|
203
|
+
metadata.SetValue(component, instance);
|
|
204
|
+
}
|
|
205
|
+
for (int i = 0; i < filteredCount; ++i)
|
|
206
|
+
{
|
|
207
|
+
instance.Add(components[i]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
foundSibling = filteredCount > 0;
|
|
211
|
+
break;
|
|
167
212
|
}
|
|
213
|
+
case FieldKind.HashSet:
|
|
214
|
+
{
|
|
215
|
+
using PooledResource<List<Component>> componentBuffer =
|
|
216
|
+
Buffers<Component>.List.Get(out List<Component> components);
|
|
217
|
+
GetComponentsOfType(
|
|
218
|
+
component,
|
|
219
|
+
metadata.elementType,
|
|
220
|
+
metadata.isInterface,
|
|
221
|
+
metadata.attribute.AllowInterfaces,
|
|
222
|
+
components
|
|
223
|
+
);
|
|
168
224
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
metadata.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
int filteredCount =
|
|
226
|
+
!filters.RequiresPostProcessing
|
|
227
|
+
&& metadata.attribute.MaxCount <= 0
|
|
228
|
+
? components.Count
|
|
229
|
+
: FilterComponentsInPlace(
|
|
230
|
+
components,
|
|
231
|
+
filters,
|
|
232
|
+
metadata.attribute,
|
|
233
|
+
metadata.elementType,
|
|
234
|
+
metadata.isInterface
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
object instance = metadata.GetValue(component);
|
|
238
|
+
if (instance != null && metadata.hashSetClearer != null)
|
|
239
|
+
{
|
|
240
|
+
metadata.hashSetClearer(instance);
|
|
241
|
+
}
|
|
242
|
+
else
|
|
243
|
+
{
|
|
244
|
+
instance = metadata.hashSetCreator(filteredCount);
|
|
245
|
+
metadata.SetValue(component, instance);
|
|
246
|
+
}
|
|
247
|
+
for (int i = 0; i < filteredCount; ++i)
|
|
248
|
+
{
|
|
249
|
+
metadata.hashSetAdder(instance, components[i]);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
foundSibling = filteredCount > 0;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
default:
|
|
195
256
|
{
|
|
196
|
-
|
|
257
|
+
foundSibling = TryAssignSingleSibling(component, metadata);
|
|
258
|
+
break;
|
|
197
259
|
}
|
|
198
|
-
|
|
199
|
-
metadata.setter(component, instance);
|
|
200
|
-
foundSibling = filteredCount > 0;
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
default:
|
|
204
|
-
{
|
|
205
|
-
foundSibling = TryAssignSingleSibling(component, metadata);
|
|
206
|
-
break;
|
|
207
260
|
}
|
|
208
261
|
}
|
|
209
262
|
}
|
|
@@ -215,6 +268,11 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
215
268
|
}
|
|
216
269
|
}
|
|
217
270
|
|
|
271
|
+
internal static FieldMetadata<SiblingComponentAttribute>[] GetOrCreateFields(Type type)
|
|
272
|
+
{
|
|
273
|
+
return FieldsByType.GetOrAdd(type, t => GetFieldMetadata<SiblingComponentAttribute>(t));
|
|
274
|
+
}
|
|
275
|
+
|
|
218
276
|
private static bool TryAssignSingleSibling(
|
|
219
277
|
Component component,
|
|
220
278
|
FieldMetadata<SiblingComponentAttribute> metadata
|
|
@@ -236,7 +294,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
236
294
|
{
|
|
237
295
|
if (component.TryGetComponent(metadata.elementType, out Component sibling))
|
|
238
296
|
{
|
|
239
|
-
metadata.
|
|
297
|
+
metadata.SetValue(component, sibling);
|
|
240
298
|
return true;
|
|
241
299
|
}
|
|
242
300
|
return false;
|
|
@@ -255,10 +313,125 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
255
313
|
)
|
|
256
314
|
)
|
|
257
315
|
{
|
|
258
|
-
metadata.
|
|
316
|
+
metadata.SetValue(component, resolved);
|
|
259
317
|
return true;
|
|
260
318
|
}
|
|
261
319
|
return false;
|
|
262
320
|
}
|
|
321
|
+
|
|
322
|
+
private static bool TryAssignSiblingCollectionFast(
|
|
323
|
+
Component component,
|
|
324
|
+
FieldMetadata<SiblingComponentAttribute> metadata
|
|
325
|
+
)
|
|
326
|
+
{
|
|
327
|
+
Array componentsArray = SiblingComponentFastInvoker.GetArray(
|
|
328
|
+
component,
|
|
329
|
+
metadata.elementType
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
return AssignComponentsFromArray(component, metadata, componentsArray);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private static bool AssignComponentsFromArray(
|
|
336
|
+
Component component,
|
|
337
|
+
FieldMetadata<SiblingComponentAttribute> metadata,
|
|
338
|
+
Array componentsArray
|
|
339
|
+
)
|
|
340
|
+
{
|
|
341
|
+
if (componentsArray == null)
|
|
342
|
+
{
|
|
343
|
+
componentsArray = Array.CreateInstance(metadata.elementType, 0);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
int count = componentsArray.Length;
|
|
347
|
+
|
|
348
|
+
switch (metadata.kind)
|
|
349
|
+
{
|
|
350
|
+
case FieldKind.Array:
|
|
351
|
+
{
|
|
352
|
+
metadata.SetValue(component, componentsArray);
|
|
353
|
+
return count > 0;
|
|
354
|
+
}
|
|
355
|
+
case FieldKind.List:
|
|
356
|
+
{
|
|
357
|
+
if (metadata.GetValue(component) is IList instance)
|
|
358
|
+
{
|
|
359
|
+
instance.Clear();
|
|
360
|
+
}
|
|
361
|
+
else
|
|
362
|
+
{
|
|
363
|
+
instance = metadata.listCreator(count);
|
|
364
|
+
metadata.SetValue(component, instance);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (int i = 0; i < count; ++i)
|
|
368
|
+
{
|
|
369
|
+
instance.Add(componentsArray.GetValue(i));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return count > 0;
|
|
373
|
+
}
|
|
374
|
+
case FieldKind.HashSet:
|
|
375
|
+
{
|
|
376
|
+
object hashSet = metadata.GetValue(component);
|
|
377
|
+
if (hashSet != null && metadata.hashSetClearer != null)
|
|
378
|
+
{
|
|
379
|
+
metadata.hashSetClearer(hashSet);
|
|
380
|
+
}
|
|
381
|
+
else
|
|
382
|
+
{
|
|
383
|
+
hashSet = metadata.hashSetCreator(count);
|
|
384
|
+
metadata.SetValue(component, hashSet);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
for (int i = 0; i < count; ++i)
|
|
388
|
+
{
|
|
389
|
+
metadata.hashSetAdder(hashSet, componentsArray.GetValue(i));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return count > 0;
|
|
393
|
+
}
|
|
394
|
+
default:
|
|
395
|
+
{
|
|
396
|
+
return TryAssignSingleSibling(component, metadata);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
internal static class SiblingComponentFastInvoker
|
|
403
|
+
{
|
|
404
|
+
private static readonly Dictionary<Type, Func<Component, Array>> ArrayGetters = new();
|
|
405
|
+
|
|
406
|
+
private static readonly MethodInfo GetComponentsGenericDefinition = typeof(Component)
|
|
407
|
+
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
|
408
|
+
.First(method =>
|
|
409
|
+
method.Name == nameof(Component.GetComponents)
|
|
410
|
+
&& method.IsGenericMethodDefinition
|
|
411
|
+
&& method.GetParameters().Length == 0
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
internal static Array GetArray(Component component, Type elementType)
|
|
415
|
+
{
|
|
416
|
+
if (!ArrayGetters.TryGetValue(elementType, out Func<Component, Array> getter))
|
|
417
|
+
{
|
|
418
|
+
getter = CreateArrayGetter(elementType);
|
|
419
|
+
ArrayGetters[elementType] = getter;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return getter(component);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private static Func<Component, Array> CreateArrayGetter(Type elementType)
|
|
426
|
+
{
|
|
427
|
+
MethodInfo closedMethod = GetComponentsGenericDefinition.MakeGenericMethod(elementType);
|
|
428
|
+
ParameterExpression componentParameter = Expression.Parameter(
|
|
429
|
+
typeof(Component),
|
|
430
|
+
"component"
|
|
431
|
+
);
|
|
432
|
+
MethodCallExpression invoke = Expression.Call(componentParameter, closedMethod);
|
|
433
|
+
UnaryExpression convert = Expression.Convert(invoke, typeof(Array));
|
|
434
|
+
return Expression.Lambda<Func<Component, Array>>(convert, componentParameter).Compile();
|
|
435
|
+
}
|
|
263
436
|
}
|
|
264
437
|
}
|
|
@@ -5,6 +5,7 @@ namespace WallstopStudios.UnityHelpers.Core.DataStructure
|
|
|
5
5
|
using System.Collections.Generic;
|
|
6
6
|
using System.Runtime.CompilerServices;
|
|
7
7
|
using ProtoBuf;
|
|
8
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
8
9
|
|
|
9
10
|
/// <summary>
|
|
10
11
|
/// An immutable value-type variant of BitSet that provides read-only access to bit data.
|
|
@@ -318,14 +319,8 @@ namespace WallstopStudios.UnityHelpers.Core.DataStructure
|
|
|
318
319
|
{
|
|
319
320
|
return false;
|
|
320
321
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (_bits[i] != other._bits[i])
|
|
324
|
-
{
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return true;
|
|
322
|
+
|
|
323
|
+
return _bits.AsSpan().SequenceEqual(other._bits);
|
|
329
324
|
}
|
|
330
325
|
|
|
331
326
|
public override bool Equals(object obj)
|
|
@@ -335,18 +330,8 @@ namespace WallstopStudios.UnityHelpers.Core.DataStructure
|
|
|
335
330
|
|
|
336
331
|
public override int GetHashCode()
|
|
337
332
|
{
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
int hash = _capacity.GetHashCode();
|
|
341
|
-
if (_bits != null)
|
|
342
|
-
{
|
|
343
|
-
foreach (ulong bit in _bits)
|
|
344
|
-
{
|
|
345
|
-
hash = (hash * 397) ^ bit.GetHashCode();
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
return hash;
|
|
349
|
-
}
|
|
333
|
+
int hash = Objects.SpanHashCode<ulong>(_bits.AsSpan());
|
|
334
|
+
return Objects.HashCode(_capacity, hash);
|
|
350
335
|
}
|
|
351
336
|
|
|
352
337
|
public static bool operator ==(ImmutableBitSet left, ImmutableBitSet right)
|
|
@@ -118,7 +118,9 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
internal static string[] AllSpriteLabels { get; private set; } = Array.Empty<string>();
|
|
121
|
+
#if UNITY_EDITOR
|
|
121
122
|
private static bool SpriteLabelCacheInitialized;
|
|
123
|
+
#endif
|
|
122
124
|
|
|
123
125
|
/// <summary>
|
|
124
126
|
/// Gets all unique sprite labels in the project (Editor only).
|
|
@@ -321,7 +323,9 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
|
|
|
321
323
|
if (labels == null || labels.Count == 0)
|
|
322
324
|
{
|
|
323
325
|
AllSpriteLabels = Array.Empty<string>();
|
|
326
|
+
#if UNITY_EDITOR
|
|
324
327
|
SpriteLabelCacheInitialized = true;
|
|
328
|
+
#endif
|
|
325
329
|
return;
|
|
326
330
|
}
|
|
327
331
|
|
|
@@ -350,12 +354,16 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
|
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
AllSpriteLabels = cache;
|
|
357
|
+
#if UNITY_EDITOR
|
|
353
358
|
SpriteLabelCacheInitialized = true;
|
|
359
|
+
#endif
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
internal static void ResetSpriteLabelCache()
|
|
357
363
|
{
|
|
364
|
+
#if UNITY_EDITOR
|
|
358
365
|
SpriteLabelCacheInitialized = false;
|
|
366
|
+
#endif
|
|
359
367
|
AllSpriteLabels = Array.Empty<string>();
|
|
360
368
|
}
|
|
361
369
|
|
|
@@ -406,13 +406,17 @@ namespace WallstopStudios.UnityHelpers.Core.Helper.Logging
|
|
|
406
406
|
RemoveDecorationInternal(existing.priority, existing.index);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
409
|
+
if (
|
|
410
|
+
!_matchingDecorations.TryGetValue(
|
|
411
|
+
priority,
|
|
412
|
+
out List<(
|
|
413
|
+
string tag,
|
|
414
|
+
bool editorOnly,
|
|
415
|
+
Func<string, bool> predicate,
|
|
416
|
+
Func<string, object, string> formatter
|
|
417
|
+
)> matchingDecorations
|
|
418
|
+
)
|
|
419
|
+
)
|
|
416
420
|
{
|
|
417
421
|
matchingDecorations = new List<(
|
|
418
422
|
string tag,
|
|
@@ -58,7 +58,7 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
|
|
|
58
58
|
/// <summary>
|
|
59
59
|
/// Combines hash codes for a span of values into a deterministic composite hash.
|
|
60
60
|
/// </summary>
|
|
61
|
-
public static int
|
|
61
|
+
public static int SpanHashCode<T>(ReadOnlySpan<T> values)
|
|
62
62
|
{
|
|
63
63
|
if (values.IsEmpty)
|
|
64
64
|
{
|